diff --git a/im-ui/.gitignore b/im-ui/.gitignore
new file mode 100644
index 0000000..0b0371a
--- /dev/null
+++ b/im-ui/.gitignore
@@ -0,0 +1,26 @@
+.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?
+
+#
+package-lock.json
diff --git a/im-ui/README.md b/im-ui/README.md
new file mode 100644
index 0000000..b29f472
--- /dev/null
+++ b/im-ui/README.md
@@ -0,0 +1,24 @@
+# web
+
+## 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/).
diff --git a/im-ui/babel.config.js b/im-ui/babel.config.js
new file mode 100644
index 0000000..a673655
--- /dev/null
+++ b/im-ui/babel.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ presets: [
+ '@vue/cli-plugin-babel/preset'
+ ]
+}
diff --git a/im-ui/package.json b/im-ui/package.json
new file mode 100644
index 0000000..a4c049a
--- /dev/null
+++ b/im-ui/package.json
@@ -0,0 +1,60 @@
+{
+ "name": "web",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "serve": "vue-cli-service serve",
+ "build": "vue-cli-service build",
+ "lint": "vue-cli-service lint"
+ },
+ "dependencies": {
+ "axios": "^1.1.3",
+ "core-js": "^3.6.5",
+ "element-ui": "^2.15.10",
+ "sass": "^1.47.0",
+ "sass-loader": "^7.3.1",
+ "vue": "^2.6.11",
+ "vue-axios": "^3.5.0",
+ "vue-router": "^3.3.3",
+ "vuex": "^3.6.2",
+ "vuex-persist": "^3.1.3"
+ },
+ "devDependencies": {
+ "@vue/cli-plugin-babel": "~4.5.12",
+ "@vue/cli-plugin-eslint": "~4.5.12",
+ "@vue/cli-service": "~4.5.12",
+ "babel-eslint": "^10.1.0",
+ "eslint": "^6.7.2",
+ "eslint-plugin-vue": "^6.2.2",
+ "vue-template-compiler": "^2.6.11"
+ },
+ "eslintConfig": {
+ "root": true,
+ "env": {
+ "node": true
+ },
+ "extends": [
+ "plugin:vue/essential",
+ "eslint:recommended"
+ ],
+ "parserOptions": {
+ "parser": "babel-eslint"
+ },
+ "rules": {
+ "no-mixed-spaces-and-tabs": 0,
+ "generator-star-spacing": "off",
+ "no-tabs": "off",
+ "no-unused-vars": "off",
+ "no-unused-labels": "off",
+ "no-console": "off",
+ "vue/no-unused-components": "off",
+ "no-irregular-whitespace": "off",
+ "no-debugger": "off"
+ }
+ },
+ "browserslist": [
+ "> 1%",
+ "last 2 versions",
+ "not dead"
+ ]
+}
diff --git a/im-ui/public/favicon.ico b/im-ui/public/favicon.ico
new file mode 100644
index 0000000..df36fcf
Binary files /dev/null and b/im-ui/public/favicon.ico differ
diff --git a/im-ui/public/index.html b/im-ui/public/index.html
new file mode 100644
index 0000000..d2c9d76
--- /dev/null
+++ b/im-ui/public/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+ <%= htmlWebpackPlugin.options.title %>
+
+
+
+
+
+
+
diff --git a/im-ui/src/App.vue b/im-ui/src/App.vue
new file mode 100644
index 0000000..55ef82f
--- /dev/null
+++ b/im-ui/src/App.vue
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
diff --git a/im-ui/src/api/wssocket.js b/im-ui/src/api/wssocket.js
new file mode 100644
index 0000000..8cb13b0
--- /dev/null
+++ b/im-ui/src/api/wssocket.js
@@ -0,0 +1,154 @@
+var websock = null;
+let rec; //断线重连后,延迟5秒重新创建WebSocket连接 rec用来存储延迟请求的代码
+let isConnect = false; //连接标识 避免重复连接
+let isCompleteConnect = false; //完全连接标识(接收到心跳)
+let wsurl = "";
+let $store = null;
+let messageCallBack = null;
+let openCallBack = null;
+
+
+let createWebSocket = (url, store) => {
+ $store = store;
+ wsurl = url;
+ initWebSocket();
+};
+
+let initWebSocket = () => {
+ try {
+ console.log("初始化WebSocket");
+ isCompleteConnect = false;
+ websock = new WebSocket(wsurl);
+ websock.onmessage = function(e) {
+ let msg = JSON.parse(decodeUnicode(e.data))
+ if (msg.cmd == 0) {
+ if(!isCompleteConnect){
+ // 第一次上传心跳成功才算连接完成
+ isCompleteConnect = true;
+ openCallBack && openCallBack();
+ }
+ heartCheck.reset();
+ } else {
+ // 其他消息转发出去
+ messageCallBack && messageCallBack(JSON.parse(e.data))
+ }
+ }
+ websock.onclose = function(e) {
+ console.log('WebSocket连接关闭')
+ isConnect = false; //断开后修改标识
+ reConnect();
+ }
+ websock.onopen = function() {
+ console.log("WebSocket连接成功");
+ isConnect = true;
+ heartCheck.start()
+ }
+
+ // 连接发生错误的回调方法
+ websock.onerror = function() {
+ console.log('WebSocket连接发生错误')
+ isConnect = false; //连接断开修改标识
+ reConnect();
+ }
+ } catch (e) {
+ console.log("尝试创建连接失败");
+ reConnect(); //如果无法连接上webSocket 那么重新连接!可能会因为服务器重新部署,或者短暂断网等导致无法创建连接
+ }
+};
+
+//定义重连函数
+let reConnect = () => {
+ console.log("尝试重新连接");
+ if (isConnect) return; //如果已经连上就不在重连了
+ rec && clearTimeout(rec);
+ rec = setTimeout(function() { // 延迟5秒重连 避免过多次过频繁请求重连
+ initWebSocket(wsurl);
+ }, 5000);
+};
+//设置关闭连接
+let closeWebSocket = () => {
+ websock.close();
+};
+//心跳设置
+var heartCheck = {
+ timeout: 5000, //每段时间发送一次心跳包 这里设置为20s
+ timeoutObj: null, //延时发送消息对象(启动心跳新建这个对象,收到消息后重置对象)
+ start: function() {
+ if(isConnect){
+ console.log('发送WebSocket心跳')
+ let heartBeat = {
+ cmd: 0,
+ data: {
+ userId: $store.state.userStore.userInfo.id
+ }
+ };
+ websock.send(JSON.stringify(heartBeat))
+ }
+
+ },
+
+ reset: function(){
+ clearTimeout(this.timeoutObj);
+ this.timeoutObj = setTimeout(function() {
+ heartCheck.start();
+ }, this.timeout);
+
+ }
+};
+
+
+
+// 实际调用的方法
+function sendMessage(agentData) {
+ // console.log(globalCallback)
+ if (websock.readyState === websock.OPEN) {
+ // 若是ws开启状态
+ websock.send(JSON.stringify(agentData))
+ } else if (websock.readyState === websock.CONNECTING) {
+ // 若是 正在开启状态,则等待1s后重新调用
+ setTimeout(function() {
+ sendMessage(agentData)
+ }, 1000)
+ } else {
+ // 若未开启 ,则等待1s后重新调用
+ setTimeout(function() {
+ sendMessage(agentData)
+ }, 1000)
+ }
+}
+
+
+function onmessage(callback) {
+ messageCallBack = callback;
+}
+
+
+function onopen(callback) {
+ openCallBack = callback;
+ if (isCompleteConnect) {
+ openCallBack();
+ }
+}
+
+
+function decodeUnicode(str) {
+ str = str.replace(/\\/g, "%");
+ //转换中文
+ str = unescape(str);
+ //将其他受影响的转换回原来
+ str = str.replace(/%/g, "\\");
+ //对网址的链接进行处理
+ str = str.replace(/\\/g, "");
+ return str;
+
+}
+
+
+// 将方法暴露出去
+export {
+ createWebSocket,
+ closeWebSocket,
+ sendMessage,
+ onmessage,
+ onopen
+}
diff --git a/im-ui/src/assets/default_head.png b/im-ui/src/assets/default_head.png
new file mode 100644
index 0000000..5f6e18a
Binary files /dev/null and b/im-ui/src/assets/default_head.png differ
diff --git a/im-ui/src/assets/logo.png b/im-ui/src/assets/logo.png
new file mode 100644
index 0000000..f3d2503
Binary files /dev/null and b/im-ui/src/assets/logo.png differ
diff --git a/im-ui/src/assets/style/global.css b/im-ui/src/assets/style/global.css
new file mode 100644
index 0000000..5e3cd46
--- /dev/null
+++ b/im-ui/src/assets/style/global.css
@@ -0,0 +1,41 @@
+@charset "UTF-8";
+
+html {
+ height: 100%;
+ overflow: hidden;
+
+}
+
+body {
+ height: 100%;
+ margin: 0;
+ overflow: hidden;
+
+}
+
+section {
+ height: 100%;
+}
+
+
+
+::-webkit-scrollbar {
+ width: 6px;
+ height: 1px;
+}
+
+::-webkit-scrollbar-thumb {
+ /*滚动条里面小方块*/
+ border-radius: 2px;
+ -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
+ background: #535353;
+}
+
+::-webkit-scrollbar-track {
+ /*滚动条里面轨道*/
+ -webkit-box-shadow: inset 0 0 5px transparent;
+ border-radius: 2px;
+ background: #ededed;
+}
+
+/*# sourceMappingURL=v-im.cssss.map */
diff --git a/im-ui/src/common/globalApi.js b/im-ui/src/common/globalApi.js
new file mode 100644
index 0000000..85dfa93
--- /dev/null
+++ b/im-ui/src/common/globalApi.js
@@ -0,0 +1,15 @@
+import globalVariable from './globalInfo';
+
+function appendToken(url){
+ console.log(url);
+ if(url.indexOf('?')==-1){
+ url+="?"
+ }
+ url += `access_token=${globalVariable.token}`;
+ return url;
+}
+
+
+export default{
+ appendToken
+}
\ No newline at end of file
diff --git a/im-ui/src/common/globalInfo.js b/im-ui/src/common/globalInfo.js
new file mode 100644
index 0000000..2c62493
--- /dev/null
+++ b/im-ui/src/common/globalInfo.js
@@ -0,0 +1,5 @@
+const token = '';
+
+export default {
+ token
+}
diff --git a/im-ui/src/components/AddFriends.vue b/im-ui/src/components/AddFriends.vue
new file mode 100644
index 0000000..9dbbd6b
--- /dev/null
+++ b/im-ui/src/components/AddFriends.vue
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
{{userInfo.nickName}}
+
{{ userInfo.online?"[在线]":"[离线]"}}
+
+
添加
+
已添加
+
+
+
+
+
+
+
+
+
diff --git a/im-ui/src/components/ChatItem.vue b/im-ui/src/components/ChatItem.vue
new file mode 100644
index 0000000..0d0b50f
--- /dev/null
+++ b/im-ui/src/components/ChatItem.vue
@@ -0,0 +1,164 @@
+
+
+
+
+
+
+
+
{{chat.unreadCount}}
+
+
+
{{ chat.showName}}
+
{{chat.lastContent}}
+
+
+
+
+
+
+
+
+
diff --git a/im-ui/src/components/ChatTime.vue b/im-ui/src/components/ChatTime.vue
new file mode 100644
index 0000000..60ec149
--- /dev/null
+++ b/im-ui/src/components/ChatTime.vue
@@ -0,0 +1,43 @@
+
+ {{formatDate}}
+
+
+
+
+
diff --git a/im-ui/src/components/FriendsItem.vue b/im-ui/src/components/FriendsItem.vue
new file mode 100644
index 0000000..3194296
--- /dev/null
+++ b/im-ui/src/components/FriendsItem.vue
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
{{ friendsInfo.friendNickName}}
+
{{ online?"[在线]":"[离线]"}}
+
+
+
+
+
+
+
+
+
+
diff --git a/im-ui/src/components/HeadImage.vue b/im-ui/src/components/HeadImage.vue
new file mode 100644
index 0000000..eebc188
--- /dev/null
+++ b/im-ui/src/components/HeadImage.vue
@@ -0,0 +1,35 @@
+
+
+

