31 changed files with 2178 additions and 0 deletions
@ -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 |
||||
@ -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/). |
||||
@ -0,0 +1,5 @@ |
|||||
|
module.exports = { |
||||
|
presets: [ |
||||
|
'@vue/cli-plugin-babel/preset' |
||||
|
] |
||||
|
} |
||||
@ -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" |
||||
|
] |
||||
|
} |
||||
|
After Width: | Height: | Size: 4.2 KiB |
@ -0,0 +1,17 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang=""> |
||||
|
<head> |
||||
|
<meta charset="utf-8"> |
||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0"> |
||||
|
<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> |
||||
@ -0,0 +1,30 @@ |
|||||
|
<template> |
||||
|
<div id="app"> |
||||
|
<router-view></router-view> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
|
||||
|
|
||||
|
export default { |
||||
|
name: 'App', |
||||
|
components: { |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
@import './assets/style/global.css'; |
||||
|
|
||||
|
#app { |
||||
|
font-family: 'Avenir', Helvetica, Arial, sans-serif; |
||||
|
-webkit-font-smoothing: antialiased; |
||||
|
-moz-osx-font-smoothing: grayscale; |
||||
|
text-align: center; |
||||
|
color: #2c3e50; |
||||
|
position: absolute; |
||||
|
height: 100%; |
||||
|
width: 100%; |
||||
|
} |
||||
|
</style> |
||||
@ -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 |
||||
|
} |
||||
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
@ -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 */ |
||||
@ -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 |
||||
|
} |
||||
@ -0,0 +1,5 @@ |
|||||
|
const token = ''; |
||||
|
|
||||
|
export default { |
||||
|
token |
||||
|
} |
||||
@ -0,0 +1,118 @@ |
|||||
|
<template> |
||||
|
<el-dialog title="添加好友" :visible.sync="dialogVisible" width="500px" :before-close="onClose"> |
||||
|
<el-input width="200px" placeholder="搜索好友" class="input-with-select" v-model="searchText" @keyup.enter.native="onSearch()"> |
||||
|
<el-button slot="append" icon="el-icon-search" @click="onSearch()"></el-button> |
||||
|
</el-input> |
||||
|
<el-scrollbar style="height:600px"> |
||||
|
<div v-for="(userInfo) in users" :key="userInfo.id"> |
||||
|
<div class="item"> |
||||
|
<div class="avatar"> |
||||
|
<head-image :url="userInfo.headImage"></head-image> |
||||
|
</div> |
||||
|
<div class="add-friend-text"> |
||||
|
<div>{{userInfo.nickName}}</div> |
||||
|
<div :class="userInfo.online ? 'online-status online':'online-status'">{{ userInfo.online?"[在线]":"[离线]"}}</div> |
||||
|
</div> |
||||
|
<el-button type="success" v-show="!isFriend(userInfo.id)" plain @click="onAddFriends(userInfo)">添加</el-button> |
||||
|
<el-button type="info" v-show="isFriend(userInfo.id)" plain disabled>已添加</el-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</el-scrollbar> |
||||
|
</el-dialog> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import HeadImage from './HeadImage.vue' |
||||
|
|
||||
|
|
||||
|
export default { |
||||
|
name: "addFriends", |
||||
|
components:{HeadImage}, |
||||
|
data() { |
||||
|
return { |
||||
|
users: [], |
||||
|
searchText: "" |
||||
|
} |
||||
|
}, |
||||
|
props: { |
||||
|
dialogVisible: { |
||||
|
type: Boolean |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
onClose() { |
||||
|
this.$emit("close"); |
||||
|
}, |
||||
|
onSearch() { |
||||
|
this.$http({ |
||||
|
url: "/api/user/findByNickName", |
||||
|
method: "get", |
||||
|
params: { |
||||
|
nickName: this.searchText |
||||
|
} |
||||
|
}).then((data) => { |
||||
|
this.users = data; |
||||
|
}) |
||||
|
}, |
||||
|
onAddFriends(userInfo){ |
||||
|
this.$http({ |
||||
|
url: "/api/friends/add", |
||||
|
method: "post", |
||||
|
params: { |
||||
|
friendId: userInfo.id |
||||
|
} |
||||
|
}).then((data) => { |
||||
|
this.$store.commit("") |
||||
|
this.$message.success("添加成功,对方已成为您的好友"); |
||||
|
let friendsInfo = { |
||||
|
friendId:userInfo.id, |
||||
|
friendNickName: userInfo.nickName, |
||||
|
friendHeadImage: userInfo.headImage, |
||||
|
online: userInfo.online |
||||
|
} |
||||
|
this.$store.commit("addFriends",friendsInfo); |
||||
|
|
||||
|
}) |
||||
|
}, |
||||
|
isFriend(userId){ |
||||
|
let friendList = this.$store.state.friendsStore.friendsList; |
||||
|
let friend = friendList.find((f)=> f.friendId==userId); |
||||
|
return friend != undefined; |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
mounted() { |
||||
|
this.onSearch(); |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.item { |
||||
|
height: 80px; |
||||
|
display: flex; |
||||
|
position: relative; |
||||
|
padding-left: 15px; |
||||
|
align-items: center; |
||||
|
padding-right: 25px; |
||||
|
|
||||
|
.add-friend-text { |
||||
|
margin-left: 15px; |
||||
|
line-height: 80px; |
||||
|
flex: 3; |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
height: 100%; |
||||
|
flex-shrink: 0; |
||||
|
overflow: hidden; |
||||
|
|
||||
|
.online-status{ |
||||
|
font-size: 12px; |
||||
|
font-weight: 600; |
||||
|
&.online{ |
||||
|
color: #5fb878; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,164 @@ |
|||||
|
<template> |
||||
|
|
||||
|
<div class="item" :class="active ? 'active' : ''"> |
||||
|
<div class="left"> |
||||
|
<head-image :url="chat.headImage" :size="40"> |
||||
|
|
||||
|
</head-image> |
||||
|
<div v-show="chat.unreadCount>0" class="unread-text">{{chat.unreadCount}}</div> |
||||
|
</div> |
||||
|
<div class="mid"> |
||||
|
<div>{{ chat.showName}}</div> |
||||
|
<div class="msg-text">{{chat.lastContent}}</div> |
||||
|
</div> |
||||
|
<div class="right "> |
||||
|
<div @click.stop="onClickClose()"><i class="el-icon-close close" style="border: none; font-size: 20px;color: black;" title="关闭"></i></div> |
||||
|
|
||||
|
<div class="msg-time"> |
||||
|
<chat-time :time="chat.lastSendTime"></chat-time> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import ChatTime from "./ChatTime.vue"; |
||||
|
import HeadImage from './HeadImage.vue'; |
||||
|
|
||||
|
export default { |
||||
|
name: "chatItem", |
||||
|
components: { |
||||
|
ChatTime, |
||||
|
HeadImage |
||||
|
}, |
||||
|
data() { |
||||
|
return {} |
||||
|
}, |
||||
|
props: { |
||||
|
chat: { |
||||
|
type: Object |
||||
|
}, |
||||
|
active: { |
||||
|
type: Boolean |
||||
|
}, |
||||
|
index: { |
||||
|
type: Number |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
onClickClose(){ |
||||
|
this.$emit("del"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scode lang="scss"> |
||||
|
.item { |
||||
|
height: 65px; |
||||
|
display: flex; |
||||
|
margin-bottom: 1px; |
||||
|
position: relative; |
||||
|
padding-left: 15px; |
||||
|
align-items: center; |
||||
|
padding-right: 5px; |
||||
|
background-color: #eeeeee; |
||||
|
|
||||
|
&:hover { |
||||
|
background-color: #dddddd; |
||||
|
} |
||||
|
|
||||
|
&.active { |
||||
|
background-color: #cccccc; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
&:hover { |
||||
|
.close { |
||||
|
display: block !important; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.left { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
width: 45px; |
||||
|
height: 45px; |
||||
|
|
||||
|
.unread-text { |
||||
|
position: absolute; |
||||
|
background-color: #f56c6c; |
||||
|
right: -8px; |
||||
|
top: -8px; |
||||
|
color: white; |
||||
|
border-radius: 30px; |
||||
|
padding: 0 5px; |
||||
|
font-size: 10px; |
||||
|
text-align: center; |
||||
|
white-space: nowrap; |
||||
|
border: 1px solid #f1e5e5; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
.mid { |
||||
|
margin-left: 15px; |
||||
|
flex: 3; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
height: 100%; |
||||
|
flex-shrink: 0; |
||||
|
overflow: hidden; |
||||
|
|
||||
|
&>div { |
||||
|
display: flex; |
||||
|
justify-content: flex-start; |
||||
|
align-items: center; |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.msg-text { |
||||
|
font-size: 14px; |
||||
|
color: #888888; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.right { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: flex-end; |
||||
|
height: 100%; |
||||
|
flex-shrink: 0; |
||||
|
overflow: hidden; |
||||
|
|
||||
|
&>div { |
||||
|
display: flex; |
||||
|
justify-content: flex-start; |
||||
|
align-items: center; |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.close { |
||||
|
width: 1.5rem; |
||||
|
height: 1.5rem; |
||||
|
right: 0; |
||||
|
top: 1rem; |
||||
|
cursor: pointer; |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
.msg-time { |
||||
|
font-size: 14px; |
||||
|
color: #888888; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.active { |
||||
|
background-color: #eeeeee; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,43 @@ |
|||||
|
<template> |
||||
|
<span>{{formatDate}}</span> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: "chatTime", |
||||
|
data() { |
||||
|
return {} |
||||
|
}, |
||||
|
props: { |
||||
|
time: { |
||||
|
type: Number |
||||
|
} |
||||
|
}, |
||||
|
computed:{ |
||||
|
formatDate(){ |
||||
|
console.log(this.time); |
||||
|
let time = new Date(this.time); |
||||
|
let strtime = ""; |
||||
|
let curTime = new Date(); |
||||
|
let dayDiff =curTime.getDate() - time.getDate() ; |
||||
|
if (time.getDate() === new Date().getDate()) { |
||||
|
strtime = time.getHours() < 9 ? "0" + time.getHours() : time.getHours(); |
||||
|
strtime += ":" |
||||
|
strtime += time.getMinutes() < 9 ? "0" + time.getMinutes() : time.getMinutes(); |
||||
|
} else if (dayDiff === 1) { |
||||
|
strtime = "昨天"; |
||||
|
} else if (dayDiff < 7) { |
||||
|
strtime = `${dayDiff}天前`; |
||||
|
} else { |
||||
|
strtime = time.getMonth()+1+"月"+time.getDate()+"日"; |
||||
|
} |
||||
|
|
||||
|
console.log(strtime); |
||||
|
return strtime; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
</style> |
||||
@ -0,0 +1,114 @@ |
|||||
|
<template> |
||||
|
<div class="item" :class="active ? 'active' : ''"> |
||||
|
<div class="avatar"> |
||||
|
<head-image :src="friendsInfo.friendHeadImage" :size="40"> </head-image> |
||||
|
</div> |
||||
|
<div class="text"> |
||||
|
<div>{{ friendsInfo.friendNickName}}</div> |
||||
|
<div :class="online ? 'online-status online':'online-status'">{{ online?"[在线]":"[离线]"}}</div> |
||||
|
</div> |
||||
|
<div class="close" @click.stop="$emit('del',friendsInfo,index)"> |
||||
|
<i class="el-icon-close" style="border: none; font-size: 20px;color: black;" title="添加好友"></i> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import HeadImage from './HeadImage.vue'; |
||||
|
|
||||
|
export default { |
||||
|
name: "frinedsItem", |
||||
|
components: {HeadImage}, |
||||
|
data() { |
||||
|
return { |
||||
|
} |
||||
|
}, |
||||
|
props: { |
||||
|
friendsInfo: { |
||||
|
type: Object |
||||
|
}, |
||||
|
active:{ |
||||
|
type: Boolean |
||||
|
}, |
||||
|
index:{ |
||||
|
type: Number |
||||
|
} |
||||
|
}, |
||||
|
computed:{ |
||||
|
online(){ |
||||
|
return this.$store.state.friendsStore.friendsList[this.index].online; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scope lang="scss"> |
||||
|
.item { |
||||
|
height: 65px; |
||||
|
display: flex; |
||||
|
margin-bottom: 1px; |
||||
|
position: relative; |
||||
|
padding-left: 15px; |
||||
|
align-items: center; |
||||
|
padding-right: 5px; |
||||
|
background-color: #eeeeee; |
||||
|
&:hover { |
||||
|
background-color: #dddddd; |
||||
|
} |
||||
|
|
||||
|
&.active{ |
||||
|
background-color: #cccccc; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
.close { |
||||
|
width: 1.5rem; |
||||
|
height: 1.5rem; |
||||
|
right: 10px; |
||||
|
top: 1.825rem; |
||||
|
cursor: pointer; |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
&:hover { |
||||
|
.close { |
||||
|
display: block; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.avatar { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
width: 45px; |
||||
|
height: 45px; |
||||
|
} |
||||
|
|
||||
|
.text { |
||||
|
margin-left: 15px; |
||||
|
flex: 3; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: space-around; |
||||
|
height: 100%; |
||||
|
flex-shrink: 0; |
||||
|
overflow: hidden; |
||||
|
&>div { |
||||
|
display: flex; |
||||
|
justify-content: flex-start; |
||||
|
} |
||||
|
|
||||
|
.online-status{ |
||||
|
font-size: 12px; |
||||
|
font-weight: 600; |
||||
|
&.online{ |
||||
|
color: #5fb878; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.active { |
||||
|
background-color: #eeeeee; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,35 @@ |
|||||
|
<template> |
||||
|
<div class='img-box'> |
||||
|
<img src="../assets/default_head.png" style="width: 100%;height: 100%;cursor: pointer;" /> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: "headImage", |
||||
|
data() { |
||||
|
return {} |
||||
|
}, |
||||
|
props: { |
||||
|
size: { |
||||
|
type: Number, |
||||
|
default: 50 |
||||
|
}, |
||||
|
url:{ |
||||
|
type: String, |
||||
|
default: '../assets/default_head.png' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.img-box { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
display: inline-block; |
||||
|
border-radius: 3px; |
||||
|
background-color: #c0c4cc; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
</style> |
||||
@ -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) |
||||
|
}) |
||||
@ -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"), |
||||
|
}, |
||||
|
] |
||||
|
} |
||||
|
] |
||||
|
|
||||
|
}); |
||||
@ -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 = []; |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -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' |
||||
|
}) |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -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 |
||||
@ -0,0 +1,321 @@ |
|||||
|
<template> |
||||
|
<el-container> |
||||
|
<el-aside width="250px" class="l-chat-box"> |
||||
|
<el-header height="60px"> |
||||
|
<el-row> |
||||
|
<el-input width="200px" placeholder="搜索" v-model="searchText"> |
||||
|
<el-button slot="append" icon="el-icon-search"></el-button> |
||||
|
</el-input> |
||||
|
</el-row> |
||||
|
</el-header> |
||||
|
<el-main> |
||||
|
<div v-for="(chat,index) in $store.state.chatStore.chats" :key="chat.targetId"> |
||||
|
<chat-item :chat="chat" :index="index" @click.native="onClickItem(index)" @del="onDelItem(chat,index)" |
||||
|
:active="index === $store.state.chatStore.activeIndex"></chat-item> |
||||
|
</div> |
||||
|
</el-main> |
||||
|
</el-aside> |
||||
|
<el-container class="r-chat-box"> |
||||
|
<el-header height="60px"> |
||||
|
{{titleName}} |
||||
|
</el-header> |
||||
|
<el-main class="im-chat-main" id="chatScrollBox"> |
||||
|
<div class="im-chat-box"> |
||||
|
<ul> |
||||
|
<li v-for="item in messages" :key="item.id" |
||||
|
:class="{ 'im-chat-mine': item.sendUserId == $store.state.userStore.userInfo.id }"> |
||||
|
<div class="head-image"> |
||||
|
<head-image :url="headImage" ></head-image> |
||||
|
</div> |
||||
|
<div class="im-msg-content"> |
||||
|
<div class="im-msg-top"> |
||||
|
<span>{{showName(item)}}</span> |
||||
|
<chat-time :time="item.sendTime"></chat-time> |
||||
|
</div> |
||||
|
<div class="im-msg-bottom"> |
||||
|
<span class="im-msg-text">{{item.content}}</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</li> |
||||
|
</ul> |
||||
|
|
||||
|
</div> |
||||
|
</el-main> |
||||
|
<el-footer height="150px" class="im-chat-footer"> |
||||
|
<textarea v-model="messageContent" ref="sendBox" class="textarea" @keyup.enter="onSendMessage()"></textarea> |
||||
|
<div class="im-chat-send"> |
||||
|
<el-button type="primary" @click="onSendMessage()">发送</el-button> |
||||
|
</div> |
||||
|
</el-footer> |
||||
|
</el-container> |
||||
|
</el-container> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import ChatItem from "../components/ChatItem.vue"; |
||||
|
import ChatTime from "../components/ChatTime.vue"; |
||||
|
import HeadImage from "../components/HeadImage.vue"; |
||||
|
|
||||
|
export default { |
||||
|
name: "chat", |
||||
|
components: { |
||||
|
ChatItem, |
||||
|
ChatTime, |
||||
|
HeadImage |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
searchText: "", |
||||
|
messageContent: "" |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
onClickItem(index) { |
||||
|
this.$store.commit("activeChat", index); |
||||
|
}, |
||||
|
onSendMessage() { |
||||
|
let msgInfo = { |
||||
|
recvUserId: this.$store.state.chatStore.chats[this.$store.state.chatStore.activeIndex].targetId, |
||||
|
content: this.messageContent, |
||||
|
type: 0 |
||||
|
} |
||||
|
this.$http({ |
||||
|
url: '/api/message/single/send', |
||||
|
method: 'post', |
||||
|
data: msgInfo |
||||
|
}).then((data) => { |
||||
|
this.$message.success("发送成功"); |
||||
|
this.messageContent = ""; |
||||
|
msgInfo.sendTime = new Date().getTime(); |
||||
|
msgInfo.sendUserId = this.$store.state.userStore.userInfo.id; |
||||
|
msgInfo.selfSend = true; |
||||
|
this.$store.commit("insertMessage", msgInfo); |
||||
|
console.log(this.$refs.sendBox) |
||||
|
// 保持输入框焦点 |
||||
|
this.$refs.sendBox.focus(); |
||||
|
// 滚动到底部 |
||||
|
this.$nextTick(() => { |
||||
|
const div = document.getElementById("chatScrollBox"); |
||||
|
div.scrollTop = div.scrollHeight; |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
}) |
||||
|
}, |
||||
|
onDelItem(chat,index){ |
||||
|
this.$store.commit("removeChat",index); |
||||
|
}, |
||||
|
showName(item) { |
||||
|
if (item.sendUserId == this.$store.state.userStore.userInfo.id) { |
||||
|
return this.$store.state.userStore.userInfo.nickName; |
||||
|
} else { |
||||
|
let index = this.$store.state.chatStore.activeIndex; |
||||
|
let chats = this.$store.state.chatStore.chats |
||||
|
return chats[index].showName; |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
messages() { |
||||
|
let index = this.$store.state.chatStore.activeIndex; |
||||
|
let chats = this.$store.state.chatStore.chats |
||||
|
if (index >= 0 && chats.length > 0) { |
||||
|
return chats[index].messages; |
||||
|
} |
||||
|
return []; |
||||
|
}, |
||||
|
titleName(){ |
||||
|
let index = this.$store.state.chatStore.activeIndex; |
||||
|
let chats = this.$store.state.chatStore.chats |
||||
|
if(index>=0 && chats.length > 0){ |
||||
|
let chats = this.$store.state.chatStore.chats; |
||||
|
return chats[index].showName; |
||||
|
} |
||||
|
return ""; |
||||
|
}, |
||||
|
headImage(){ |
||||
|
let index = this.$store.state.chatStore.activeIndex; |
||||
|
let chats = this.$store.state.chatStore.chats |
||||
|
if(index>=0 && chats.length > 0){ |
||||
|
let chats = this.$store.state.chatStore.chats; |
||||
|
return chats[index].headImage; |
||||
|
} |
||||
|
return ""; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.el-container { |
||||
|
|
||||
|
.l-chat-box { |
||||
|
border: #dddddd solid 1px; |
||||
|
background: #eeeeee; |
||||
|
width: 3rem; |
||||
|
.el-header { |
||||
|
padding: 5px; |
||||
|
background-color: white; |
||||
|
line-height: 50px; |
||||
|
} |
||||
|
|
||||
|
.el-main { |
||||
|
padding: 0 |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.r-chat-box { |
||||
|
background: white; |
||||
|
border: #dddddd solid 1px; |
||||
|
.el-header { |
||||
|
padding: 5px; |
||||
|
background-color: white; |
||||
|
line-height: 50px; |
||||
|
} |
||||
|
|
||||
|
.im-chat-main { |
||||
|
padding: 0; |
||||
|
border: #dddddd solid 1px; |
||||
|
.im-chat-box { |
||||
|
ul { |
||||
|
padding: 10px; |
||||
|
|
||||
|
li { |
||||
|
position: relative; |
||||
|
font-size: 0; |
||||
|
margin-bottom: 10px; |
||||
|
padding-left: 60px; |
||||
|
min-height: 68px; |
||||
|
|
||||
|
|
||||
|
.head-image { |
||||
|
position: absolute; |
||||
|
width: 40px; |
||||
|
height: 40px; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
} |
||||
|
|
||||
|
.im-msg-content { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
|
||||
|
.im-msg-top { |
||||
|
display: flex; |
||||
|
flex-wrap: nowrap; |
||||
|
color: #333; |
||||
|
font-size: 14px; |
||||
|
line-height: 20px; |
||||
|
|
||||
|
span { |
||||
|
margin-right: 12px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.im-msg-bottom { |
||||
|
text-align: left; |
||||
|
|
||||
|
.im-msg-text { |
||||
|
position: relative; |
||||
|
line-height: 22px; |
||||
|
margin-top: 10px; |
||||
|
padding: 10px; |
||||
|
background-color: #eeeeee; |
||||
|
border-radius: 3px; |
||||
|
color: #333; |
||||
|
display: inline-block; |
||||
|
font-size: 14px; |
||||
|
|
||||
|
&:after { |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
left: -10px; |
||||
|
top: 13px; |
||||
|
width: 0; |
||||
|
height: 0; |
||||
|
border-style: solid dashed dashed; |
||||
|
border-color: #eeeeee transparent transparent; |
||||
|
overflow: hidden; |
||||
|
border-width: 10px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.im-chat-mine { |
||||
|
text-align: right; |
||||
|
padding-left: 0; |
||||
|
padding-right: 60px; |
||||
|
|
||||
|
.head-image { |
||||
|
left: auto; |
||||
|
right: 0; |
||||
|
} |
||||
|
|
||||
|
.im-msg-content { |
||||
|
|
||||
|
.im-msg-top { |
||||
|
flex-direction: row-reverse; |
||||
|
|
||||
|
span { |
||||
|
margin-left: 12px; |
||||
|
margin-right: 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.im-msg-bottom { |
||||
|
text-align: right; |
||||
|
|
||||
|
.im-msg-text { |
||||
|
margin-left: 10px; |
||||
|
background-color: #5fb878; |
||||
|
color: #fff; |
||||
|
display: inline-block; |
||||
|
vertical-align: top; |
||||
|
font-size: 14px; |
||||
|
|
||||
|
&:after { |
||||
|
left: auto; |
||||
|
right: -10px; |
||||
|
border-top-color: #5fb878; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.message-info { |
||||
|
right: 60px !important; |
||||
|
display: inline-block; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
.im-chat-footer { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
padding: 0; |
||||
|
|
||||
|
textarea { |
||||
|
box-sizing: border-box; |
||||
|
padding: 5px; |
||||
|
width: 100%; |
||||
|
flex: 1; |
||||
|
resize: none; |
||||
|
background-color: #f8f8f8 !important; |
||||
|
outline-color: rgba(83, 160, 231, 0.61); |
||||
|
} |
||||
|
|
||||
|
.im-chat-send { |
||||
|
text-align: right; |
||||
|
padding: 7px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,163 @@ |
|||||
|
<template> |
||||
|
<el-container> |
||||
|
<el-aside width="250px" class="l-friend-box"> |
||||
|
<el-header height="60px"> |
||||
|
<el-row> |
||||
|
<el-col :span="19"> |
||||
|
<el-input width="200px" placeholder="搜索好友" v-model="searchText"> |
||||
|
<el-button slot="append" icon="el-icon-search"></el-button> |
||||
|
</el-input> |
||||
|
</el-col> |
||||
|
<el-col :span="1"></el-col> |
||||
|
<el-col :span="3"> |
||||
|
<el-button plain icon="el-icon-plus" style="border: none; font-size: 20px;color: black;" |
||||
|
title="添加好友" @click="onShowAddFriends"></el-button> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
<add-friends :dialogVisible="showAddFriend" @close="onCloseAddFriends" @add="onAddFriend()"> |
||||
|
</add-friends> |
||||
|
</el-header> |
||||
|
<el-main> |
||||
|
<div v-for="(friendsInfo,index) in $store.state.friendsStore.friendsList" :key="friendsInfo.id"> |
||||
|
<friends-item v-show="friendsInfo.friendNickName.startsWith(searchText)" :friendsInfo="friendsInfo" |
||||
|
:index="index" :active="index === $store.state.friendsStore.activeIndex" |
||||
|
@del="onDelItem(friendsInfo,index)" @click.native="onClickItem(friendsInfo,index)"> |
||||
|
</friends-item> |
||||
|
</div> |
||||
|
</el-main> |
||||
|
</el-aside> |
||||
|
<el-container class="r-friend-box"> |
||||
|
<div v-show="$store.state.friendsStore.activeIndex>=0"> |
||||
|
<div class="user-detail"> |
||||
|
<div class="detail-head-image"> |
||||
|
<head-image :url="$store.state.friendsStore.activeUserInfo.headImage" ></head-image> |
||||
|
</div> |
||||
|
<div class="info-item"> |
||||
|
<el-descriptions title="好友信息" class="description" :column="1"> |
||||
|
<el-descriptions-item label="用户名">{{ $store.state.friendsStore.activeUserInfo.userName }} |
||||
|
</el-descriptions-item> |
||||
|
<el-descriptions-item label="昵称">{{ $store.state.friendsStore.activeUserInfo.nickName }} |
||||
|
</el-descriptions-item> |
||||
|
<el-descriptions-item label="备注">好基友</el-descriptions-item> |
||||
|
<el-descriptions-item label="签名">世界这么大,我想去看看</el-descriptions-item> |
||||
|
</el-descriptions> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="btn-group"> |
||||
|
<el-button class="send-btn" @click="onSend()">发消息</el-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</el-container> |
||||
|
</el-container> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import FriendsItem from "../components/FriendsItem.vue"; |
||||
|
import AddFriends from "../components/AddFriends.vue"; |
||||
|
import HeadImage from "../components/HeadImage.vue"; |
||||
|
|
||||
|
export default { |
||||
|
name: "friends", |
||||
|
components: { |
||||
|
FriendsItem, |
||||
|
AddFriends, |
||||
|
HeadImage |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
searchText: "", |
||||
|
showAddFriend: false |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
onShowAddFriends() { |
||||
|
this.showAddFriend = true; |
||||
|
}, |
||||
|
onCloseAddFriends() { |
||||
|
this.showAddFriend = false; |
||||
|
}, |
||||
|
onClickItem(friendsInfo, index) { |
||||
|
this.$store.commit("activeFriends", index); |
||||
|
}, |
||||
|
onDelItem(friendsInfo, index) { |
||||
|
this.$http({ |
||||
|
url: '/api/friends/delete', |
||||
|
method: 'delete', |
||||
|
params: { |
||||
|
friendId: friendsInfo.friendId |
||||
|
} |
||||
|
}).then((data) => { |
||||
|
this.$message.success("删除好友成功"); |
||||
|
this.$store.commit("removeFriends", index); |
||||
|
}) |
||||
|
}, |
||||
|
onSend() { |
||||
|
let userInfo = this.$store.state.friendsStore.activeUserInfo |
||||
|
let chatInfo = { |
||||
|
type: 'single', |
||||
|
targetId: userInfo.id, |
||||
|
showName: userInfo.nickName, |
||||
|
headImage: userInfo.headImage, |
||||
|
}; |
||||
|
this.$store.commit("openChat", chatInfo); |
||||
|
this.$store.commit("activeChat", 0); |
||||
|
this.$router.push("/home/chat"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.el-container { |
||||
|
.l-friend-box { |
||||
|
border: #dddddd solid 1px; |
||||
|
background: #eeeeee; |
||||
|
|
||||
|
.el-header { |
||||
|
padding: 5px; |
||||
|
background-color: white; |
||||
|
line-height: 50px; |
||||
|
} |
||||
|
|
||||
|
.el-main { |
||||
|
padding: 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
.r-friend-box { |
||||
|
.user-detail { |
||||
|
width: 100%; |
||||
|
display: flex; |
||||
|
padding: 50px 10px 10px 50px; |
||||
|
text-align: center; |
||||
|
justify-content: space-around; |
||||
|
|
||||
|
.detail-head-image { |
||||
|
width: 200px; |
||||
|
height: 200px; |
||||
|
} |
||||
|
|
||||
|
.info-item { |
||||
|
width: 400px; |
||||
|
height: 200px; |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
.description { |
||||
|
padding: 20px 20px 0px 20px; |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.btn-group { |
||||
|
text-align: left !important; |
||||
|
padding-left: 100px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,155 @@ |
|||||
|
<template> |
||||
|
<el-container> |
||||
|
<el-aside width="80px" class="navi-bar"> |
||||
|
<div class="user-head-image" @click="onClickHeadImage"> |
||||
|
<head-image :src="$store.state.userStore.userInfo.headImage" > </head-image> |
||||
|
</div> |
||||
|
|
||||
|
<el-menu background-color="#333333" text-color="#ddd" style="margin-top: 30px;" > |
||||
|
<el-menu-item title="聊天"> |
||||
|
<router-link v-bind:to="'/home/chat'"> |
||||
|
<span class="el-icon-chat-dot-round"></span> |
||||
|
</router-link> |
||||
|
</el-menu-item > |
||||
|
<el-menu-item title="好友" > |
||||
|
<router-link v-bind:to="'/home/friends'"> |
||||
|
<span class="el-icon-user"></span> |
||||
|
</router-link> |
||||
|
</el-menu-item> |
||||
|
<el-menu-item title="设置" index="/group"> |
||||
|
<span class="el-icon-setting"></span> |
||||
|
</el-menu-item> |
||||
|
</el-menu> |
||||
|
<div class="exit-box" @click="onExit()" title="退出"> |
||||
|
<span class="el-icon-circle-close"></span> |
||||
|
</div> |
||||
|
</el-aside> |
||||
|
<el-main class="content-box"> |
||||
|
<router-view></router-view> |
||||
|
</el-main> |
||||
|
</el-container> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import HeadImage from '../components/HeadImage.vue'; |
||||
|
|
||||
|
export default { |
||||
|
components:{HeadImage}, |
||||
|
methods: { |
||||
|
init(userInfo){ |
||||
|
this.$store.commit("setUserInfo", userInfo); |
||||
|
this.$store.commit("initStore"); |
||||
|
console.log("socket"); |
||||
|
this.$wsApi.createWebSocket("ws://localhost:8878/im",this.$store); |
||||
|
this.$wsApi.onopen(()=>{ |
||||
|
this.pullUnreadMessage(); |
||||
|
}); |
||||
|
this.$wsApi.onmessage((e)=>{ |
||||
|
console.log(e); |
||||
|
if(e.cmd==1){ |
||||
|
// 插入私聊消息 |
||||
|
this.handleSingleMessage(e.data); |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
pullUnreadMessage(){ |
||||
|
this.$http({ |
||||
|
url: "/api/message/single/pullUnreadMessage", |
||||
|
method: 'post' |
||||
|
}) |
||||
|
}, |
||||
|
handleSingleMessage(msg){ |
||||
|
// 插入私聊消息 |
||||
|
let f = this.$store.state.friendsStore.friendsList.find((f)=>f.friendId==msg.sendUserId); |
||||
|
let chatInfo = { |
||||
|
type: 'single', |
||||
|
targetId: f.friendId, |
||||
|
showName: f.friendNickName, |
||||
|
headImage: f.friendHeadImage |
||||
|
}; |
||||
|
// 打开会话 |
||||
|
this.$store.commit("openChat",chatInfo); |
||||
|
// 插入消息 |
||||
|
this.$store.commit("insertMessage",msg); |
||||
|
}, |
||||
|
onExit(){ |
||||
|
this.$http({ |
||||
|
url: "/api/logout", |
||||
|
method: 'get' |
||||
|
}).then(()=>{ |
||||
|
this.$router.push("/login"); |
||||
|
}) |
||||
|
}, |
||||
|
onClickHeadImage(){ |
||||
|
this.$message.success(JSON.stringify(this.$store.state.userStore.userInfo)); |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.$http({ |
||||
|
url: "/api/user/self", |
||||
|
methods: 'get' |
||||
|
}).then((userInfo) => { |
||||
|
this.init(userInfo); |
||||
|
}) |
||||
|
}, |
||||
|
unmounted(){ |
||||
|
this.$wsApi.closeWebSocket(); |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.navi-bar { |
||||
|
background: #333333; |
||||
|
padding: 10px; |
||||
|
padding-top: 50px; |
||||
|
|
||||
|
.user-head-image{ |
||||
|
position: relative; |
||||
|
width: 50px; |
||||
|
height: 50px; |
||||
|
} |
||||
|
|
||||
|
.el-menu { |
||||
|
border: none; |
||||
|
flex: 1; |
||||
|
.el-menu-item { |
||||
|
margin-top: 20px; |
||||
|
.router-link-exact-active span{ |
||||
|
color: white !important; |
||||
|
} |
||||
|
|
||||
|
span { |
||||
|
font-size: 24px !important; |
||||
|
color: #aaaaaa; |
||||
|
|
||||
|
&:hover{ |
||||
|
color: white !important; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.exit-box { |
||||
|
position: absolute; |
||||
|
width: 60px; |
||||
|
bottom: 40px; |
||||
|
color: #aaaaaa; |
||||
|
font-size: 24px; |
||||
|
text-align: center; |
||||
|
cursor: pointer; |
||||
|
&:hover{ |
||||
|
color: white !important; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.content-box { |
||||
|
padding: 0; |
||||
|
background-color: #E9EEF3; |
||||
|
color: #333; |
||||
|
text-align: center; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
</style> |
||||
@ -0,0 +1,130 @@ |
|||||
|
<template> |
||||
|
<div class="login-view"> |
||||
|
|
||||
|
|
||||
|
|
||||
|
<el-form :model="loginForm" status-icon :rules="rules" ref="loginForm" label-width="60px" class="web-ruleForm"> |
||||
|
<div class="login-brand">欢迎登陆fly-chat</div> |
||||
|
<el-form-item label="用户名" prop="username"> |
||||
|
<el-input type="username" v-model="loginForm.username" autocomplete="off"></el-input> |
||||
|
|
||||
|
</el-form-item> |
||||
|
<el-form-item label="密码" prop="password"> |
||||
|
<el-input type="password" v-model="loginForm.password" autocomplete="off"></el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item> |
||||
|
<el-button type="primary" @click="submitForm('loginForm')">登陆</el-button> |
||||
|
<el-button @click="resetForm('loginForm')">清空</el-button> |
||||
|
</el-form-item> |
||||
|
<div class="register"> |
||||
|
<router-link to="/register">没有账号,前往注册</router-link> |
||||
|
</div> |
||||
|
|
||||
|
</el-form> |
||||
|
|
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: "login", |
||||
|
data() { |
||||
|
var checkUsername = (rule, value, callback) => { |
||||
|
console.log("checkUsername"); |
||||
|
if (!value) { |
||||
|
return callback(new Error('请输入用户名')); |
||||
|
} |
||||
|
callback(); |
||||
|
}; |
||||
|
var checkPassword = (rule, value, callback) => { |
||||
|
console.log("checkPassword"); |
||||
|
if (value === '') { |
||||
|
callback(new Error('请输入密码')); |
||||
|
} |
||||
|
callback(); |
||||
|
|
||||
|
}; |
||||
|
return { |
||||
|
loginForm: { |
||||
|
username: '', |
||||
|
password: '' |
||||
|
}, |
||||
|
rules: { |
||||
|
username: [{ |
||||
|
validator: checkUsername, |
||||
|
trigger: 'blur' |
||||
|
}], |
||||
|
password: [{ |
||||
|
validator: checkPassword, |
||||
|
trigger: 'blur' |
||||
|
}] |
||||
|
} |
||||
|
}; |
||||
|
}, |
||||
|
methods: { |
||||
|
submitForm(formName) { |
||||
|
this.$refs[formName].validate((valid) => { |
||||
|
if (valid) { |
||||
|
this.$http({ |
||||
|
url: "/api/login", |
||||
|
method: 'post', |
||||
|
params: this.loginForm |
||||
|
}) |
||||
|
.then((data) => { |
||||
|
this.$message.success("登陆成功"); |
||||
|
this.$router.push("/home/chat"); |
||||
|
}) |
||||
|
|
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
resetForm(formName) { |
||||
|
this.$refs[formName].resetFields(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.login-view { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
justify-content: space-around; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
background: linear-gradient(#65807a, #182e3c); |
||||
|
background-size: cover; |
||||
|
|
||||
|
|
||||
|
.web-ruleForm { |
||||
|
height: 340px; |
||||
|
padding: 20px; |
||||
|
margin-top: 150px ; |
||||
|
background: rgba(255,255,255,.75); |
||||
|
box-shadow: 0px 0px 1px #ccc; |
||||
|
border-radius: 5px; |
||||
|
overflow: hidden; |
||||
|
|
||||
|
|
||||
|
.login-brand { |
||||
|
line-height: 50px; |
||||
|
margin: 30px 0 40px 0; |
||||
|
font-size: 22px; |
||||
|
font-weight: 600; |
||||
|
letter-spacing: 2px; |
||||
|
text-transform: uppercase; |
||||
|
} |
||||
|
|
||||
|
.register { |
||||
|
display: flex; |
||||
|
flex-direction: row-reverse; |
||||
|
line-height: 40px; |
||||
|
text-align: left; |
||||
|
padding-left: 20px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
</style> |
||||
@ -0,0 +1,161 @@ |
|||||
|
<template> |
||||
|
<el-container class="register-view"> |
||||
|
<div> |
||||
|
|
||||
|
<el-form :model="registerForm" status-icon :rules="rules" ref="registerForm" label-width="80px" class="web-ruleForm"> |
||||
|
<div class="register-brand">欢迎注册成为FLY CHAT用户</div> |
||||
|
<el-form-item label="用户名" prop="userName"> |
||||
|
<el-input type="userName" v-model="registerForm.userName" autocomplete="off"></el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="昵称" prop="nickName"> |
||||
|
<el-input type="nickName" v-model="registerForm.nickName" autocomplete="off"></el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="密码" prop="password"> |
||||
|
<el-input type="password" v-model="registerForm.password" autocomplete="off"></el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="确认密码" prop="confirmPassword"> |
||||
|
<el-input type="password" v-model="registerForm.confirmPassword" autocomplete="off"></el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item> |
||||
|
<el-button type="primary" @click="submitForm('registerForm')">注册</el-button> |
||||
|
<el-button @click="resetForm('registerForm')">清空</el-button> |
||||
|
</el-form-item> |
||||
|
<div class="to-login"> |
||||
|
<router-link to="/login">已有账号,前往登录</router-link> |
||||
|
</div> |
||||
|
</el-form> |
||||
|
</div> |
||||
|
</el-container> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: "login", |
||||
|
data() { |
||||
|
var checkUserName = (rule, value, callback) => { |
||||
|
if (!value) { |
||||
|
return callback(new Error('请输入用户名')); |
||||
|
} |
||||
|
callback(); |
||||
|
}; |
||||
|
var checkNickName = (rule, value, callback) => { |
||||
|
if (!value) { |
||||
|
return callback(new Error('请输入昵称')); |
||||
|
} |
||||
|
callback(); |
||||
|
}; |
||||
|
var checkPassword = (rule, value, callback) => { |
||||
|
if (value === '') { |
||||
|
return callback(new Error('请输入密码')); |
||||
|
} |
||||
|
callback(); |
||||
|
}; |
||||
|
|
||||
|
var checkConfirmPassword = (rule, value, callback) => { |
||||
|
console.log("checkConfirmPassword"); |
||||
|
if (value === '') { |
||||
|
return callback(new Error('请输入密码')); |
||||
|
} |
||||
|
if (value != this.registerForm.password) { |
||||
|
return callback(new Error('两次密码输入不一致')); |
||||
|
} |
||||
|
callback(); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
return { |
||||
|
registerForm: { |
||||
|
userName: '', |
||||
|
nickName: '', |
||||
|
password: '', |
||||
|
confirmPassword: '' |
||||
|
}, |
||||
|
rules: { |
||||
|
userName: [{ |
||||
|
validator: checkUserName, |
||||
|
trigger: 'blur' |
||||
|
}], |
||||
|
nickName: [{ |
||||
|
validator: checkNickName, |
||||
|
trigger: 'blur' |
||||
|
}], |
||||
|
password: [{ |
||||
|
validator: checkPassword, |
||||
|
trigger: 'blur' |
||||
|
}], |
||||
|
confirmPassword: [{ |
||||
|
validator: checkConfirmPassword, |
||||
|
trigger: 'blur' |
||||
|
}] |
||||
|
} |
||||
|
}; |
||||
|
}, |
||||
|
methods: { |
||||
|
submitForm(formName) { |
||||
|
this.$refs[formName].validate((valid) => { |
||||
|
if (valid) { |
||||
|
this.$http({ |
||||
|
url: "/api/register", |
||||
|
method: 'post', |
||||
|
data: this.registerForm |
||||
|
}) |
||||
|
.then((data) => { |
||||
|
this.$message.success("注册成功!"); |
||||
|
}) |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
resetForm(formName) { |
||||
|
this.$refs[formName].resetFields(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.register-view { |
||||
|
position: fixed; |
||||
|
display: flex; |
||||
|
justify-content: space-around; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
background: #466368; |
||||
|
background: linear-gradient(#65807a, #182e3c); |
||||
|
background-size: cover; |
||||
|
-webkit-user-select: none; |
||||
|
background-size: cover; |
||||
|
|
||||
|
|
||||
|
.web-ruleForm { |
||||
|
width: 500px; |
||||
|
height: 430px; |
||||
|
padding: 20px; |
||||
|
margin-top: 100px ; |
||||
|
background: rgba(255,255,255,.75); |
||||
|
box-shadow: 0px 0px 1px #ccc; |
||||
|
border-radius: 3px; |
||||
|
overflow: hidden; |
||||
|
|
||||
|
.register-brand { |
||||
|
line-height: 50px; |
||||
|
margin: 20px 0 30px 0; |
||||
|
font-size: 22px; |
||||
|
font-weight: 600; |
||||
|
letter-spacing: 2px; |
||||
|
text-transform: uppercase; |
||||
|
} |
||||
|
|
||||
|
.to-login { |
||||
|
display: flex; |
||||
|
flex-direction: row-reverse; |
||||
|
line-height: 40px; |
||||
|
text-align: left; |
||||
|
padding-left: 20px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
</style> |
||||
@ -0,0 +1,16 @@ |
|||||
|
module.exports = { |
||||
|
|
||||
|
devServer: { |
||||
|
proxy: { |
||||
|
'/api': { |
||||
|
target: 'http://127.0.0.1:8888', |
||||
|
changeOrigin: true, |
||||
|
ws: false, |
||||
|
pathRewrite: { |
||||
|
'^/api': '' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
Loading…
Reference in new issue