首次提交

This commit is contained in:
TOP糯米 2023-03-27 20:28:51 +08:00
commit 99c8e04bb4
15 changed files with 14501 additions and 0 deletions

5
.editorconfig Normal file
View File

@ -0,0 +1,5 @@
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

24
README.md Normal file
View File

@ -0,0 +1,24 @@
# chat
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

5
babel.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

14015
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

58
package.json Normal file
View File

@ -0,0 +1,58 @@
{
"name": "chat",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@kangc/v-md-editor": "^2.3.15",
"axios": "^1.3.4",
"core-js": "^3.6.5",
"js-base64": "^3.7.5",
"js-md5": "^0.7.3",
"vue": "^3.0.0",
"vue-axios": "^3.5.2",
"vue-router": "^4.0.0-0",
"vuex": "^4.0.0-0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.1",
"@vue/cli-plugin-eslint": "~4.5.1",
"@vue/cli-plugin-router": "~4.5.1",
"@vue/cli-plugin-vuex": "~4.5.1",
"@vue/cli-service": "~4.5.1",
"@vue/compiler-sfc": "^3.0.0",
"@vue/eslint-config-standard": "^5.1.2",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^7.0.0",
"less": "^3.0.4",
"less-loader": "^5.0.0"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"@vue/standard"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

19
public/index.html Normal file
View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="viewport"
content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

18
src/App.vue Normal file
View File

@ -0,0 +1,18 @@
<template>
<router-view />
</template>
<style lang="less">
* {
padding: 0;
margin: 0;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
::-webkit-scrollbar {
width: 4px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #b9b9b9;
}
</style>

25
src/main.js Normal file
View File

@ -0,0 +1,25 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import axios from 'axios'
import VueAxios from 'vue-axios'
import VMdPreview from '@kangc/v-md-editor/lib/preview';
import '@kangc/v-md-editor/lib/style/preview.css';
import githubTheme from '@kangc/v-md-editor/lib/theme/github.js';
import '@kangc/v-md-editor/lib/theme/style/github.css';
import hljs from 'highlight.js';
VMdPreview.use(githubTheme, {
Hljs: hljs,
});
const app = createApp(App)
// 全局配置
app.config.globalProperties.$config = {
api: "",
privateKey: "",
}
app.use(store).use(router).use(VueAxios, axios).use(VMdPreview).mount('#app')

16
src/router/index.js Normal file
View File

@ -0,0 +1,16 @@
import { createRouter, createWebHashHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: () => import(/* webpackChunkName: "about" */ '../views/Home.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router

12
src/store/index.js Normal file
View File

@ -0,0 +1,12 @@
import { createStore } from 'vuex'
export default createStore({
state: {
},
mutations: {
},
actions: {
},
modules: {
}
})

14
src/utils/sign.js Normal file
View File

@ -0,0 +1,14 @@
import md5 from 'js-md5'
export default {
makeSign(data, key) {
let keys = Object.keys(data).sort(), obj = {}, str = "";
for (let i = 0; i < keys.length; i++) {
if (keys[i].toLowerCase() == 'sign') {
continue
}
str += keys[i] + "=" + data[keys[i]] + '&'
}
return md5(str + 'key=' + key)
}
}

263
src/views/Home.vue Normal file
View File

@ -0,0 +1,263 @@
<template>
<div class="chat-window">
<div class="message-content">
<div class="message-group">
<div class="message-item" :class="[item.role]" v-for="(item, index) in lists" :key="index">
<span class="name">{{ item.role == "user" ? "你" : pageTitle }}</span>
<v-md-preview class="content" :text="item.content"></v-md-preview>
</div>
</div>
</div>
<div class="user-input" :class="{ disable: !canUse }">
<input
class="inp"
type="text"
v-model="words"
:disabled="!canUse"
@keyup.enter="send"
@input="scrollToBottom"
@focus="scrollToBottom"
placeholder="输入你的问题"
/>
<button class="btn" @click="send">{{ canUse ? "发送" : "正在回答" }}</button>
</div>
</div>
</template>
<script>
import { Base64 } from "js-base64";
import Sign from "../utils/sign";
export default {
name: "Home",
data() {
return {
pageTitle: "GPT-3.5语言模型",
canUse: true,
words: "",
lists: [
{
role: "system",
content: "请问有什么可以帮你的吗?",
},
],
};
},
components: {},
created() {
document.title = this.pageTitle;
},
mounted() {},
unmounted() {},
methods: {
/**
* 发送消息
*/
send() {
if (!this.canUse || this.words == "") return;
this.lists.push({
role: "user",
content: this.words,
});
let requestData = {
words: Base64.encode(this.words),
time: parseInt(Date.parse(new Date()) / 1000),
};
requestData.sign = Sign.makeSign(requestData, this.$config.privateKey);
this.lists.push({
role: "system",
content: "...",
});
let lastMessage = this.lists[this.lists.length - 1];
this.scrollToBottom();
this.canUse = false;
this.axios({
method: "POST",
url: this.$config.api,
data: JSON.stringify(requestData),
}).then((r) => {
this.canUse = true;
if (r.data.code == 1) {
this.words = "";
let answer = JSON.parse(Base64.decode(r.data.data));
lastMessage.content = answer.answer[0].message.content;
} else {
lastMessage.content = "[网络错误]";
}
this.scrollToBottom();
});
},
/**
* 控制滚动条
*/
scrollToBottom() {
setTimeout(() => {
var ele = document.getElementsByClassName("message-content")[0];
if (ele.scrollHeight > ele.clientHeight) {
ele.scrollTop = ele.scrollHeight;
}
}, 200);
},
},
};
</script>
<style lang="less" scoped>
.chat-window {
width: 1200px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px #eaeaf9;
background-color: #ededed;
.message-content {
z-index: 5;
width: 100%;
height: 700px;
padding: 20px;
box-sizing: border-box;
overflow-y: scroll;
}
.message-group {
display: flex;
flex-direction: column;
}
.message-item {
display: flex;
flex-direction: column;
width: 100%;
font-size: 14px;
margin-bottom: 10px;
.name {
color: #666666;
}
.content {
max-width: 650px;
padding: 10px 20px;
margin-top: 10px;
box-sizing: border-box;
}
}
.message-item.system {
align-items: flex-start;
.content {
color: #000000;
background-color: #ffffff;
border-radius: 0 10px 10px 10px;
}
}
.message-item.user {
align-items: flex-end;
.content {
color: #ffffff;
background-color: #484aa1;
border-radius: 10px 0 10px 10px;
}
}
.user-input {
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100px;
border-top: 2px solid #f5f5f5;
.inp,
.btn {
height: 50px;
border: 0;
outline: 0;
box-sizing: border-box;
font-size: 14px;
}
.inp {
width: 600px;
padding: 0 10px;
border-radius: 10px 0 0 10px;
background-color: #ffffff;
}
.btn {
width: 100px;
color: #ffffff;
background-color: #484aa1;
border-radius: 0 10px 10px 0;
}
}
.user-input.disable {
.btn {
cursor: not-allowed;
background-color: #b1b1b1;
}
}
}
.content:deep(.github-markdown-body) {
padding: 0;
font-size: unset;
blockquote,
details,
dl,
ol,
p,
pre,
table,
ul {
margin-bottom: 0;
}
}
@media screen and (max-width: 768px) {
.chat-window {
position: relative;
top: 0;
left: 0;
transform: translate(0, 0);
width: 100%;
height: 100vh;
border-radius: 0;
.message-content {
height: 100%;
padding: 10px;
}
.message-group {
padding-bottom: 65px;
}
.message-item {
.name {
display: none;
}
.content {
max-width: 100%;
padding: 10px;
}
}
.user-input {
position: absolute;
bottom: 20px;
left: 0;
right: 0;
height: auto;
border-top: 0;
.inp,
.btn {
height: 45px;
border: 2px solid #484aa1;
}
.inp {
width: 70%;
}
.btn {
width: 20%;
}
}
.user-input.disable {
.inp,
.btn {
border: 2px solid #b1b1b1;
}
}
}
}
</style>

4
vue.config.js Normal file
View File

@ -0,0 +1,4 @@
module.exports = {
publicPath: "/",
lintOnSave: false
}