+
+
+
+
+
+
\ No newline at end of file
diff --git a/im-ui/src/main.js b/im-ui/src/main.js
new file mode 100644
index 0000000..8a9008f
--- /dev/null
+++ b/im-ui/src/main.js
@@ -0,0 +1,33 @@
+import Vue from 'vue'
+import App from './App'
+import router from './router' // 自动扫描index.js
+import axios from 'axios'
+import VueAxios from 'vue-axios'
+import ElementUI from 'element-ui';
+import 'element-ui/lib/theme-chalk/index.css';
+import httpRequest from './utils/httpRequest';
+
+import globalApi from './common/globalApi';
+import globalInfo from './common/globalInfo';
+import * as socketApi from './api/wssocket' ;
+import store from './store';
+
+console.log(store);
+Vue.use(ElementUI);
+
+// 挂载全局
+
+Vue.prototype.$wsApi = socketApi;
+Vue.prototype.$http = httpRequest // http请求方法
+Vue.prototype.globalApi = globalApi; // 注册全局方法
+Vue.prototype.globalInfo = globalInfo; // 注册全局变量
+
+Vue.config.productionTip = false
+
+new Vue({
+ el: '#app',
+ // 配置路由
+ router,
+ store,
+ render: h=>h(App)
+})
diff --git a/im-ui/src/router/index.js b/im-ui/src/router/index.js
new file mode 100644
index 0000000..79e99f8
--- /dev/null
+++ b/im-ui/src/router/index.js
@@ -0,0 +1,44 @@
+import Vue from 'vue'
+import VueRouter from 'vue-router'
+import Login from '../view/Login'
+import Register from '../view/Register'
+import Home from '../view/Home'
+// 安装路由
+Vue.use(VueRouter);
+
+// 配置导出路由
+export default new VueRouter({
+ routes: [{
+ path: "/",
+ redirect: "/login"
+ },
+ {
+ name: "Login",
+ path: '/login',
+ component: Login
+ },
+ {
+ name: "Register",
+ path: '/register',
+ component: Register
+ },
+ {
+ name: "Home",
+ path: '/home',
+ component: Home,
+ children:[
+ {
+ name: "Chat",
+ path: "/home/chat",
+ component: () => import("../view/Chat"),
+ },
+ {
+ name: "Friends",
+ path: "/home/friends",
+ component: () => import("../view/Friends"),
+ },
+ ]
+ }
+ ]
+
+});
diff --git a/im-ui/src/store/chatStore.js b/im-ui/src/store/chatStore.js
new file mode 100644
index 0000000..62852e9
--- /dev/null
+++ b/im-ui/src/store/chatStore.js
@@ -0,0 +1,78 @@
+import httpRequest from '../utils/httpRequest.js'
+
+
+export default {
+
+ state: {
+ activeIndex: -1,
+ chats: []
+ },
+
+ mutations: {
+ openChat(state,chatInfo){
+ let chat = null;
+ for(let i in state.chats){
+ if(state.chats[i].targetId === chatInfo.targetId){
+
+ chat = state.chats[i];
+ // 放置头部
+ state.chats.splice(i,1);
+ state.chats.unshift(chat);
+ break;
+ }
+ }
+ // 创建会话
+ if (chat == null) {
+ chat = {
+ targetId: chatInfo.targetId,
+ type: chatInfo.type,
+ showName: chatInfo.showName,
+ headImage: chatInfo.headImage,
+ lastContent: "",
+ lastSendTime: new Date().getTime(),
+ unreadCount: 0,
+ messages: [],
+ };
+ state.chats.unshift(chat);
+ }
+
+ },
+ activeChat(state,idx){
+ state.activeIndex = idx;
+ state.chats[idx].unreadCount=0;
+ },
+ removeChat(state,idx){
+ state.chats.splice(idx, 1);
+ if(state.activeIndex >= state.chats.length){
+ state.activeIndex = state.chats.length-1;
+ }
+ },
+ insertMessage(state, msgInfo) {
+ let targetId = msgInfo.selfSend?msgInfo.recvUserId:msgInfo.sendUserId;
+ let chat = state.chats.find((chat)=>chat.targetId==targetId);
+
+ chat.lastContent = msgInfo.content;
+ chat.lastSendTime = msgInfo.sendTime;
+ chat.messages.push(msgInfo);
+ // 如果不是当前会话,未读加1
+ if(state.activeIndex == -1 || state.chats[state.activeIndex].targetId != targetId){
+ chat.unreadCount++;
+ }
+ },
+ setChatUserInfo(state, userInfo){
+ for(let i in state.chats){
+ if(state.chats[i].targetId == userInfo.id){
+ state.chats[i].headImage = userInfo.headImage;
+ state.chats[i].showName = userInfo.nickName;
+ break;
+ }
+ }
+ },
+ resetChatStore(state){
+ console.log("清空store")
+ state.activeIndex = -1;
+ state.chats = [];
+ }
+ },
+
+}
\ No newline at end of file
diff --git a/im-ui/src/store/friendsStore.js b/im-ui/src/store/friendsStore.js
new file mode 100644
index 0000000..f06fe70
--- /dev/null
+++ b/im-ui/src/store/friendsStore.js
@@ -0,0 +1,88 @@
+import httpRequest from '../utils/httpRequest.js'
+
+export default {
+
+ state: {
+ friendsList: [],
+ activeIndex: -1,
+ activeUserInfo: {},
+ timer: null
+ },
+ mutations: {
+ initFriendsStore(state, userInfo) {
+ httpRequest({
+ url: '/api/friends/list',
+ method: 'get'
+ }).then((friendsList) => {
+ this.commit("setFriendsList",friendsList);
+ this.commit("refreshOnlineStatus");
+ })
+ },
+ setActiveUserInfo(state, userInfo){
+ state.activeUserInfo = userInfo;
+ },
+ setFriendsList(state, friendsList) {
+ state.friendsList = friendsList;
+ },
+ activeFriends(state, index) {
+ state.activeIndex = index;
+ httpRequest({
+ url: `/api/user/find/${state.friendsList[index].friendId}`,
+ method: 'get'
+ }).then((userInfo) => {
+ this.commit("setActiveUserInfo",userInfo);
+ })
+ },
+ removeFriends(state, index) {
+ state.friendsList.splice(index, 1);
+ },
+ addFriends(state, friendsInfo) {
+ state.friendsList.push(friendsInfo);
+ },
+ refreshOnlineStatus(state){
+ let userIds = [];
+ state.friendsList.forEach((f)=>{userIds.push(f.friendId)});
+ httpRequest({
+ url: '/api/user/online',
+ method: 'get',
+ params: {userIds: userIds.join(',')}
+ }).then((onlineIds) => {
+ this.commit("setOnlineStatus",onlineIds);
+ })
+
+ // 30s后重新拉取
+ clearTimeout(state.timer);
+ state.timer = setTimeout(()=>{
+ this.commit("refreshOnlineStatus");
+ },30000)
+ },
+ setOnlineStatus(state,onlineIds){
+ state.friendsList.forEach((f)=>{
+ let onlineFriend = onlineIds.find((id)=> f.friendId==id);
+ f.online = onlineFriend != undefined;
+ console.log(f.friendNickName+":"+f.online);
+ });
+
+ let activeFriend = state.friendsList[state.activeIndex];
+ state.friendsList.sort((f1,f2)=>{
+ if(f1.online&&!f2.online){
+ return -1;
+ }
+ if(f2.online&&!f1.online){
+ return 1;
+ }
+ return 0;
+ });
+
+ // 重新排序后,activeIndex指向的好友可能会变化,需要重新指定
+ if(state.activeIndex >=0){
+ state.friendsList.forEach((f,i)=>{
+ if(f.friendId == activeFriend.friendId){
+ state.activeIndex = i;
+ }
+ })
+ }
+ }
+
+ }
+}
diff --git a/im-ui/src/store/index.js b/im-ui/src/store/index.js
new file mode 100644
index 0000000..b01db4c
--- /dev/null
+++ b/im-ui/src/store/index.js
@@ -0,0 +1,30 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import chatStore from './chatStore.js';
+import friendsStore from './friendsStore.js';
+import userStore from './userStore.js';
+import VuexPersistence from 'vuex-persist'
+
+
+const vuexLocal = new VuexPersistence({
+ storage: window.localStorage,
+ modules: ["userStore","chatStore"]
+})
+
+Vue.use(Vuex)
+
+export default new Vuex.Store({
+ modules: {chatStore,friendsStore,userStore},
+ state: {
+ userInfo: {}
+ },
+ plugins: [vuexLocal.plugin],
+ mutations: {
+ initStore(state){
+
+ this.commit("initFriendsStore");
+ }
+
+ },
+ strict: process.env.NODE_ENV !== 'production'
+})
diff --git a/im-ui/src/store/userStore.js b/im-ui/src/store/userStore.js
new file mode 100644
index 0000000..4ad45bd
--- /dev/null
+++ b/im-ui/src/store/userStore.js
@@ -0,0 +1,18 @@
+export default {
+
+ state: {
+ userInfo: {}
+ },
+
+ mutations: {
+ setUserInfo(state, userInfo) {
+ // 切换用户后,清理缓存
+ if(userInfo.id != state.userInfo.id){
+ console.log("用户切换")
+ this.commit("resetChatStore");
+ }
+ state.userInfo = userInfo;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/im-ui/src/utils/httpRequest.js b/im-ui/src/utils/httpRequest.js
new file mode 100644
index 0000000..b03e4fa
--- /dev/null
+++ b/im-ui/src/utils/httpRequest.js
@@ -0,0 +1,90 @@
+import axios from 'axios'
+import router from '@/router'
+import qs from 'qs'
+import merge from 'lodash/merge'
+import {
+ Message
+} from 'element-ui'
+
+const http = axios.create({
+ timeout: 1000 * 30,
+ withCredentials: true,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ }
+})
+
+/**
+ * 请求拦截
+ */
+http.interceptors.request.use(config => {
+ // todo 请求头带上token
+ return config
+}, error => {
+ return Promise.reject(error)
+})
+
+/**
+ * 响应拦截
+ */
+http.interceptors.response.use(response => {
+ if (response.data.code == 200) {
+ return response.data.data;
+ } else {
+ Message({
+ message: response.data.message,
+ type: 'error',
+ duration: 1500,
+ customClass: 'element-error-message-zindex'
+ })
+
+ if (response.data.code == 401) {
+ router.replace("/login");
+ }
+ return Promise.reject(response.data)
+ }
+}, error => {
+ switch (error.response.status) {
+ case 400:
+ Message({
+ message: error.response.data,
+ type: 'error',
+ duration: 1500,
+ customClass: 'element-error-message-zindex'
+ })
+ break
+ case 401:
+ router.replace("/login");
+ break
+ case 405:
+ Message({
+ message: 'http请求方式有误',
+ type: 'error',
+ duration: 1500,
+ customClass: 'element-error-message-zindex'
+ })
+ break
+ case 404:
+ case 500:
+ Message({
+ message: '服务器出了点小差,请稍后再试',
+ type: 'error',
+ duration: 1500,
+ customClass: 'element-error-message-zindex'
+ })
+ break
+ case 501:
+ Message({
+ message: '服务器不支持当前请求所需要的某个功能',
+ type: 'error',
+ duration: 1500,
+ customClass: 'element-error-message-zindex'
+ })
+ break
+ }
+
+ return Promise.reject(error)
+})
+
+
+export default http
diff --git a/im-ui/src/view/Chat.vue b/im-ui/src/view/Chat.vue
new file mode 100644
index 0000000..ea196d0
--- /dev/null
+++ b/im-ui/src/view/Chat.vue
@@ -0,0 +1,321 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{titleName}}
+
+
+
+
+ -
+
+
+
+
+
+ {{showName(item)}}
+
+
+
+ {{item.content}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/im-ui/src/view/Friends.vue b/im-ui/src/view/Friends.vue
new file mode 100644
index 0000000..ba78274
--- /dev/null
+++ b/im-ui/src/view/Friends.vue
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $store.state.friendsStore.activeUserInfo.userName }}
+
+ {{ $store.state.friendsStore.activeUserInfo.nickName }}
+
+ 好基友
+ 世界这么大,我想去看看
+
+
+
+
+ 发消息
+
+
+
+
+
+
+
+
+
diff --git a/im-ui/src/view/Home.vue b/im-ui/src/view/Home.vue
new file mode 100644
index 0000000..db395ed
--- /dev/null
+++ b/im-ui/src/view/Home.vue
@@ -0,0 +1,155 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/im-ui/src/view/Login.vue b/im-ui/src/view/Login.vue
new file mode 100644
index 0000000..59300cb
--- /dev/null
+++ b/im-ui/src/view/Login.vue
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+ 欢迎登陆fly-chat
+
+
+
+
+
+
+
+
+ 登陆
+ 清空
+
+
+ 没有账号,前往注册
+
+
+
+
+
+
+
+
+
+
diff --git a/im-ui/src/view/Register.vue b/im-ui/src/view/Register.vue
new file mode 100644
index 0000000..a51d805
--- /dev/null
+++ b/im-ui/src/view/Register.vue
@@ -0,0 +1,161 @@
+
+
+
+
+
+ 欢迎注册成为FLY CHAT用户
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 注册
+ 清空
+
+
+ 已有账号,前往登录
+
+
+
+
+
+
+
+
+
diff --git a/im-ui/vue.config.js b/im-ui/vue.config.js
new file mode 100644
index 0000000..a991c1f
--- /dev/null
+++ b/im-ui/vue.config.js
@@ -0,0 +1,16 @@
+module.exports = {
+
+ devServer: {
+ proxy: {
+ '/api': {
+ target: 'http://127.0.0.1:8888',
+ changeOrigin: true,
+ ws: false,
+ pathRewrite: {
+ '^/api': ''
+ }
+ }
+ }
+ }
+
+}