diff --git a/nuxt.config.js b/nuxt.config.js
index d4ddde2..7043019 100644
--- a/nuxt.config.js
+++ b/nuxt.config.js
@@ -57,7 +57,8 @@ export default {
{src: '~/plugins/extend'},
{src: '~/plugins/axios'},
{src: '~/plugins/element-ui'},
- {src: '~/plugins/vditor', ssr: false}
+ {src: '~/plugins/vditor', ssr: false},
+ {src: '~/plugins/vue-sse'}
// {src: '~/plugins/vue-cropper', ssr: false}
],
/*
@@ -99,14 +100,15 @@ export default {
autoFetch: false
},
endpoints: {
- login: { url: '/api/auth/login', method: 'post' },
- logout: { url: '/api/auth/logout', method: 'post' },
- refresh: { url: '/api/auth/refresh-token', method: 'post' },
- user: { url: '/api/auth/user', method: 'get' }
+ login: {url: '/api/auth/login', method: 'post'},
+ logout: {url: '/api/auth/logout', method: 'post'},
+ refresh: {url: '/api/auth/refresh-token', method: 'post'},
+ user: {url: '/api/auth/user', method: 'get'}
},
autoLogout: false
}
- }
+ },
+ plugins: [{src: '~/plugins/axios', ssr: true}]
},
axios: {
proxy: true // 开启proxy
@@ -139,7 +141,7 @@ export default {
config.module.rules[2].use[0].options.plugins = ['lodash']
},
babel: {
- presets({ envName }) {
+ presets({envName}) {
return [
[
'@nuxt/babel-preset-app',
diff --git a/package.json b/package.json
index aaae1ff..160353b 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,8 @@
"@nuxtjs/auth-next": "^5.0.0-1667386184.dfbbb54",
"@nuxtjs/axios": "^5.13.1",
"babel-plugin-lodash": "^3.3.4",
+ "core-js": "^2.6.12",
+ "defu": "^5.0.1",
"echarts": "^4.9.0",
"element-ui": "^2.15.12",
"lodash": "^4.17.21",
@@ -26,9 +28,8 @@
"simple-icons": "^6.23.0",
"vditor": "^3.8.18",
"vue-cropperjs": "^4.2.0",
- "vuejs-avataaars": "^4.0.1",
- "core-js": "^2.6.12",
- "defu": "^5.0.1"
+ "vue-sse": "^2.5.2",
+ "vuejs-avataaars": "^4.0.1"
},
"devDependencies": {
"@commitlint/cli": "^17.4.0",
diff --git a/pages/chats/_account.vue b/pages/chats/_account.vue
index 0798f47..8f1b41f 100644
--- a/pages/chats/_account.vue
+++ b/pages/chats/_account.vue
@@ -8,6 +8,16 @@
发送
+
+
+
+
+
+
+
+
@@ -39,6 +49,7 @@
import Vue from 'vue';
import {mapState} from 'vuex';
import apiConfig from '~/config/api.config';
+import 'vditor/dist/css/content-theme/light.css';
export default {
name: "Chat",
@@ -61,7 +72,10 @@ export default {
isShow: true,
loading: false,
to: {},
- messages: []
+ messages: [],
+ vueSse: null,
+ customEvents: null,
+ message: ''
}
},
watch: {
@@ -159,6 +173,7 @@ export default {
},
async send() {
let _ts = this;
+ _ts.message = '';
const message = {
to: _ts.to.account,
from: _ts.user.account,
@@ -173,18 +188,59 @@ export default {
_ts.contentEditor.setValue('')
_ts.$axios.$post('/api/openai/chat', {
message: message.content
- }).then(res => {
+ }).then(async res => {
+ const html = await Vue.Vditor.md2html(_ts.message);
_ts.messages.push({
to: _ts.user.account,
from: _ts.to.account,
dataType: 1,
dataId: new Date().getTime(),
- content: res[0].message.content
+ content: html
});
_ts.messages.sort((a, b) => {
return b.dataId - a.dataId;
});
+ _ts.message = '';
});
+ },
+ close() {
+ let _ts = this;
+ //浏览器关闭SSE连接
+ _ts.vueSse.disconnect();
+ _ts.$axios.$get(`/api/sse/close/${_ts.user.idUser}`);
+ },
+ init() {
+ //初始化 vue-sse
+ let _ts = this;
+ let vueSse = _ts.vueSse;
+ //监听 message
+ vueSse.on('message', this.handleMessage);
+ //监听 customEvents
+ vueSse.once('customEvents', this.handleCustomEvents);
+
+ //里面的 on、once、off 是用了发布订阅模式,
+ //源码 once 方法这有点小问题,写文章时改了
+ //源码但还没提PR(主要是没提过,不会弄)
+ //执行 connect() 返回个Promise,
+ vueSse
+ .connect()
+ .then((sse) => {
+ console.log("We're connected!", sse);
+ })
+ .catch((err) => console.error('Failed make initial connection:', err));
+ },
+ //message回调
+ handleMessage(res) {
+ if (typeof res !== "undefined") {
+ this.message += res;
+ }
+ },
+ //handleCustomEvents回调
+ handleCustomEvents(res) {
+ console.log('customEvents22:', res)
+ let { data } = res;
+ console.info('customEvents:', data);
+ this.customEvents = data;
}
},
async mounted() {
@@ -215,6 +271,12 @@ export default {
content: '伟大的"坦格利安家族的风暴降生丹妮莉丝 · 铁王座的合法继承人 · 安达尔人和先民的合法女王 · 七国的守护者 · 草海上的卡丽熙 · 不焚者 · 解放者 · 傲之追猎者 · 悠米"为你服务'
}
_ts.messages.push(message);
+ _ts.vueSse = _ts.$sse.create({
+ url: `/api/sse/subscribe/${_ts.user.idUser}`,
+ format: 'json',
+ withCredentials: true
+ });
+ this.init();
}
if (!_ts.initEditor) {
@@ -230,6 +292,14 @@ export default {
});
}, 500);
}
+ },
+ sse: {
+ //配置后自动添加断开连接事件,源码里面是做了判断,
+ //然后加在组件 beforeDestroy 生命周期里
+ cleanup: true,
+ },
+ beforeDestroy() {
+ this.close();
}
}
diff --git a/plugins/vue-sse.js b/plugins/vue-sse.js
new file mode 100644
index 0000000..46f3d83
--- /dev/null
+++ b/plugins/vue-sse.js
@@ -0,0 +1,15 @@
+import Vue from 'vue'
+import VueSSE from 'vue-sse';
+
+// using defaults
+Vue.use(VueSSE);
+
+// OR specify custom defaults (described below)
+Vue.use(VueSSE, {
+ format: 'json', //数据格式
+ url: '/', //路径
+ withCredentials: true, //标识为open? // withCredentials should be set after "open" for Safari and Chrome (< 19 ?)
+ forcePolyfill:false, //强制使用原生SSE,使用另一个库event-source-polyfill
+ polyfill: true, //支持旧版浏览器
+ polyfillOptions:null, //配置参数
+});