@ -1,408 +1,430 @@ |
|||||
<script> |
<script> |
||||
import App from './App' |
import App from './App' |
||||
import http from './common/request'; |
import http from './common/request'; |
||||
import * as msgType from './common/messageType'; |
import * as msgType from './common/messageType'; |
||||
import * as enums from './common/enums'; |
import * as enums from './common/enums'; |
||||
import * as wsApi from './common/wssocket'; |
import * as wsApi from './common/wssocket'; |
||||
import UNI_APP from '@/.env.js' |
import UNI_APP from '@/.env.js' |
||||
|
|
||||
export default { |
export default { |
||||
data() { |
data() { |
||||
return { |
return { |
||||
isExit: false, // 是否已退出 |
isExit: false, // 是否已退出 |
||||
audioTip: null, |
audioTip: null, |
||||
reconnecting: false // 正在重连标志 |
reconnecting: false // 正在重连标志 |
||||
} |
} |
||||
|
}, |
||||
|
methods: { |
||||
|
init() { |
||||
|
this.isExit = false; |
||||
|
// 加载数据 |
||||
|
this.loadStore().then(() => { |
||||
|
// 初始化websocket |
||||
|
this.initWebSocket(); |
||||
|
}).catch((e) => { |
||||
|
console.log(e); |
||||
|
this.exit(); |
||||
|
}) |
||||
}, |
}, |
||||
methods: { |
initWebSocket() { |
||||
init() { |
let loginInfo = uni.getStorageSync("loginInfo") |
||||
this.isExit = false; |
wsApi.init(); |
||||
// 加载数据 |
wsApi.connect(UNI_APP.WS_URL, loginInfo.accessToken); |
||||
this.loadStore().then(() => { |
wsApi.onConnect(() => { |
||||
// 初始化websocket |
// 重连成功提示 |
||||
this.initWebSocket(); |
if (this.reconnecting) { |
||||
}).catch((e) => { |
this.reconnecting = false; |
||||
console.log(e); |
uni.showToast({ |
||||
this.exit(); |
title: "已重新连接", |
||||
}) |
icon: 'none' |
||||
}, |
|
||||
initWebSocket() { |
|
||||
let loginInfo = uni.getStorageSync("loginInfo") |
|
||||
wsApi.init(); |
|
||||
wsApi.connect(UNI_APP.WS_URL, loginInfo.accessToken); |
|
||||
wsApi.onConnect(() => { |
|
||||
// 重连成功提示 |
|
||||
if (this.reconnecting) { |
|
||||
this.reconnecting = false; |
|
||||
uni.showToast({ |
|
||||
title: "已重新连接", |
|
||||
icon: 'none' |
|
||||
}) |
|
||||
} |
|
||||
// 加载离线消息 |
|
||||
this.pullPrivateOfflineMessage(this.chatStore.privateMsgMaxId); |
|
||||
this.pullGroupOfflineMessage(this.chatStore.groupMsgMaxId); |
|
||||
}); |
|
||||
wsApi.onMessage((cmd, msgInfo) => { |
|
||||
if (cmd == 2) { |
|
||||
// 异地登录,强制下线 |
|
||||
uni.showModal({ |
|
||||
content: '您已在其他地方登陆,将被强制下线', |
|
||||
showCancel: false, |
|
||||
}) |
|
||||
this.exit(); |
|
||||
} else if (cmd == 3) { |
|
||||
// 私聊消息 |
|
||||
this.handlePrivateMessage(msgInfo); |
|
||||
} else if (cmd == 4) { |
|
||||
// 群聊消息 |
|
||||
this.handleGroupMessage(msgInfo); |
|
||||
} else if (cmd == 5) { |
|
||||
// 系统消息 |
|
||||
this.handleSystemMessage(msgInfo); |
|
||||
} |
|
||||
}); |
|
||||
wsApi.onClose((res) => { |
|
||||
console.log("ws断开", res); |
|
||||
// 重新连接 |
|
||||
this.reconnectWs(); |
|
||||
|
|
||||
}) |
|
||||
}, |
|
||||
loadStore() { |
|
||||
return this.userStore.loadUser().then(() => { |
|
||||
const promises = []; |
|
||||
promises.push(this.friendStore.loadFriend()); |
|
||||
promises.push(this.groupStore.loadGroup()); |
|
||||
promises.push(this.chatStore.loadChat()); |
|
||||
promises.push(this.configStore.loadConfig()); |
|
||||
return Promise.all(promises); |
|
||||
}) |
|
||||
}, |
|
||||
unloadStore(){ |
|
||||
this.friendStore.clear(); |
|
||||
this.groupStore.clear(); |
|
||||
this.chatStore.clear(); |
|
||||
this.configStore.clear(); |
|
||||
this.userStore.clear(); |
|
||||
}, |
|
||||
pullPrivateOfflineMessage(minId) { |
|
||||
this.chatStore.setLoadingPrivateMsg(true) |
|
||||
http({ |
|
||||
url: "/message/private/pullOfflineMessage?minId=" + minId, |
|
||||
method: 'GET' |
|
||||
}).catch(() => { |
|
||||
this.chatStore.setLoadingPrivateMsg(false) |
|
||||
}) |
|
||||
}, |
|
||||
pullGroupOfflineMessage(minId) { |
|
||||
this.chatStore.setLoadingGroupMsg(true) |
|
||||
http({ |
|
||||
url: "/message/group/pullOfflineMessage?minId=" + minId, |
|
||||
method: 'GET' |
|
||||
}).catch(() => { |
|
||||
this.chatStore.setLoadingGroupMsg(false) |
|
||||
}) |
|
||||
}, |
|
||||
handlePrivateMessage(msg) { |
|
||||
// 消息加载标志 |
|
||||
if (msg.type == enums.MESSAGE_TYPE.LOADING) { |
|
||||
this.chatStore.setLoadingPrivateMsg(JSON.parse(msg.content)) |
|
||||
return; |
|
||||
} |
|
||||
// 消息已读处理,清空已读数量 |
|
||||
if (msg.type == enums.MESSAGE_TYPE.READED) { |
|
||||
this.chatStore.resetUnreadCount({ |
|
||||
type: 'PRIVATE', |
|
||||
targetId: msg.recvId |
|
||||
}) |
|
||||
return; |
|
||||
} |
|
||||
// 消息回执处理,改消息状态为已读 |
|
||||
if (msg.type == enums.MESSAGE_TYPE.RECEIPT) { |
|
||||
this.chatStore.readedMessage({ |
|
||||
friendId: msg.sendId |
|
||||
}) |
}) |
||||
return; |
|
||||
} |
} |
||||
// 标记这条消息是不是自己发的 |
// 加载离线消息 |
||||
msg.selfSend = msg.sendId == this.userStore.userInfo.id; |
this.pullPrivateOfflineMessage(this.chatStore.privateMsgMaxId); |
||||
// 好友id |
this.pullGroupOfflineMessage(this.chatStore.groupMsgMaxId); |
||||
let friendId = msg.selfSend ? msg.recvId : msg.sendId; |
}); |
||||
this.loadFriendInfo(friendId, (friend) => { |
wsApi.onMessage((cmd, msgInfo) => { |
||||
this.insertPrivateMessage(friend, msg); |
if (cmd == 2) { |
||||
}) |
// 异地登录,强制下线 |
||||
}, |
|
||||
insertPrivateMessage(friend, msg) { |
|
||||
// 单人视频信令 |
|
||||
if (msgType.isRtcPrivate(msg.type)) { |
|
||||
// #ifdef MP-WEIXIN |
|
||||
// 小程序不支持音视频 |
|
||||
return; |
|
||||
// #endif |
|
||||
// 被呼叫,弹出视频页面 |
|
||||
let delayTime = 100; |
|
||||
if (msg.type == enums.MESSAGE_TYPE.RTC_CALL_VOICE || |
|
||||
msg.type == enums.MESSAGE_TYPE.RTC_CALL_VIDEO) { |
|
||||
let mode = msg.type == enums.MESSAGE_TYPE.RTC_CALL_VIDEO ? "video" : "voice"; |
|
||||
let pages = getCurrentPages(); |
|
||||
let curPage = pages[pages.length - 1].route; |
|
||||
if (curPage != "pages/chat/chat-private-video") { |
|
||||
const friendInfo = encodeURIComponent(JSON.stringify(friend)); |
|
||||
uni.navigateTo({ |
|
||||
url: `/pages/chat/chat-private-video?mode=${mode}&friend=${friendInfo}&isHost=false` |
|
||||
}) |
|
||||
delayTime = 500; |
|
||||
} |
|
||||
} |
|
||||
setTimeout(() => { |
|
||||
uni.$emit('WS_RTC_PRIVATE', msg); |
|
||||
}, delayTime) |
|
||||
return; |
|
||||
} |
|
||||
let chatInfo = { |
|
||||
type: 'PRIVATE', |
|
||||
targetId: friend.id, |
|
||||
showName: friend.nickName, |
|
||||
headImage: friend.headImage |
|
||||
}; |
|
||||
// 打开会话 |
|
||||
this.chatStore.openChat(chatInfo); |
|
||||
// 插入消息 |
|
||||
this.chatStore.insertMessage(msg); |
|
||||
// 播放提示音 |
|
||||
this.playAudioTip(); |
|
||||
|
|
||||
}, |
|
||||
handleGroupMessage(msg) { |
|
||||
// 消息加载标志 |
|
||||
if (msg.type == enums.MESSAGE_TYPE.LOADING) { |
|
||||
this.chatStore.setLoadingGroupMsg(JSON.parse(msg.content)) |
|
||||
return; |
|
||||
} |
|
||||
// 消息已读处理 |
|
||||
if (msg.type == enums.MESSAGE_TYPE.READED) { |
|
||||
// 我已读对方的消息,清空已读数量 |
|
||||
let chatInfo = { |
|
||||
type: 'GROUP', |
|
||||
targetId: msg.groupId |
|
||||
} |
|
||||
this.chatStore.resetUnreadCount(chatInfo) |
|
||||
return; |
|
||||
} |
|
||||
// 消息回执处理 |
|
||||
if (msg.type == enums.MESSAGE_TYPE.RECEIPT) { |
|
||||
// 更新消息已读人数 |
|
||||
let msgInfo = { |
|
||||
id: msg.id, |
|
||||
groupId: msg.groupId, |
|
||||
readedCount: msg.readedCount, |
|
||||
receiptOk: msg.receiptOk |
|
||||
}; |
|
||||
this.chatStore.updateMessage(msgInfo) |
|
||||
return; |
|
||||
} |
|
||||
// 标记这条消息是不是自己发的 |
|
||||
msg.selfSend = msg.sendId == this.userStore.userInfo.id; |
|
||||
this.loadGroupInfo(msg.groupId, (group) => { |
|
||||
// 插入群聊消息 |
|
||||
this.insertGroupMessage(group, msg); |
|
||||
}) |
|
||||
}, |
|
||||
handleSystemMessage(msg) { |
|
||||
if (msg.type == enums.MESSAGE_TYPE.USER_BANNED) { |
|
||||
// 用户被封禁 |
|
||||
wsApi.close(3099); |
|
||||
uni.showModal({ |
uni.showModal({ |
||||
content: '您的账号已被管理员封禁,原因:' + msg.content, |
content: '您已在其他地方登陆,将被强制下线', |
||||
showCancel: false, |
showCancel: false, |
||||
}) |
}) |
||||
this.exit(); |
this.exit(); |
||||
|
} else if (cmd == 3) { |
||||
|
// 私聊消息 |
||||
|
this.handlePrivateMessage(msgInfo); |
||||
|
} else if (cmd == 4) { |
||||
|
// 群聊消息 |
||||
|
this.handleGroupMessage(msgInfo); |
||||
|
} else if (cmd == 5) { |
||||
|
// 系统消息 |
||||
|
this.handleSystemMessage(msgInfo); |
||||
} |
} |
||||
}, |
}); |
||||
insertGroupMessage(group, msg) { |
wsApi.onClose((res) => { |
||||
// 群视频信令 |
console.log("ws断开", res); |
||||
if (msgType.isRtcGroup(msg.type)) { |
// 重新连接 |
||||
// #ifdef MP-WEIXIN |
this.reconnectWs(); |
||||
// 小程序不支持音视频 |
|
||||
return; |
}) |
||||
// #endif |
}, |
||||
// 被呼叫,弹出视频页面 |
loadStore() { |
||||
let delayTime = 100; |
return this.userStore.loadUser().then(() => { |
||||
if (msg.type == enums.MESSAGE_TYPE.RTC_GROUP_SETUP) { |
const promises = []; |
||||
let pages = getCurrentPages(); |
promises.push(this.friendStore.loadFriend()); |
||||
let curPage = pages[pages.length - 1].route; |
promises.push(this.groupStore.loadGroup()); |
||||
if (curPage != "pages/chat/chat-group-video") { |
promises.push(this.chatStore.loadChat()); |
||||
const userInfos = encodeURIComponent(msg.content); |
promises.push(this.configStore.loadConfig()); |
||||
const inviterId = msg.sendId; |
return Promise.all(promises); |
||||
const groupId = msg.groupId |
}) |
||||
uni.navigateTo({ |
}, |
||||
url: `/pages/chat/chat-group-video?groupId=${groupId}&isHost=false |
unloadStore() { |
||||
&inviterId=${inviterId}&userInfos=${userInfos}` |
this.friendStore.clear(); |
||||
}) |
this.groupStore.clear(); |
||||
delayTime = 500; |
this.chatStore.clear(); |
||||
} |
this.configStore.clear(); |
||||
|
this.userStore.clear(); |
||||
|
}, |
||||
|
pullPrivateOfflineMessage(minId) { |
||||
|
this.chatStore.setLoadingPrivateMsg(true) |
||||
|
http({ |
||||
|
url: "/message/private/pullOfflineMessage?minId=" + minId, |
||||
|
method: 'GET' |
||||
|
}).catch(() => { |
||||
|
this.chatStore.setLoadingPrivateMsg(false) |
||||
|
}) |
||||
|
}, |
||||
|
pullGroupOfflineMessage(minId) { |
||||
|
this.chatStore.setLoadingGroupMsg(true) |
||||
|
http({ |
||||
|
url: "/message/group/pullOfflineMessage?minId=" + minId, |
||||
|
method: 'GET' |
||||
|
}).catch(() => { |
||||
|
this.chatStore.setLoadingGroupMsg(false) |
||||
|
}) |
||||
|
}, |
||||
|
handlePrivateMessage(msg) { |
||||
|
// 消息加载标志 |
||||
|
if (msg.type == enums.MESSAGE_TYPE.LOADING) { |
||||
|
this.chatStore.setLoadingPrivateMsg(JSON.parse(msg.content)) |
||||
|
return; |
||||
|
} |
||||
|
// 消息已读处理,清空已读数量 |
||||
|
if (msg.type == enums.MESSAGE_TYPE.READED) { |
||||
|
this.chatStore.resetUnreadCount({ |
||||
|
type: 'PRIVATE', |
||||
|
targetId: msg.recvId |
||||
|
}) |
||||
|
return; |
||||
|
} |
||||
|
// 消息回执处理,改消息状态为已读 |
||||
|
if (msg.type == enums.MESSAGE_TYPE.RECEIPT) { |
||||
|
this.chatStore.readedMessage({ |
||||
|
friendId: msg.sendId |
||||
|
}) |
||||
|
return; |
||||
|
} |
||||
|
// 标记这条消息是不是自己发的 |
||||
|
msg.selfSend = msg.sendId == this.userStore.userInfo.id; |
||||
|
// 好友id |
||||
|
let friendId = msg.selfSend ? msg.recvId : msg.sendId; |
||||
|
this.loadFriendInfo(friendId, (friend) => { |
||||
|
this.insertPrivateMessage(friend, msg); |
||||
|
}) |
||||
|
}, |
||||
|
insertPrivateMessage(friend, msg) { |
||||
|
// 单人视频信令 |
||||
|
if (msgType.isRtcPrivate(msg.type)) { |
||||
|
// #ifdef MP-WEIXIN |
||||
|
// 小程序不支持音视频 |
||||
|
return; |
||||
|
// #endif |
||||
|
// 被呼叫,弹出视频页面 |
||||
|
let delayTime = 100; |
||||
|
if (msg.type == enums.MESSAGE_TYPE.RTC_CALL_VOICE || |
||||
|
msg.type == enums.MESSAGE_TYPE.RTC_CALL_VIDEO) { |
||||
|
let mode = msg.type == enums.MESSAGE_TYPE.RTC_CALL_VIDEO ? "video" : "voice"; |
||||
|
let pages = getCurrentPages(); |
||||
|
let curPage = pages[pages.length - 1].route; |
||||
|
if (curPage != "pages/chat/chat-private-video") { |
||||
|
const friendInfo = encodeURIComponent(JSON.stringify(friend)); |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/chat/chat-private-video?mode=${mode}&friend=${friendInfo}&isHost=false` |
||||
|
}) |
||||
|
delayTime = 500; |
||||
} |
} |
||||
// 消息转发到chat-group-video页面进行处理 |
|
||||
setTimeout(() => { |
|
||||
uni.$emit('WS_RTC_GROUP', msg); |
|
||||
}, delayTime) |
|
||||
return; |
|
||||
} |
} |
||||
|
setTimeout(() => { |
||||
|
uni.$emit('WS_RTC_PRIVATE', msg); |
||||
|
}, delayTime) |
||||
|
return; |
||||
|
} |
||||
|
let chatInfo = { |
||||
|
type: 'PRIVATE', |
||||
|
targetId: friend.id, |
||||
|
showName: friend.nickName, |
||||
|
headImage: friend.headImage |
||||
|
}; |
||||
|
// 打开会话 |
||||
|
this.chatStore.openChat(chatInfo); |
||||
|
// 插入消息 |
||||
|
this.chatStore.insertMessage(msg); |
||||
|
// 播放提示音 |
||||
|
this.playAudioTip(); |
||||
|
|
||||
|
}, |
||||
|
handleGroupMessage(msg) { |
||||
|
// 消息加载标志 |
||||
|
if (msg.type == enums.MESSAGE_TYPE.LOADING) { |
||||
|
this.chatStore.setLoadingGroupMsg(JSON.parse(msg.content)) |
||||
|
return; |
||||
|
} |
||||
|
// 消息已读处理 |
||||
|
if (msg.type == enums.MESSAGE_TYPE.READED) { |
||||
|
// 我已读对方的消息,清空已读数量 |
||||
let chatInfo = { |
let chatInfo = { |
||||
type: 'GROUP', |
type: 'GROUP', |
||||
targetId: group.id, |
targetId: msg.groupId |
||||
showName: group.showGroupName, |
|
||||
headImage: group.headImageThumb |
|
||||
}; |
|
||||
// 打开会话 |
|
||||
this.chatStore.openChat(chatInfo); |
|
||||
// 插入消息 |
|
||||
this.chatStore.insertMessage(msg); |
|
||||
// 播放提示音 |
|
||||
this.playAudioTip(); |
|
||||
}, |
|
||||
loadFriendInfo(id, callback) { |
|
||||
let friend = this.friendStore.findFriend(id); |
|
||||
if (friend) { |
|
||||
callback(friend); |
|
||||
} else { |
|
||||
http({ |
|
||||
url: `/friend/find/${id}`, |
|
||||
method: 'GET' |
|
||||
}).then((friend) => { |
|
||||
this.friendStore.addFriend(friend); |
|
||||
callback(friend) |
|
||||
}) |
|
||||
} |
|
||||
}, |
|
||||
loadGroupInfo(id, callback) { |
|
||||
let group = this.groupStore.findGroup(id); |
|
||||
if (group) { |
|
||||
callback(group); |
|
||||
} else { |
|
||||
http({ |
|
||||
url: `/group/find/${id}`, |
|
||||
method: 'GET' |
|
||||
}).then((group) => { |
|
||||
this.groupStore.addGroup(group); |
|
||||
callback(group) |
|
||||
}) |
|
||||
} |
} |
||||
}, |
this.chatStore.resetUnreadCount(chatInfo) |
||||
exit() { |
return; |
||||
console.log("exit"); |
} |
||||
this.isExit = true; |
// 消息回执处理 |
||||
|
if (msg.type == enums.MESSAGE_TYPE.RECEIPT) { |
||||
|
// 更新消息已读人数 |
||||
|
let msgInfo = { |
||||
|
id: msg.id, |
||||
|
groupId: msg.groupId, |
||||
|
readedCount: msg.readedCount, |
||||
|
receiptOk: msg.receiptOk |
||||
|
}; |
||||
|
this.chatStore.updateMessage(msgInfo) |
||||
|
return; |
||||
|
} |
||||
|
// 标记这条消息是不是自己发的 |
||||
|
msg.selfSend = msg.sendId == this.userStore.userInfo.id; |
||||
|
this.loadGroupInfo(msg.groupId, (group) => { |
||||
|
// 插入群聊消息 |
||||
|
this.insertGroupMessage(group, msg); |
||||
|
}) |
||||
|
}, |
||||
|
handleSystemMessage(msg) { |
||||
|
if (msg.type == enums.MESSAGE_TYPE.USER_BANNED) { |
||||
|
// 用户被封禁 |
||||
wsApi.close(3099); |
wsApi.close(3099); |
||||
uni.removeStorageSync("loginInfo"); |
uni.showModal({ |
||||
uni.reLaunch({ |
content: '您的账号已被管理员封禁,原因:' + msg.content, |
||||
url: "/pages/login/login" |
showCancel: false, |
||||
}) |
}) |
||||
this.unloadStore(); |
this.exit(); |
||||
}, |
} |
||||
playAudioTip() { |
}, |
||||
// 音频播放无法成功 |
insertGroupMessage(group, msg) { |
||||
// this.audioTip = uni.createInnerAudioContext(); |
// 群视频信令 |
||||
// this.audioTip.src = "/static/audio/tip.wav"; |
if (msgType.isRtcGroup(msg.type)) { |
||||
// this.audioTip.play(); |
// #ifdef MP-WEIXIN |
||||
}, |
// 小程序不支持音视频 |
||||
refreshToken(loginInfo) { |
return; |
||||
return new Promise((resolve, reject) => { |
// #endif |
||||
if (!loginInfo || !loginInfo.refreshToken) { |
// 被呼叫,弹出视频页面 |
||||
reject(); |
let delayTime = 100; |
||||
return; |
if (msg.type == enums.MESSAGE_TYPE.RTC_GROUP_SETUP) { |
||||
|
let pages = getCurrentPages(); |
||||
|
let curPage = pages[pages.length - 1].route; |
||||
|
if (curPage != "pages/chat/chat-group-video") { |
||||
|
const userInfos = encodeURIComponent(msg.content); |
||||
|
const inviterId = msg.sendId; |
||||
|
const groupId = msg.groupId |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/chat/chat-group-video?groupId=${groupId}&isHost=false |
||||
|
&inviterId=${inviterId}&userInfos=${userInfos}` |
||||
|
}) |
||||
|
delayTime = 500; |
||||
} |
} |
||||
http({ |
|
||||
url: '/refreshToken', |
|
||||
method: 'PUT', |
|
||||
header: { |
|
||||
refreshToken: loginInfo.refreshToken |
|
||||
} |
|
||||
}).then((newLoginInfo) => { |
|
||||
uni.setStorageSync("loginInfo", newLoginInfo) |
|
||||
resolve() |
|
||||
}).catch((e) => { |
|
||||
reject(e) |
|
||||
}) |
|
||||
}) |
|
||||
}, |
|
||||
reconnectWs() { |
|
||||
// 已退出则不再重连 |
|
||||
if (this.isExit) { |
|
||||
return; |
|
||||
} |
} |
||||
// 记录标志 |
// 消息转发到chat-group-video页面进行处理 |
||||
this.reconnecting = true; |
setTimeout(() => { |
||||
// 重新加载一次个人信息,目的是为了保证网络已经正常且token有效 |
uni.$emit('WS_RTC_GROUP', msg); |
||||
this.reloadUserInfo().then((userInfo) => { |
}, delayTime) |
||||
uni.showToast({ |
return; |
||||
title: '连接已断开,尝试重新连接...', |
} |
||||
icon: 'none', |
|
||||
}) |
let chatInfo = { |
||||
this.userStore.setUserInfo(userInfo); |
type: 'GROUP', |
||||
// 重新连接 |
targetId: group.id, |
||||
let loginInfo = uni.getStorageSync("loginInfo") |
showName: group.showGroupName, |
||||
wsApi.reconnect(UNI_APP.WS_URL, loginInfo.accessToken); |
headImage: group.headImageThumb |
||||
}).catch(() => { |
}; |
||||
// 5s后重试 |
// 打开会话 |
||||
setTimeout(() => { |
this.chatStore.openChat(chatInfo); |
||||
this.reconnectWs(); |
// 插入消息 |
||||
}, 5000) |
this.chatStore.insertMessage(msg); |
||||
|
// 播放提示音 |
||||
|
this.playAudioTip(); |
||||
|
}, |
||||
|
loadFriendInfo(id, callback) { |
||||
|
let friend = this.friendStore.findFriend(id); |
||||
|
if (friend) { |
||||
|
callback(friend); |
||||
|
} else { |
||||
|
http({ |
||||
|
url: `/friend/find/${id}`, |
||||
|
method: 'GET' |
||||
|
}).then((friend) => { |
||||
|
this.friendStore.addFriend(friend); |
||||
|
callback(friend) |
||||
}) |
}) |
||||
}, |
} |
||||
reloadUserInfo() { |
}, |
||||
return http({ |
loadGroupInfo(id, callback) { |
||||
url: '/user/self', |
let group = this.groupStore.findGroup(id); |
||||
|
if (group) { |
||||
|
callback(group); |
||||
|
} else { |
||||
|
http({ |
||||
|
url: `/group/find/${id}`, |
||||
method: 'GET' |
method: 'GET' |
||||
|
}).then((group) => { |
||||
|
this.groupStore.addGroup(group); |
||||
|
callback(group) |
||||
}) |
}) |
||||
} |
} |
||||
}, |
}, |
||||
onLaunch() { |
exit() { |
||||
this.$mountStore(); |
console.log("exit"); |
||||
// 登录状态校验 |
this.isExit = true; |
||||
let loginInfo = uni.getStorageSync("loginInfo") |
wsApi.close(3099); |
||||
this.refreshToken(loginInfo).then(() => { |
uni.removeStorageSync("loginInfo"); |
||||
// 初始化 |
uni.reLaunch({ |
||||
this.init(); |
url: "/pages/login/login" |
||||
// 跳转到聊天页面 |
}) |
||||
uni.switchTab({ |
this.unloadStore(); |
||||
url: "/pages/chat/chat" |
}, |
||||
|
playAudioTip() { |
||||
|
// 音频播放无法成功 |
||||
|
// this.audioTip = uni.createInnerAudioContext(); |
||||
|
// this.audioTip.src = "/static/audio/tip.wav"; |
||||
|
// this.audioTip.play(); |
||||
|
}, |
||||
|
refreshToken(loginInfo) { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
if (!loginInfo || !loginInfo.refreshToken) { |
||||
|
reject(); |
||||
|
return; |
||||
|
} |
||||
|
http({ |
||||
|
url: '/refreshToken', |
||||
|
method: 'PUT', |
||||
|
header: { |
||||
|
refreshToken: loginInfo.refreshToken |
||||
|
} |
||||
|
}).then((newLoginInfo) => { |
||||
|
uni.setStorageSync("loginInfo", newLoginInfo) |
||||
|
resolve() |
||||
|
}).catch((e) => { |
||||
|
reject(e) |
||||
}) |
}) |
||||
}).catch(() => { |
}) |
||||
// 跳转到登录页 |
}, |
||||
// #ifdef H5 |
reconnectWs() { |
||||
uni.navigateTo({ |
// 已退出则不再重连 |
||||
url: "/pages/login/login" |
if (this.isExit) { |
||||
|
return; |
||||
|
} |
||||
|
// 记录标志 |
||||
|
this.reconnecting = true; |
||||
|
// 重新加载一次个人信息,目的是为了保证网络已经正常且token有效 |
||||
|
this.reloadUserInfo().then((userInfo) => { |
||||
|
uni.showToast({ |
||||
|
title: '连接已断开,尝试重新连接...', |
||||
|
icon: 'none', |
||||
}) |
}) |
||||
// #endif |
this.userStore.setUserInfo(userInfo); |
||||
|
// 重新连接 |
||||
|
let loginInfo = uni.getStorageSync("loginInfo") |
||||
|
wsApi.reconnect(UNI_APP.WS_URL, loginInfo.accessToken); |
||||
|
}).catch(() => { |
||||
|
// 5s后重试 |
||||
|
setTimeout(() => { |
||||
|
this.reconnectWs(); |
||||
|
}, 5000) |
||||
|
}) |
||||
|
}, |
||||
|
reloadUserInfo() { |
||||
|
return http({ |
||||
|
url: '/user/self', |
||||
|
method: 'GET' |
||||
}) |
}) |
||||
} |
} |
||||
|
}, |
||||
|
onLaunch() { |
||||
|
this.$mountStore(); |
||||
|
// 登录状态校验 |
||||
|
let loginInfo = uni.getStorageSync("loginInfo") |
||||
|
this.refreshToken(loginInfo).then(() => { |
||||
|
// 初始化 |
||||
|
this.init(); |
||||
|
// 跳转到聊天页面 |
||||
|
uni.switchTab({ |
||||
|
url: "/pages/chat/chat" |
||||
|
}) |
||||
|
}).catch(() => { |
||||
|
// 跳转到登录页 |
||||
|
// #ifdef H5 |
||||
|
uni.navigateTo({ |
||||
|
url: "/pages/login/login" |
||||
|
}) |
||||
|
// #endif |
||||
|
}) |
||||
} |
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style lang="scss"> |
<style lang="scss"> |
||||
@import "@/uni_modules/uview-plus/index.scss"; |
@import "@/uni_modules/uview-plus/index.scss"; |
||||
@import url('./static/icon/iconfont.css'); |
@import "@/im.scss"; |
||||
|
@import url('./static/icon/iconfont.css'); |
||||
|
|
||||
// #ifdef H5 |
// #ifdef H5 |
||||
uni-page-head { |
uni-page-head { |
||||
display: none; // h5浏览器本身就有标题 |
display: none; // h5浏览器本身就有标题 |
||||
} |
} |
||||
|
|
||||
|
// #endif |
||||
|
|
||||
|
.tab-page { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
// #ifdef H5 |
||||
|
height: calc(100vh - 50px - $im-nav-bar-height); // h5平台100vh是包含了底部高度,需要减去 |
||||
|
top: $im-nav-bar-height; |
||||
// #endif |
// #endif |
||||
|
|
||||
.tab-page { |
// #ifndef H5 |
||||
// #ifdef H5 |
height: calc(100vh - var(--status-bar-height) - $im-nav-bar-height); // app平台还要减去顶部手机状态栏高度 |
||||
height: calc(100vh - 50px); // h5平台100vh是包含了底部高度,需要减去 |
top: calc($im-nav-bar-height + var(--status-bar-height)); |
||||
// #endif |
// #endif |
||||
// #ifndef H5 |
color: $im-text-color; |
||||
height: calc(100vh); |
background-color: $im-bg; |
||||
// #endif |
font-size: $im-font-size; |
||||
background-color: #f8f8f8; |
font-family: $font-family; |
||||
} |
} |
||||
|
|
||||
.page { |
.page { |
||||
height: calc(100vh); |
position: relative; |
||||
background-color: #f8f8f8; |
// #ifdef H5 |
||||
} |
height: calc(100vh - $im-nav-bar-height); // app平台还要减去顶部手机状态栏高度 |
||||
|
top: $im-nav-bar-height; |
||||
|
// #endif |
||||
|
// #ifndef H5 |
||||
|
height: calc(100vh - var(--status-bar-height) - $im-nav-bar-height); // app平台还要减去顶部手机状态栏高度 |
||||
|
top: calc($im-nav-bar-height + var(--status-bar-height)); |
||||
|
// #endif |
||||
|
color: $im-text-color; |
||||
|
background-color: $im-bg; |
||||
|
font-size: $im-font-size; |
||||
|
font-family: $font-family; |
||||
|
} |
||||
</style> |
</style> |
||||
@ -0,0 +1,40 @@ |
|||||
|
<template> |
||||
|
<view class="arrow-bar"> |
||||
|
<text class="title">{{ title }}</text> |
||||
|
<uni-icons class="arrow" type="right" size="16"></uni-icons> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: "arrow-bar", |
||||
|
props: { |
||||
|
title: { |
||||
|
type: String, |
||||
|
required: true |
||||
|
} |
||||
|
}, |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.arrow-bar { |
||||
|
width: 100%; |
||||
|
height: 90rpx; |
||||
|
font-size: 30rpx; |
||||
|
color: black; |
||||
|
margin-top: 5rpx; |
||||
|
background-color: white; |
||||
|
line-height: 90rpx; |
||||
|
display: flex; |
||||
|
|
||||
|
.title { |
||||
|
flex: 1; |
||||
|
margin-left: 40rpx; |
||||
|
} |
||||
|
|
||||
|
.arrow { |
||||
|
margin-right: 40rpx; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,17 @@ |
|||||
|
<template> |
||||
|
<view class="bar-group"> |
||||
|
<slot></slot> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: "bar-group" |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.bar-group { |
||||
|
margin: 20rpx 0; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,68 @@ |
|||||
|
<template> |
||||
|
<view class="btn-bar" :style="style"> |
||||
|
<text v-if="icon" class="icon iconfont" :class="icon"></text> |
||||
|
<text class="title">{{ title }}</text> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: "btn-bar", |
||||
|
props: { |
||||
|
title: { |
||||
|
type: String, |
||||
|
required: true |
||||
|
}, |
||||
|
icon: { |
||||
|
type: String, |
||||
|
required: false |
||||
|
}, |
||||
|
type: { |
||||
|
type: String, |
||||
|
default: "normal" |
||||
|
}, |
||||
|
color: { |
||||
|
type: String, |
||||
|
default: "#000" |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
style() { |
||||
|
let color = "#000"; |
||||
|
switch (this.type) { |
||||
|
case 'danger': |
||||
|
color = "#f14747"; |
||||
|
break; |
||||
|
case 'primary': |
||||
|
color = "#35567f"; |
||||
|
break; |
||||
|
} |
||||
|
return `color: ${color};` |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.btn-bar { |
||||
|
width: 100%; |
||||
|
height: 100rpx; |
||||
|
margin-top: 5rpx; |
||||
|
background-color: white; |
||||
|
line-height: 100rpx; |
||||
|
text-align: center; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
|
||||
|
.icon { |
||||
|
font-size: 40rpx; |
||||
|
font-weight: 600; |
||||
|
margin-right: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.title { |
||||
|
font-size: 32rpx; |
||||
|
font-weight: 600; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,61 @@ |
|||||
|
<template> |
||||
|
<view class="switch-bar"> |
||||
|
<text class="title">{{ title }}</text> |
||||
|
<switch class="switch" :checked="checked" color="#18bc37" @change="onChange"></switch> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: "switch-bar", |
||||
|
props: { |
||||
|
title: { |
||||
|
type: String, |
||||
|
required: true |
||||
|
}, |
||||
|
checked: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
value: this.checked |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
onChange(e) { |
||||
|
this.value = true; |
||||
|
setTimeout(() => { |
||||
|
this.value = false; |
||||
|
}, 100) |
||||
|
//this.value = false; |
||||
|
|
||||
|
this.$emit('change', e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.switch-bar { |
||||
|
width: 100%; |
||||
|
height: 100rpx; |
||||
|
font-size: 34rpx; |
||||
|
color: black; |
||||
|
margin-top: 5rpx; |
||||
|
background-color: white; |
||||
|
line-height: 100rpx; |
||||
|
display: flex; |
||||
|
|
||||
|
.title { |
||||
|
flex: 1; |
||||
|
margin-left: 40rpx; |
||||
|
} |
||||
|
|
||||
|
.switch { |
||||
|
margin-right: 40rpx; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -1,184 +1,182 @@ |
|||||
<template> |
<template> |
||||
<view class="chat-item" :class="active?'active':''"> |
<view class="chat-item" :class="active ? 'active' : ''"> |
||||
<!--rich-text中的表情包会屏蔽事件,所以这里用一个遮罩层捕获点击事件 --> |
<!--rich-text中的表情包会屏蔽事件,所以这里用一个遮罩层捕获点击事件 --> |
||||
<view class="mask" @tap="showChatBox()"></view> |
<view class="mask" @tap="showChatBox()"></view> |
||||
<view class="left"> |
<view class="left"> |
||||
<head-image :url="chat.headImage" :name="chat.showName" :size="90"></head-image> |
<head-image :url="chat.headImage" :name="chat.showName"></head-image> |
||||
</view> |
</view> |
||||
<view class="chat-right"> |
<view class="chat-right"> |
||||
<view class="chat-name"> |
<view class="chat-name"> |
||||
<view class="chat-name-text"> |
<view class="chat-name-text"> |
||||
<view>{{chat.showName}}</view> |
<view>{{ chat.showName }}</view> |
||||
<uni-tag v-if="chat.type=='GROUP'" circle text="群" size="small"></uni-tag> |
<uni-tag v-if="chat.type == 'GROUP'" circle text="群" size="small" type="primary"></uni-tag> |
||||
</view> |
</view> |
||||
<view class="chat-time">{{$date.toTimeText(chat.lastSendTime,true)}}</view> |
<view class="chat-time">{{ $date.toTimeText(chat.lastSendTime, true) }}</view> |
||||
</view> |
</view> |
||||
<view class="chat-content"> |
<view class="chat-content"> |
||||
<view class="chat-at-text">{{atText}}</view> |
<view class="chat-at-text">{{ atText }}</view> |
||||
<view class="chat-send-name" v-if="isShowSendName">{{chat.sendNickName+': '}}</view> |
<view class="chat-send-name" v-if="isShowSendName">{{ chat.sendNickName + ': ' }}</view> |
||||
<rich-text class="chat-content-text" :nodes="$emo.transform(chat.lastContent)"></rich-text> |
<rich-text class="chat-content-text" :nodes="$emo.transform(chat.lastContent)"></rich-text> |
||||
<uni-badge v-if="chat.unreadCount>0" size="small" :max-num="99" :text="chat.unreadCount" /> |
<uni-badge v-if="chat.unreadCount > 0" :max-num="99" :text="chat.unreadCount" /> |
||||
</view> |
</view> |
||||
</view> |
</view> |
||||
</view> |
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
export default { |
export default { |
||||
name: "chatItem", |
name: "chatItem", |
||||
data() { |
data() { |
||||
return {} |
return {} |
||||
|
}, |
||||
|
props: { |
||||
|
chat: { |
||||
|
type: Object |
||||
}, |
}, |
||||
props: { |
index: { |
||||
chat: { |
type: Number |
||||
type: Object |
|
||||
}, |
|
||||
index: { |
|
||||
type: Number |
|
||||
}, |
|
||||
active: { |
|
||||
type: Boolean, |
|
||||
default: false |
|
||||
} |
|
||||
}, |
}, |
||||
methods: { |
active: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
|
||||
showChatBox() { |
showChatBox() { |
||||
uni.navigateTo({ |
uni.navigateTo({ |
||||
url: "/pages/chat/chat-box?chatIdx=" + this.index |
url: "/pages/chat/chat-box?chatIdx=" + this.index |
||||
}) |
}) |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
isShowSendName() { |
||||
|
if (!this.chat.sendNickName) { |
||||
|
return false; |
||||
} |
} |
||||
|
let size = this.chat.messages.length; |
||||
|
if (size == 0) { |
||||
|
return false; |
||||
|
} |
||||
|
// 只有群聊的普通消息需要显示名称 |
||||
|
let lastMsg = this.chat.messages[size - 1]; |
||||
|
return this.$msgType.isNormal(lastMsg.type) |
||||
}, |
}, |
||||
computed: { |
atText() { |
||||
isShowSendName() { |
if (this.chat.atMe) { |
||||
if (!this.chat.sendNickName) { |
return "[有人@我]" |
||||
return false; |
} else if (this.chat.atAll) { |
||||
} |
return "[@全体成员]" |
||||
let size = this.chat.messages.length; |
|
||||
if (size == 0) { |
|
||||
return false; |
|
||||
} |
|
||||
// 只有群聊的普通消息需要显示名称 |
|
||||
let lastMsg = this.chat.messages[size - 1]; |
|
||||
return this.$msgType.isNormal(lastMsg.type) |
|
||||
}, |
|
||||
atText() { |
|
||||
if (this.chat.atMe) { |
|
||||
return "[有人@我]" |
|
||||
} else if (this.chat.atAll) { |
|
||||
return "[@全体成员]" |
|
||||
} |
|
||||
return ""; |
|
||||
} |
} |
||||
|
return ""; |
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style scoped lang="scss"> |
<style scoped lang="scss"> |
||||
.chat-item { |
.chat-item { |
||||
height: 100rpx; |
height: 96rpx; |
||||
display: flex; |
display: flex; |
||||
margin-bottom: 2rpx; |
margin-bottom: 2rpx; |
||||
position: relative; |
position: relative; |
||||
padding: 10rpx 20rpx; |
padding: 18rpx 20rpx; |
||||
align-items: center; |
align-items: center; |
||||
background-color: white; |
background-color: white; |
||||
white-space: nowrap; |
white-space: nowrap; |
||||
|
|
||||
|
&:hover { |
||||
|
background-color: $im-bg-active; |
||||
|
} |
||||
|
|
||||
&:hover { |
&.active { |
||||
background-color: #f5f6ff; |
background-color: $im-bg-active; |
||||
} |
} |
||||
|
|
||||
&.active { |
.mask { |
||||
background-color: #f5f6ff; |
position: absolute; |
||||
} |
width: 100%; |
||||
|
height: 100%; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 99; |
||||
|
} |
||||
|
|
||||
|
.left { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
width: 100rpx; |
||||
|
height: 100rpx; |
||||
|
} |
||||
|
|
||||
.mask { |
.chat-right { |
||||
position: absolute; |
height: 100%; |
||||
width: 100%; |
flex: 1; |
||||
height: 100%; |
display: flex; |
||||
z-index: 99; |
flex-direction: column; |
||||
} |
justify-content: center; |
||||
|
padding-left: 20rpx; |
||||
|
text-align: left; |
||||
|
overflow: hidden; |
||||
|
|
||||
.left { |
.chat-name { |
||||
position: relative; |
|
||||
display: flex; |
display: flex; |
||||
justify-content: center; |
|
||||
align-items: center; |
|
||||
width: 100rpx; |
|
||||
height: 100rpx; |
|
||||
|
|
||||
|
.chat-name-text { |
||||
|
flex: 1; |
||||
|
font-size: $im-font-size-large; |
||||
|
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
|
||||
|
.uni-tag { |
||||
|
text-align: center; |
||||
|
margin-left: 5rpx; |
||||
|
border: 0; |
||||
|
padding: 1px 5px; |
||||
|
//opacity: 0.8; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.chat-time { |
||||
|
font-size: $im-font-size-smaller-extra; |
||||
|
color: $im-text-color-lighter; |
||||
|
text-align: right; |
||||
|
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
|
} |
||||
} |
} |
||||
|
|
||||
.chat-right { |
.chat-content { |
||||
flex: 1; |
|
||||
display: flex; |
display: flex; |
||||
flex-direction: column; |
font-size: $im-font-size-smaller; |
||||
padding-left: 20rpx; |
color: $im-text-color-lighter; |
||||
text-align: left; |
padding-top: 8rpx; |
||||
overflow: hidden; |
|
||||
|
|
||||
.chat-name { |
.chat-at-text { |
||||
display: flex; |
color: $im-color-danger; |
||||
line-height: 44rpx; |
|
||||
height: 44rpx; |
|
||||
|
|
||||
.chat-name-text { |
|
||||
flex: 1; |
|
||||
font-size: 30rpx; |
|
||||
font-weight: 600; |
|
||||
white-space: nowrap; |
|
||||
overflow: hidden; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
|
|
||||
.uni-tag { |
|
||||
text-align: center; |
|
||||
margin-left: 5rpx; |
|
||||
border: 0; |
|
||||
height: 30rpx; |
|
||||
line-height: 30rpx; |
|
||||
font-size: 20rpx; |
|
||||
padding: 1px 5px; |
|
||||
background-color: #de1c1c; |
|
||||
opacity: 0.8; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.chat-time { |
|
||||
font-size: 26rpx; |
|
||||
text-align: right; |
|
||||
color: #888888; |
|
||||
white-space: nowrap; |
|
||||
overflow: hidden; |
|
||||
} |
|
||||
} |
} |
||||
|
|
||||
.chat-content { |
.chat-send-name { |
||||
display: flex; |
font-size: $im-font-size-smaller; |
||||
line-height: 60rpx; |
} |
||||
height: 60rpx; |
|
||||
|
|
||||
.chat-at-text { |
|
||||
color: #c70b0b; |
|
||||
font-size: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.chat-send-name { |
.chat-content-text { |
||||
font-size: 26rpx; |
flex: 1; |
||||
} |
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
|
||||
.chat-content-text { |
img { |
||||
flex: 1; |
width: 40rpx !important; |
||||
font-size: 28rpx; |
height: 40rpx !important; |
||||
white-space: nowrap; |
|
||||
overflow: hidden; |
|
||||
text-overflow: ellipsis; |
|
||||
} |
} |
||||
|
|
||||
} |
} |
||||
|
|
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
</style> |
</style> |
||||
@ -1,79 +1,75 @@ |
|||||
<template> |
<template> |
||||
<view class="friend-item" @click="showFriendInfo()"> |
<view class="friend-item" @click="showFriendInfo()"> |
||||
<head-image :name="friend.nickName" :online="friend.online" :url="friend.headImage" |
<head-image :name="friend.nickName" :online="friend.online" :url="friend.headImage" size="small"></head-image> |
||||
:size="90"></head-image> |
|
||||
<view class="friend-info"> |
<view class="friend-info"> |
||||
<view class="friend-name">{{ friend.nickName}}</view> |
<view class="friend-name">{{ friend.nickName }}</view> |
||||
<view class="friend-online"> |
<view class="friend-online"> |
||||
<image v-show="friend.onlineWeb" class="online" src="/static/image/online_web.png" |
<image v-show="friend.onlineWeb" class="online" src="/static/image/online_web.png" title="电脑设备在线" /> |
||||
title="电脑设备在线" /> |
<image v-show="friend.onlineApp" class="online" src="/static/image/online_app.png" title="移动设备在线" /> |
||||
<image v-show="friend.onlineApp" class="online" src="/static/image/online_app.png" |
|
||||
title="移动设备在线" /> |
|
||||
</view> |
</view> |
||||
</view> |
</view> |
||||
</view> |
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
export default { |
export default { |
||||
name: "frined-item", |
name: "frined-item", |
||||
data() { |
data() { |
||||
return {} |
return {} |
||||
|
}, |
||||
|
methods: { |
||||
|
showFriendInfo() { |
||||
|
uni.navigateTo({ |
||||
|
url: "/pages/common/user-info?id=" + this.friend.id |
||||
|
}) |
||||
}, |
}, |
||||
methods: { |
}, |
||||
showFriendInfo() { |
props: { |
||||
uni.navigateTo({ |
friend: { |
||||
url: "/pages/common/user-info?id=" + this.friend.id |
type: Object |
||||
}) |
|
||||
}, |
|
||||
}, |
|
||||
props: { |
|
||||
friend: { |
|
||||
type: Object |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style scope lang="scss"> |
<style scope lang="scss"> |
||||
.friend-item { |
.friend-item { |
||||
height: 100rpx; |
height: 90rpx; |
||||
|
display: flex; |
||||
|
margin-bottom: 1rpx; |
||||
|
position: relative; |
||||
|
padding: 10rpx; |
||||
|
padding-left: 20rpx; |
||||
|
align-items: center; |
||||
|
background-color: white; |
||||
|
white-space: nowrap; |
||||
|
|
||||
|
&:hover { |
||||
|
background-color: $im-bg; |
||||
|
} |
||||
|
|
||||
|
.friend-info { |
||||
|
flex: 1; |
||||
display: flex; |
display: flex; |
||||
margin-bottom: 1rpx; |
flex-direction: column; |
||||
position: relative; |
|
||||
padding: 10rpx; |
|
||||
padding-left: 20rpx; |
padding-left: 20rpx; |
||||
align-items: center; |
text-align: left; |
||||
background-color: white; |
|
||||
white-space: nowrap; |
|
||||
|
|
||||
&:hover { |
.friend-name { |
||||
background-color: #f5f6ff; |
font-size: $im-font-size; |
||||
|
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
} |
} |
||||
|
|
||||
.friend-info { |
.friend-online { |
||||
flex: 1; |
margin-top: 4rpx; |
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
padding-left: 20rpx; |
|
||||
text-align: left; |
|
||||
|
|
||||
.friend-name { |
.online { |
||||
font-size: 30rpx; |
padding-right: 4rpx; |
||||
font-weight: 600; |
width: 24rpx; |
||||
line-height: 60rpx; |
height: 24rpx; |
||||
white-space: nowrap; |
|
||||
overflow: hidden; |
|
||||
} |
|
||||
|
|
||||
.friend-online { |
|
||||
.online { |
|
||||
padding-right: 4rpx; |
|
||||
width: 32rpx; |
|
||||
height: 32rpx; |
|
||||
|
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
</style> |
} |
||||
|
</style> |
||||
|
|||||
@ -1,56 +1,58 @@ |
|||||
<template> |
<template> |
||||
<view class="group-item" @click="showGroupInfo()"> |
<view class="group-item" @click="showGroupInfo()"> |
||||
<head-image :name="group.showGroupName" |
<head-image :name="group.showGroupName" :url="group.headImage" size="small"></head-image> |
||||
:url="group.headImage" :size="90"></head-image> |
|
||||
<view class="group-name"> |
<view class="group-name"> |
||||
<view>{{ group.showGroupName}}</view> |
<view>{{ group.showGroupName }}</view> |
||||
</view> |
</view> |
||||
</view> |
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
export default { |
export default { |
||||
name: "group-item", |
name: "group-item", |
||||
data() { |
data() { |
||||
return {} |
return {} |
||||
|
}, |
||||
|
methods: { |
||||
|
showGroupInfo() { |
||||
|
uni.navigateTo({ |
||||
|
url: "/pages/group/group-info?id=" + this.group.id |
||||
|
}) |
||||
}, |
}, |
||||
methods:{ |
}, |
||||
showGroupInfo(){ |
props: { |
||||
uni.navigateTo({ |
group: { |
||||
url:"/pages/group/group-info?id="+this.group.id |
type: Object |
||||
}) |
|
||||
}, |
|
||||
}, |
|
||||
props: { |
|
||||
group: { |
|
||||
type: Object |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style scope lang="scss"> |
<style scope lang="scss"> |
||||
.group-item { |
.group-item { |
||||
height: 100rpx; |
height: 90rpx; |
||||
display: flex; |
display: flex; |
||||
margin-bottom: 1rpx; |
margin-bottom: 2rpx; |
||||
position: relative; |
position: relative; |
||||
padding: 10rpx; |
padding: 18rpx 20rpx; |
||||
|
align-items: center; |
||||
|
background-color: white; |
||||
|
white-space: nowrap; |
||||
|
|
||||
|
&:hover { |
||||
|
background-color: $im-bg-active; |
||||
|
} |
||||
|
|
||||
|
&.active { |
||||
|
background-color: $im-bg-active; |
||||
|
} |
||||
|
|
||||
|
.group-name { |
||||
|
font-size: $im-font-size; |
||||
padding-left: 20rpx; |
padding-left: 20rpx; |
||||
align-items: center; |
text-align: left; |
||||
background-color: white; |
|
||||
white-space: nowrap; |
white-space: nowrap; |
||||
&:hover { |
overflow: hidden; |
||||
background-color: #f5f6ff; |
|
||||
} |
|
||||
|
|
||||
.group-name { |
|
||||
font-size: 32rpx; |
|
||||
padding-left: 20rpx; |
|
||||
font-weight: 600; |
|
||||
text-align: left; |
|
||||
white-space: nowrap; |
|
||||
overflow: hidden; |
|
||||
} |
|
||||
} |
} |
||||
|
} |
||||
</style> |
</style> |
||||
|
|||||
@ -0,0 +1,121 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
<view @longpress.stop="onLongPress($event)" @touchmove="onTouchMove" @touchend="onTouchEnd"> |
||||
|
<slot></slot> |
||||
|
</view> |
||||
|
<view v-if="isShowMenu" class="menu-mask" @touchstart="onClose()" @contextmenu.prevent=""></view> |
||||
|
<view v-if="isShowMenu" class="menu" :style="menuStyle"> |
||||
|
<view class="menu-item" v-for="(item) in items" :key="item.key" @click.prevent="onSelectMenu(item)"> |
||||
|
<text :style="itemStyle(item)"> {{ item.name }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: "long-press-menu", |
||||
|
data() { |
||||
|
return { |
||||
|
isShowMenu: false, |
||||
|
isTouchMove: false, |
||||
|
style: "" |
||||
|
} |
||||
|
}, |
||||
|
props: { |
||||
|
items: { |
||||
|
type: Array |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
onLongPress(e) { |
||||
|
if (this.isTouchMove) { |
||||
|
// 屏幕移动时不弹出 |
||||
|
return; |
||||
|
} |
||||
|
uni.getSystemInfo({ |
||||
|
success: (res) => { |
||||
|
let touches = e.touches[0]; |
||||
|
let style = ""; |
||||
|
/* 因 非H5端不兼容 style 属性绑定 Object ,所以拼接字符 */ |
||||
|
if (touches.clientY > (res.windowHeight / 2)) { |
||||
|
style = `bottom:${res.windowHeight - touches.clientY}px;`; |
||||
|
} else { |
||||
|
style = `top:${touches.clientY}px;`; |
||||
|
} |
||||
|
if (touches.clientX > (res.windowWidth / 2)) { |
||||
|
style += `right:${res.windowWidth - touches.clientX}px;`; |
||||
|
} else { |
||||
|
style += `left:${touches.clientX}px;`; |
||||
|
} |
||||
|
this.menuStyle = style; |
||||
|
// |
||||
|
this.$nextTick(() => { |
||||
|
this.isShowMenu = true; |
||||
|
}); |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
onTouchMove() { |
||||
|
this.onClose(); |
||||
|
this.isTouchMove = true; |
||||
|
}, |
||||
|
onTouchEnd() { |
||||
|
this.isTouchMove = false; |
||||
|
}, |
||||
|
onSelectMenu(item) { |
||||
|
this.$emit("select", item); |
||||
|
this.isShowMenu = false; |
||||
|
}, |
||||
|
onClose() { |
||||
|
this.isShowMenu = false; |
||||
|
}, |
||||
|
itemStyle(item) { |
||||
|
if (item.color) { |
||||
|
return `color:${item.color};` |
||||
|
} |
||||
|
return `color:#000;`; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.menu-mask { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
top: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
z-index: 999; |
||||
|
} |
||||
|
|
||||
|
.menu { |
||||
|
position: fixed; |
||||
|
border-radius: 4px; |
||||
|
overflow: hidden; |
||||
|
background-color: #fff; |
||||
|
z-index: 1000; |
||||
|
box-shadow: $im-box-shadow-dark; |
||||
|
|
||||
|
.menu-item { |
||||
|
height: 28px; |
||||
|
min-width: 120rpx; |
||||
|
line-height: 28px; |
||||
|
font-size: $im-font-size-small; |
||||
|
display: flex; |
||||
|
padding: 6px 20px; |
||||
|
justify-content: flex-start; |
||||
|
|
||||
|
&:hover { |
||||
|
background: $im-bg-active; |
||||
|
} |
||||
|
|
||||
|
.menu-icon { |
||||
|
margin-right: 10rpx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,119 @@ |
|||||
|
<template> |
||||
|
<view class="im-nav-bar"> |
||||
|
<!-- #ifndef H5 --> |
||||
|
<view style="height: var(--status-bar-height)"></view> |
||||
|
<!-- #endif --> |
||||
|
<view class="im-nav-bar-content"> |
||||
|
<view class="back" @click="handleBackClick" v-if="back"> |
||||
|
<uni-icons type="back" :size="iconFontSize"></uni-icons> |
||||
|
</view> |
||||
|
<view class="title" v-if="title"> |
||||
|
<slot></slot> |
||||
|
</view> |
||||
|
<view class="btn"> |
||||
|
<uni-icons class="btn-item" v-if="search" type="search" :size="iconFontSize" |
||||
|
@click="$emit('search')"></uni-icons> |
||||
|
<uni-icons class="btn-item" v-if="add" type="plusempty" :size="iconFontSize" @click="$emit('add')"></uni-icons> |
||||
|
<uni-icons class="btn-item" v-if="more" type="more-filled" :size="iconFontSize" |
||||
|
@click="$emit('more')"></uni-icons> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: "nav-bar", |
||||
|
props: { |
||||
|
back: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
title: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
}, |
||||
|
search: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
add: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
more: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
iconFontSize: { |
||||
|
type: Number, |
||||
|
default: 24 |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return {} |
||||
|
}, |
||||
|
computed: { |
||||
|
height() { |
||||
|
|
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
handleBackClick() { |
||||
|
uni.navigateBack({ |
||||
|
delta: 1 |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.im-nav-bar { |
||||
|
background-color: #fff; |
||||
|
//background-color: $im-bg; |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
width: 100%; |
||||
|
color: $im-text-color; |
||||
|
border-bottom: 1px solid $im-border-light; |
||||
|
font-size: $im-font-size-large; |
||||
|
z-index: 99; |
||||
|
|
||||
|
.im-nav-bar-content { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
box-sizing: border-box; |
||||
|
height: $im-nav-bar-height; |
||||
|
|
||||
|
.title {} |
||||
|
|
||||
|
.back { |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
height: 100%; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
padding: 12px; |
||||
|
font-size: 22px; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
|
||||
|
.btn { |
||||
|
position: absolute; |
||||
|
right: 0; |
||||
|
height: 100%; |
||||
|
display: flex; |
||||
|
padding: 12px; |
||||
|
align-items: center; |
||||
|
box-sizing: border-box; |
||||
|
|
||||
|
.btn-item { |
||||
|
margin-left: 8px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
</style> |
||||
@ -1,121 +0,0 @@ |
|||||
<template> |
|
||||
<view> |
|
||||
<view @longpress.stop="onLongPress($event)" @touchmove="onTouchMove" @touchend="onTouchEnd"> |
|
||||
<slot></slot> |
|
||||
</view> |
|
||||
<view v-if="isShowMenu" class="pop-menu" @touchstart="onClose()" @contextmenu.prevent=""></view> |
|
||||
<view v-if="isShowMenu" class="menu" :style="menuStyle"> |
|
||||
<view class="menu-item" v-for="(item) in items" :key="item.key" @click.prevent="onSelectMenu(item)"> |
|
||||
<uni-icons class="menu-icon" :type="item.icon" :style="itemStyle(item)" size="22"></uni-icons> |
|
||||
<text :style="itemStyle(item)"> {{item.name}}</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
export default { |
|
||||
name: "pop-menu", |
|
||||
data() { |
|
||||
return { |
|
||||
isShowMenu : false, |
|
||||
isTouchMove: false, |
|
||||
style : "" |
|
||||
} |
|
||||
}, |
|
||||
props: { |
|
||||
items: { |
|
||||
type: Array |
|
||||
} |
|
||||
}, |
|
||||
methods: { |
|
||||
onLongPress(e){ |
|
||||
if(this.isTouchMove){ |
|
||||
// 屏幕移动时不弹出 |
|
||||
return; |
|
||||
} |
|
||||
uni.getSystemInfo({ |
|
||||
success: (res) => { |
|
||||
let touches = e.touches[0]; |
|
||||
let style = ""; |
|
||||
/* 因 非H5端不兼容 style 属性绑定 Object ,所以拼接字符 */ |
|
||||
if (touches.clientY > (res.windowHeight / 2)) { |
|
||||
style = `bottom:${res.windowHeight-touches.clientY}px;`; |
|
||||
} else { |
|
||||
style = `top:${touches.clientY}px;`; |
|
||||
} |
|
||||
if (touches.clientX > (res.windowWidth / 2)) { |
|
||||
style += `right:${res.windowWidth-touches.clientX}px;`; |
|
||||
} else { |
|
||||
style += `left:${touches.clientX}px;`; |
|
||||
} |
|
||||
this.menuStyle = style; |
|
||||
// |
|
||||
this.$nextTick(() => { |
|
||||
this.isShowMenu = true; |
|
||||
}); |
|
||||
} |
|
||||
}) |
|
||||
}, |
|
||||
onTouchMove(){ |
|
||||
this.onClose(); |
|
||||
this.isTouchMove = true; |
|
||||
}, |
|
||||
onTouchEnd(){ |
|
||||
this.isTouchMove = false; |
|
||||
}, |
|
||||
onSelectMenu(item) { |
|
||||
this.$emit("select", item); |
|
||||
this.isShowMenu = false; |
|
||||
}, |
|
||||
onClose() { |
|
||||
this.isShowMenu = false; |
|
||||
}, |
|
||||
itemStyle(item){ |
|
||||
if(item.color){ |
|
||||
return `color:${item.color};` |
|
||||
} |
|
||||
return `color:#4f76e6;`; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style lang="scss" scoped> |
|
||||
.pop-menu { |
|
||||
position: fixed; |
|
||||
left: 0; |
|
||||
top: 0; |
|
||||
right: 0; |
|
||||
bottom: 0; |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
background-color: #333; |
|
||||
z-index: 999; |
|
||||
opacity: 0.5; |
|
||||
|
|
||||
} |
|
||||
|
|
||||
.menu { |
|
||||
position: fixed; |
|
||||
border: 1px solid #b4b4b4; |
|
||||
border-radius: 7px; |
|
||||
overflow: hidden; |
|
||||
background-color: #f5f6ff; |
|
||||
z-index: 1000; |
|
||||
.menu-item { |
|
||||
height: 25px; |
|
||||
min-width: 150rpx; |
|
||||
line-height: 25px; |
|
||||
font-size: 18px; |
|
||||
display: flex; |
|
||||
padding: 10px; |
|
||||
justify-content: center; |
|
||||
border-bottom: 1px solid #d0d0d8; |
|
||||
|
|
||||
.menu-icon { |
|
||||
margin-right: 10rpx; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</style> |
|
||||
@ -0,0 +1,60 @@ |
|||||
|
// 颜色 |
||||
|
$im-color-primary: #3e45d7; |
||||
|
$im-color-primary-light-1: mix(#fff, $im-color-primary, 10%); |
||||
|
$im-color-primary-light-2: mix(#fff, $im-color-primary, 20%); |
||||
|
$im-color-primary-light-3: mix(#fff, $im-color-primary, 30%); |
||||
|
$im-color-primary-light-4: mix(#fff, $im-color-primary, 40%); |
||||
|
$im-color-primary-light-5: mix(#fff, $im-color-primary, 50%); |
||||
|
$im-color-primary-light-6: mix(#fff, $im-color-primary, 60%); |
||||
|
$im-color-primary-light-7: mix(#fff, $im-color-primary, 70%); |
||||
|
$im-color-primary-light-8: mix(#fff, $im-color-primary, 80%); |
||||
|
$im-color-primary-light-9: mix(#fff, $im-color-primary, 90%); |
||||
|
$im-color-primary-dark-1: mix(#000, $im-color-primary, 10%); |
||||
|
$im-color-primary-dark-2: mix(#000, $im-color-primary, 20%); |
||||
|
$im-color-primary-dark-3: mix(#000, $im-color-primary, 30%); |
||||
|
$im-color-primary-dark-4: mix(#000, $im-color-primary, 40%); |
||||
|
|
||||
|
$im-color-success: #18bc37; |
||||
|
$im-color-warning: #f3a73f; |
||||
|
$im-color-danger: #e43d33; |
||||
|
$im-color-info: #8f939c; |
||||
|
|
||||
|
// 文字颜色 |
||||
|
$im-text-color: #000000; |
||||
|
$im-text-color-light: #6a6a6a; |
||||
|
$im-text-color-lighter: #909399; |
||||
|
$im-text-color-lighter-extra: #c7c7c7; |
||||
|
|
||||
|
// 边框颜色 |
||||
|
$im-border: #F0F0F0; |
||||
|
$im-border-light: #EDEDED; |
||||
|
$im-border-lighter: #DCDCDC; |
||||
|
$im-border-lighter-extra: #B9B9B9; |
||||
|
|
||||
|
// 文字大小 |
||||
|
$im-font-size: 30rpx; |
||||
|
$im-font-size-small: 28rpx; |
||||
|
$im-font-size-smaller: 26rpx; |
||||
|
$im-font-size-smaller-extra: 24rpx; |
||||
|
$im-font-size-large: 32rpx; |
||||
|
$im-font-size-larger: 34rpx; |
||||
|
$im-font-size-larger-extra: 36rpx; |
||||
|
|
||||
|
// 阴影 |
||||
|
$im-box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04); |
||||
|
$im-box-shadow-light: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
||||
|
$im-box-shadow-lighter: 0px 0px 6px rgba(0, 0, 0, .12); |
||||
|
$im-box-shadow-dark: 0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5); |
||||
|
|
||||
|
// 背景 |
||||
|
$im-bg: #f7f7f7; |
||||
|
$im-bg-active: #f1f1f1; |
||||
|
|
||||
|
// 标题 |
||||
|
$im-title-size: 26px; |
||||
|
$im-title-size-1: 22px; |
||||
|
$im-title-size-2: 18px; |
||||
|
|
||||
|
$font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, SimSun, sans-serif; |
||||
|
|
||||
|
$im-nav-bar-height: 50px; |
||||
@ -0,0 +1,141 @@ |
|||||
|
/** 原生button样式 **/ |
||||
|
uni-button { |
||||
|
font-size: $im-font-size !important; |
||||
|
} |
||||
|
uni-button[type='primary'] { |
||||
|
color: #fff !important; |
||||
|
background-color: $im-color-primary !important; |
||||
|
} |
||||
|
|
||||
|
uni-button[type='primary'][plain] { |
||||
|
color: $im-color-primary !important; |
||||
|
border: 1px solid $im-color-primary; |
||||
|
background-color: transparent; |
||||
|
} |
||||
|
|
||||
|
uni-button[type='warn'] { |
||||
|
color: #fff !important; |
||||
|
background-color: $im-color-danger !important; |
||||
|
} |
||||
|
|
||||
|
uni-button[type='warn'][plain] { |
||||
|
color: $im-color-danger !important; |
||||
|
border: 1px solid $im-color-danger !important; |
||||
|
background-color: transparent !important; |
||||
|
} |
||||
|
|
||||
|
uni-button[size='mini'] { |
||||
|
font-size: $im-font-size-smaller !important; |
||||
|
} |
||||
|
|
||||
|
.button-hover[type='primary'] { |
||||
|
color: #fff !important; |
||||
|
background-color: $im-color-primary-dark-1 !important; |
||||
|
} |
||||
|
|
||||
|
/** uni-ui input激活后边框、图标颜色 **/ |
||||
|
.uni-easyinput__content.is-focused:not(.is-input-error-border) { |
||||
|
border-color: $im-color-primary-light-2 !important; |
||||
|
|
||||
|
.content-clear-icon { |
||||
|
color: $im-color-primary-light-2 !important; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** 底部导航 **/ |
||||
|
.uni-tabbar-bottom .uni-tabbar { |
||||
|
box-shadow: $im-box-shadow; |
||||
|
} |
||||
|
|
||||
|
.uni-tabbar-border { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
.segmented-control { |
||||
|
border-color: $im-color-primary !important; |
||||
|
|
||||
|
.segmented-control__item--button { |
||||
|
border-color: $im-color-primary !important; |
||||
|
} |
||||
|
.segmented-control__item--button--active { |
||||
|
background-color: $im-color-primary !important; |
||||
|
|
||||
|
.segmented-control__text{ |
||||
|
color: #fff !important; |
||||
|
} |
||||
|
} |
||||
|
.segmented-control__text{ |
||||
|
color: $im-color-primary !important; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.uni-radio-input { |
||||
|
//border-color: $im-color-primary !important; |
||||
|
//background-color: $im-color-primary !important; |
||||
|
} |
||||
|
|
||||
|
.uni-section__content-title { |
||||
|
font-size: $im-font-size !important; |
||||
|
color: $im-text-color-light; |
||||
|
} |
||||
|
|
||||
|
.uni-forms-item__label { |
||||
|
color: $im-text-color; |
||||
|
font-size: $im-font-size !important; |
||||
|
} |
||||
|
.uni-forms-item { |
||||
|
//margin-bottom: 8px !important; |
||||
|
} |
||||
|
.uni-easyinput__content-input { |
||||
|
font-size: $im-font-size !important; |
||||
|
} |
||||
|
.uni-easyinput__placeholder-class { |
||||
|
color: $im-text-color-lighter; |
||||
|
font-size: $im-font-size !important;; |
||||
|
} |
||||
|
.uni-easyinput__content-textarea { |
||||
|
font-size: $im-font-size !important;; |
||||
|
} |
||||
|
.uni-input-input:disabled { |
||||
|
color: $im-text-color-light; |
||||
|
} |
||||
|
.uni-forms-item.is-direction-top .uni-forms-item__label { |
||||
|
padding: 0 !important; |
||||
|
} |
||||
|
.uni-data-checklist .checklist-group .checklist-box .checklist-content .checklist-text { |
||||
|
font-size: $im-font-size !important; |
||||
|
} |
||||
|
.uni-card .uni-card__content { |
||||
|
color: unset !important; |
||||
|
padding: 10px 0 !important; |
||||
|
} |
||||
|
.uni-tag-text--small{ |
||||
|
font-size: 10px !important; |
||||
|
font-weight: bolder !important; |
||||
|
} |
||||
|
|
||||
|
.nav-bar { |
||||
|
height: 100rpx; |
||||
|
padding: 0 20rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
background-color: white; |
||||
|
border-bottom: 1px solid $im-border; |
||||
|
|
||||
|
.nav-search { |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.nav-add { |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
.bottom-btn { |
||||
|
margin: 40rpx 40rpx; |
||||
|
|
||||
|
uni-button + uni-button { |
||||
|
margin-top: 20rpx; |
||||
|
} |
||||
|
} |
||||
@ -1,174 +1,154 @@ |
|||||
<template> |
<template> |
||||
<view class="tab-page"> |
<view class="tab-page"> |
||||
|
<nav-bar search @search="showSearch = !showSearch">消息</nav-bar> |
||||
<view v-if="loading" class="chat-loading"> |
<view v-if="loading" class="chat-loading"> |
||||
<loading :size="50" :mask="false"> |
<loading :size="50" :mask="false"> |
||||
<view>消息接收中...</view> |
<view>消息接收中...</view> |
||||
</loading> |
</loading> |
||||
</view> |
</view> |
||||
<view class="nav-bar"> |
<view class="nav-bar" v-if="showSearch"> |
||||
<view class="nav-search"> |
<view class="nav-search"> |
||||
<uni-search-bar radius="100" v-model="searchText" cancelButton="none" placeholder="搜索"></uni-search-bar> |
<uni-search-bar radius="100" v-model="searchText" cancelButton="none" placeholder="搜索"></uni-search-bar> |
||||
</view> |
</view> |
||||
</view> |
</view> |
||||
<view class="chat-tip" v-if="!loading && chatStore.chats.length==0"> |
<view class="chat-tip" v-if="!loading && chatStore.chats.length == 0"> |
||||
温馨提示:您现在还没有任何聊天消息,快跟您的好友发起聊天吧~ |
温馨提示:您现在还没有任何聊天消息,快跟您的好友发起聊天吧~ |
||||
</view> |
</view> |
||||
<scroll-view class="scroll-bar" v-else scroll-with-animation="true" scroll-y="true"> |
<scroll-view class="scroll-bar" v-else scroll-with-animation="true" scroll-y="true"> |
||||
<view v-for="(chat,index) in chatStore.chats" :key="index"> |
<view v-for="(chat, index) in chatStore.chats" :key="index"> |
||||
<pop-menu v-if="isShowChat(chat)" :items="menu.items" |
<long-press-menu v-if="isShowChat(chat)" :items="menu.items" @select="onSelectMenu($event, index)"> |
||||
@select="onSelectMenu($event,index)"> |
<chat-item :chat="chat" :index="index" :active="menu.chatIdx == index"></chat-item> |
||||
<chat-item :chat="chat" :index="index" |
</long-press-menu> |
||||
:active="menu.chatIdx==index"></chat-item> |
|
||||
</pop-menu> |
|
||||
</view> |
</view> |
||||
</scroll-view> |
</scroll-view> |
||||
</view> |
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
import useChatStore from '@/store/chatStore.js' |
|
||||
|
export default { |
||||
export default { |
data() { |
||||
data() { |
return { |
||||
return { |
showSearch: false, |
||||
searchText: "", |
searchText: "", |
||||
menu: { |
menu: { |
||||
show: false, |
show: false, |
||||
style: "", |
style: "", |
||||
chatIdx: -1, |
chatIdx: -1, |
||||
isTouchMove: false, |
isTouchMove: false, |
||||
items: [{ |
items: [{ |
||||
key: 'DELETE', |
key: 'DELETE', |
||||
name: '删除该聊天', |
name: '删除该聊天', |
||||
icon: 'trash', |
icon: 'trash', |
||||
color: '#e64e4e' |
color: '#e64e4e' |
||||
}, |
}, |
||||
{ |
{ |
||||
key: 'TOP', |
key: 'TOP', |
||||
name: '置顶该聊天', |
name: '置顶该聊天', |
||||
icon: 'arrow-up' |
icon: 'arrow-up' |
||||
} |
|
||||
] |
|
||||
} |
} |
||||
|
] |
||||
} |
} |
||||
}, |
} |
||||
methods: { |
}, |
||||
onSelectMenu(item,chatIdx) { |
methods: { |
||||
switch (item.key) { |
onSelectMenu(item, chatIdx) { |
||||
case 'DELETE': |
switch (item.key) { |
||||
this.removeChat(chatIdx); |
case 'DELETE': |
||||
break; |
this.removeChat(chatIdx); |
||||
case 'TOP': |
break; |
||||
this.moveToTop(chatIdx); |
case 'TOP': |
||||
break; |
this.moveToTop(chatIdx); |
||||
default: |
break; |
||||
break; |
default: |
||||
} |
break; |
||||
this.menu.show = false; |
|
||||
}, |
|
||||
removeChat(chatIdx) { |
|
||||
this.chatStore.removeChat(chatIdx); |
|
||||
}, |
|
||||
moveToTop(chatIdx) { |
|
||||
this.chatStore.moveTop(chatIdx); |
|
||||
}, |
|
||||
isShowChat(chat){ |
|
||||
if(chat.delete){ |
|
||||
return false; |
|
||||
} |
|
||||
return !this.searchText || chat.showName.includes(this.searchText) |
|
||||
}, |
|
||||
refreshUnreadBadge() { |
|
||||
if (this.unreadCount > 0) { |
|
||||
uni.setTabBarBadge({ |
|
||||
index: 0, |
|
||||
text: this.unreadCount + "" |
|
||||
}) |
|
||||
} else { |
|
||||
uni.removeTabBarBadge({ |
|
||||
index: 0, |
|
||||
complete: () => {} |
|
||||
}) |
|
||||
} |
|
||||
} |
} |
||||
|
this.menu.show = false; |
||||
}, |
}, |
||||
computed: { |
removeChat(chatIdx) { |
||||
unreadCount() { |
this.chatStore.removeChat(chatIdx); |
||||
let count = 0; |
}, |
||||
this.chatStore.chats.forEach(chat => { |
moveToTop(chatIdx) { |
||||
if (!chat.delete) { |
this.chatStore.moveTop(chatIdx); |
||||
count += chat.unreadCount; |
}, |
||||
} |
isShowChat(chat) { |
||||
}) |
if (chat.delete) { |
||||
return count; |
return false; |
||||
}, |
|
||||
loading() { |
|
||||
return this.chatStore.loadingGroupMsg || this.chatStore.loadingPrivateMsg |
|
||||
} |
} |
||||
|
return !this.searchText || chat.showName.includes(this.searchText) |
||||
}, |
}, |
||||
watch: { |
refreshUnreadBadge() { |
||||
unreadCount(newCount, oldCount) { |
if (this.unreadCount > 0) { |
||||
this.refreshUnreadBadge(); |
uni.setTabBarBadge({ |
||||
|
index: 0, |
||||
|
text: this.unreadCount + "" |
||||
|
}) |
||||
|
} else { |
||||
|
uni.removeTabBarBadge({ |
||||
|
index: 0, |
||||
|
complete: () => { } |
||||
|
}) |
||||
} |
} |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
unreadCount() { |
||||
|
let count = 0; |
||||
|
this.chatStore.chats.forEach(chat => { |
||||
|
if (!chat.delete) { |
||||
|
count += chat.unreadCount; |
||||
|
} |
||||
|
}) |
||||
|
return count; |
||||
}, |
}, |
||||
onShow() { |
loading() { |
||||
|
return this.chatStore.loadingGroupMsg || this.chatStore.loadingPrivateMsg |
||||
|
} |
||||
|
}, |
||||
|
watch: { |
||||
|
unreadCount(newCount, oldCount) { |
||||
this.refreshUnreadBadge(); |
this.refreshUnreadBadge(); |
||||
} |
} |
||||
|
}, |
||||
|
onShow() { |
||||
|
this.refreshUnreadBadge(); |
||||
} |
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style scoped lang="scss"> |
<style scoped lang="scss"> |
||||
.tab-page { |
.tab-page { |
||||
position: relative; |
position: relative; |
||||
border: #dddddd solid 1px; |
display: flex; |
||||
display: flex; |
flex-direction: column; |
||||
flex-direction: column; |
|
||||
|
|
||||
|
|
||||
.nav-bar { |
.chat-tip { |
||||
padding: 2rpx 20rpx; |
position: absolute; |
||||
display: flex; |
top: 400rpx; |
||||
align-items: center; |
padding: 50rpx; |
||||
background-color: white; |
line-height: 50rpx; |
||||
border-bottom: 1px solid #ddd; |
text-align: center; |
||||
height: 110rpx; |
color: $im-text-color-lighter; |
||||
.nav-search { |
} |
||||
flex: 1; |
|
||||
height: 110rpx; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
} |
|
||||
|
|
||||
.chat-tip { |
|
||||
position: absolute; |
|
||||
top: 400rpx; |
|
||||
padding: 50rpx; |
|
||||
line-height: 50rpx; |
|
||||
text-align: left; |
|
||||
color: darkblue; |
|
||||
font-size: 30rpx; |
|
||||
} |
|
||||
|
|
||||
.chat-loading { |
|
||||
display: block; |
|
||||
width: 100%; |
|
||||
height: 120rpx; |
|
||||
background: white; |
|
||||
position: fixed; |
|
||||
top: 0; |
|
||||
z-index: 999; |
|
||||
color: blue; |
|
||||
|
|
||||
.loading-box { |
.chat-loading { |
||||
position: relative; |
display: block; |
||||
|
width: 100%; |
||||
|
height: 120rpx; |
||||
|
background: white; |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
z-index: 999; |
||||
|
color: $im-text-color-lighter; |
||||
|
|
||||
} |
.loading-box { |
||||
|
position: relative; |
||||
} |
} |
||||
|
} |
||||
|
|
||||
.scroll-bar { |
.scroll-bar { |
||||
flex: 1; |
flex: 1; |
||||
height: 100%; |
height: 100%; |
||||
} |
|
||||
} |
} |
||||
|
} |
||||
</style> |
</style> |
||||
@ -1,192 +1,218 @@ |
|||||
<template> |
<template> |
||||
<view class="page user-info"> |
<view class="page user-info"> |
||||
<view class="content"> |
<nav-bar back>用户信息</nav-bar> |
||||
<head-image :name="userInfo.nickName" :url="userInfo.headImageThumb" |
<uni-card :is-shadow="false" is-full :border="false"> |
||||
:size="160" @click="onShowFullImage()"></head-image> |
<view class="content"> |
||||
|
<head-image :name="userInfo.nickName" :url="userInfo.headImageThumb" :size="160" |
||||
<view class="info-item"> |
@click="onShowFullImage()"></head-image> |
||||
<view class="info-primary"> |
|
||||
<text class="info-username"> |
<view class="info-item"> |
||||
{{userInfo.userName}} |
<view class="info-primary"> |
||||
</text> |
<text class="info-username"> |
||||
<uni-icons v-show="userInfo.sex==0" class="sex-boy" type="person-filled" size="20" |
{{ userInfo.userName }} |
||||
color="darkblue"></uni-icons> |
</text> |
||||
<uni-icons v-show="userInfo.sex==1" class="sex-girl" type="person-filled" size="20" |
<text v-show="userInfo.sex == 0" class="iconfont icon-man" color="darkblue"></text> |
||||
color="darkred"></uni-icons> |
<text v-show="userInfo.sex == 1" class="iconfont icon-girl" color="darkred"></text> |
||||
|
</view> |
||||
|
<view class="info-text"> |
||||
|
<text class="label-text"> |
||||
|
昵称: |
||||
|
</text> |
||||
|
<text class="content-text"> |
||||
|
{{ userInfo.nickName }} |
||||
|
</text> |
||||
|
</view> |
||||
|
<view class="info-text"> |
||||
|
<view> |
||||
|
<text class="label-text"> |
||||
|
签名: |
||||
|
</text> |
||||
|
<text class="content-text"> |
||||
|
{{ userInfo.signature }} |
||||
|
</text> |
||||
|
</view> |
||||
|
</view> |
||||
</view> |
</view> |
||||
<text> |
|
||||
昵称 :{{userInfo.nickName}} |
|
||||
</text> |
|
||||
<text> |
|
||||
签名 :{{userInfo.signature}} |
|
||||
</text> |
|
||||
</view> |
</view> |
||||
</view> |
</uni-card> |
||||
<view class="line"></view> |
<bar-group> |
||||
<view class="btn-group"> |
<btn-bar v-show="isFriend" type="primary" title="发送消息" @click="onSendMessage()"> |
||||
<button class="btn" v-show="isFriend" type="primary" @click="onSendMessage()">发消息</button> |
</btn-bar> |
||||
<button class="btn" v-show="!isFriend" type="primary" @click="onAddFriend()">加为好友</button> |
<btn-bar v-show="!isFriend" type="primary" title="加为好友" @click="onAddFriend()"></btn-bar> |
||||
<button class="btn" v-show="isFriend" type="warn" @click="onDelFriend()">删除好友</button> |
<btn-bar v-show="isFriend" type="danger" title="删除好友" @click="onDelFriend()"></btn-bar> |
||||
</view> |
</bar-group> |
||||
</view> |
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
export default { |
export default { |
||||
data() { |
data() { |
||||
return { |
return { |
||||
userInfo: {} |
userInfo: {} |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
onShowFullImage() { |
||||
|
let imageUrl = this.userInfo.headImage; |
||||
|
if (imageUrl) { |
||||
|
uni.previewImage({ |
||||
|
urls: [imageUrl] |
||||
|
}) |
||||
} |
} |
||||
}, |
}, |
||||
methods: { |
onSendMessage() { |
||||
onShowFullImage(){ |
let chat = { |
||||
let imageUrl = this.userInfo.headImage; |
type: 'PRIVATE', |
||||
if(imageUrl){ |
targetId: this.userInfo.id, |
||||
uni.previewImage({ |
showName: this.userInfo.nickName, |
||||
urls: [imageUrl] |
headImage: this.userInfo.headImage, |
||||
}) |
}; |
||||
|
this.chatStore.openChat(chat); |
||||
|
let chatIdx = this.chatStore.findChatIdx(chat); |
||||
|
uni.navigateTo({ |
||||
|
url: "/pages/chat/chat-box?chatIdx=" + chatIdx |
||||
|
}) |
||||
|
}, |
||||
|
onAddFriend() { |
||||
|
this.$http({ |
||||
|
url: "/friend/add?friendId=" + this.userInfo.id, |
||||
|
method: "POST" |
||||
|
}).then((data) => { |
||||
|
let friend = { |
||||
|
id: this.userInfo.id, |
||||
|
nickName: this.userInfo.nickName, |
||||
|
headImage: this.userInfo.headImageThumb, |
||||
|
online: this.userInfo.online |
||||
} |
} |
||||
}, |
this.friendStore.addFriend(friend); |
||||
onSendMessage() { |
uni.showToast({ |
||||
let chat = { |
title: '对方已成为您的好友', |
||||
type: 'PRIVATE', |
icon: 'none' |
||||
targetId: this.userInfo.id, |
|
||||
showName: this.userInfo.nickName, |
|
||||
headImage: this.userInfo.headImage, |
|
||||
}; |
|
||||
this.chatStore.openChat(chat); |
|
||||
let chatIdx = this.chatStore.findChatIdx(chat); |
|
||||
uni.navigateTo({ |
|
||||
url:"/pages/chat/chat-box?chatIdx=" + chatIdx |
|
||||
}) |
}) |
||||
}, |
}) |
||||
onAddFriend() { |
}, |
||||
this.$http({ |
onDelFriend() { |
||||
url: "/friend/add?friendId=" + this.userInfo.id, |
uni.showModal({ |
||||
method: "POST" |
title: "确认删除", |
||||
}).then((data) => { |
content: `确认删除 '${this.userInfo.nickName}',并删除聊天记录吗?`, |
||||
let friend = { |
success: (res) => { |
||||
id: this.userInfo.id, |
if (res.cancel) |
||||
nickName: this.userInfo.nickName, |
return; |
||||
headImage: this.userInfo.headImageThumb, |
this.$http({ |
||||
online: this.userInfo.online |
url: `/friend/delete/${this.userInfo.id}`, |
||||
} |
method: 'delete' |
||||
this.friendStore.addFriend(friend); |
}).then((data) => { |
||||
uni.showToast({ |
this.friendStore.removeFriend(this.userInfo.id); |
||||
title: '对方已成为您的好友', |
this.chatStore.removePrivateChat(this.userInfo.id); |
||||
icon: 'none' |
uni.showToast({ |
||||
}) |
title: `与 '${this.userInfo.nickName}'的好友关系已解除`, |
||||
}) |
icon: 'none' |
||||
}, |
|
||||
onDelFriend(){ |
|
||||
uni.showModal({ |
|
||||
title: "确认删除", |
|
||||
content: `确认删除 '${this.userInfo.nickName}',并删除聊天记录吗?`, |
|
||||
success: (res)=> { |
|
||||
if(res.cancel) |
|
||||
return; |
|
||||
this.$http({ |
|
||||
url: `/friend/delete/${this.userInfo.id}`, |
|
||||
method: 'delete' |
|
||||
}).then((data) => { |
|
||||
this.friendStore.removeFriend(this.userInfo.id); |
|
||||
this.chatStore.removePrivateChat(this.userInfo.id); |
|
||||
uni.showToast({ |
|
||||
title: `与 '${this.userInfo.nickName}'的好友关系已解除`, |
|
||||
icon: 'none' |
|
||||
}) |
|
||||
}) |
}) |
||||
} |
}) |
||||
}) |
} |
||||
}, |
}) |
||||
updateFriendInfo() { |
|
||||
// store的数据不能直接修改,深拷贝一份store的数据 |
|
||||
let friend = JSON.parse(JSON.stringify(this.friendInfo)); |
|
||||
friend.headImage = this.userInfo.headImageThumb; |
|
||||
friend.nickName = this.userInfo.nickName; |
|
||||
this.$http({ |
|
||||
url: "/friend/update", |
|
||||
method: "PUT", |
|
||||
data: friend |
|
||||
}).then(() => { |
|
||||
// 更新好友列表中的昵称和头像 |
|
||||
this.friendStore.updateFriend(friend); |
|
||||
// 更新会话中的头像和昵称 |
|
||||
this.chatStore.updateChatFromFriend(this.userInfo); |
|
||||
}) |
|
||||
}, |
|
||||
loadUserInfo(id){ |
|
||||
this.$http({ |
|
||||
url: "/user/find/" + id, |
|
||||
method: 'GET' |
|
||||
}).then((user) => { |
|
||||
this.userInfo = user; |
|
||||
// 如果发现好友的头像和昵称改了,进行更新 |
|
||||
if (this.isFriend && (this.userInfo.headImageThumb != this.friendInfo.headImage || |
|
||||
this.userInfo.nickName != this.friendInfo.nickName)) { |
|
||||
this.updateFriendInfo() |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
}, |
}, |
||||
computed: { |
updateFriendInfo() { |
||||
isFriend() { |
// store的数据不能直接修改,深拷贝一份store的数据 |
||||
return !!this.friendInfo; |
let friend = JSON.parse(JSON.stringify(this.friendInfo)); |
||||
}, |
friend.headImage = this.userInfo.headImageThumb; |
||||
friendInfo(){ |
friend.nickName = this.userInfo.nickName; |
||||
let friends = this.friendStore.friends; |
this.$http({ |
||||
let friend = friends.find((f) => f.id == this.userInfo.id); |
url: "/friend/update", |
||||
return friend; |
method: "PUT", |
||||
} |
data: friend |
||||
|
}).then(() => { |
||||
|
// 更新好友列表中的昵称和头像 |
||||
|
this.friendStore.updateFriend(friend); |
||||
|
// 更新会话中的头像和昵称 |
||||
|
this.chatStore.updateChatFromFriend(this.userInfo); |
||||
|
}) |
||||
}, |
}, |
||||
onLoad(options) { |
loadUserInfo(id) { |
||||
// 查询用户信息 |
this.$http({ |
||||
this.loadUserInfo(options.id); |
url: "/user/find/" + id, |
||||
|
method: 'GET' |
||||
|
}).then((user) => { |
||||
|
this.userInfo = user; |
||||
|
// 如果发现好友的头像和昵称改了,进行更新 |
||||
|
if (this.isFriend && (this.userInfo.headImageThumb != this.friendInfo.headImage || |
||||
|
this.userInfo.nickName != this.friendInfo.nickName)) { |
||||
|
this.updateFriendInfo() |
||||
|
} |
||||
|
}) |
||||
} |
} |
||||
|
}, |
||||
|
computed: { |
||||
|
isFriend() { |
||||
|
return !!this.friendInfo; |
||||
|
}, |
||||
|
friendInfo() { |
||||
|
let friends = this.friendStore.friends; |
||||
|
let friend = friends.find((f) => f.id == this.userInfo.id); |
||||
|
return friend; |
||||
|
} |
||||
|
}, |
||||
|
onLoad(options) { |
||||
|
// 查询用户信息 |
||||
|
this.loadUserInfo(options.id); |
||||
} |
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style lang="scss" scoped> |
<style lang="scss" scoped> |
||||
.user-info { |
.user-info { |
||||
.content { |
.content { |
||||
height: 200rpx; |
height: 200rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
padding: 20rpx; |
||||
|
|
||||
|
.info-item { |
||||
display: flex; |
display: flex; |
||||
align-items: center; |
align-items: flex-start; |
||||
justify-content: space-between; |
flex-direction: column; |
||||
padding: 20rpx; |
padding-left: 40rpx; |
||||
|
flex: 1; |
||||
|
|
||||
|
.info-text { |
||||
|
line-height: 1.5; |
||||
|
//margin-bottom: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.label-text { |
||||
|
font-size: $im-font-size-small; |
||||
|
color: $im-text-color-light; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
.content-text { |
||||
|
font-size: $im-font-size-small; |
||||
|
color: $im-text-color-light; |
||||
|
} |
||||
|
|
||||
.info-item { |
.info-primary { |
||||
display: flex; |
display: flex; |
||||
align-items: flex-start; |
align-items: center; |
||||
flex-direction: column; |
margin-bottom: 10rpx; |
||||
padding-left: 40rpx; |
|
||||
flex: 1; |
.info-username { |
||||
|
font-size: $im-font-size-large; |
||||
|
font-weight: 600; |
||||
.info-primary { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
|
|
||||
.info-username { |
|
||||
font-size: 40rpx; |
|
||||
font-weight: 600; |
|
||||
} |
|
||||
} |
} |
||||
} |
|
||||
} |
|
||||
|
|
||||
.line { |
.icon-man { |
||||
margin: 20rpx; |
color: $im-text-color; |
||||
border-bottom: 1px solid #aaaaaa; |
font-size: $im-font-size-large; |
||||
} |
padding-left: 10rpx; |
||||
|
} |
||||
|
|
||||
.btn-group { |
.icon-girl { |
||||
margin: 100rpx; |
color: darkred; |
||||
|
} |
||||
.btn{ |
|
||||
margin-top: 20rpx; |
|
||||
} |
} |
||||
|
|
||||
} |
} |
||||
} |
} |
||||
|
|
||||
|
} |
||||
</style> |
</style> |
||||
@ -1,151 +1,150 @@ |
|||||
<template> |
<template> |
||||
|
|
||||
<view class="tab-page friend"> |
<view class="tab-page friend"> |
||||
<view class="nav-bar"> |
<nav-bar add search @add="onAddNewFriends" @search="showSearch = !showSearch">好友</nav-bar> |
||||
|
<view class="nav-bar" v-if="showSearch"> |
||||
<view class="nav-search"> |
<view class="nav-search"> |
||||
<uni-search-bar v-model="searchText" radius="100" cancelButton="none" placeholder="点击搜索好友"></uni-search-bar> |
<uni-search-bar v-model="searchText" radius="100" cancelButton="none" |
||||
</view> |
placeholder="点击搜索好友"></uni-search-bar> |
||||
<view class="nav-add" @click="onAddNewFriends()"> |
|
||||
<uni-icons type="personadd" size="35"></uni-icons> |
|
||||
</view> |
</view> |
||||
</view> |
</view> |
||||
<view class="friend-tip" v-if="friends.length==0"> |
<view class="friend-tip" v-if="friends.length == 0"> |
||||
温馨提示:您现在还没有任何好友,快点击右上方'+'按钮添加好友吧~ |
温馨提示:您现在还没有任何好友,快点击右上方'+'按钮添加好友吧~ |
||||
</view> |
</view> |
||||
<view class="friend-items" v-else> |
<view class="friend-items" v-else> |
||||
<up-index-list :index-list="friendIdx" > |
<up-index-list :index-list="friendIdx"> |
||||
<template v-for="(friends,i) in friendGroups"> |
<template v-for="(friends, i) in friendGroups"> |
||||
<up-index-item> |
<up-index-item> |
||||
<up-index-anchor :text="friendIdx[i]=='*'?'在线':friendIdx[i]" |
<up-index-anchor :text="friendIdx[i] == '*' ? '在线' : friendIdx[i]"></up-index-anchor> |
||||
bgColor="#f2f3fd"></up-index-anchor> |
<view v-for="(friend, idx) in friends" :key="idx"> |
||||
<view v-for="(friend,idx) in friends" :key="idx"> |
|
||||
<friend-item :friend="friend"></friend-item> |
<friend-item :friend="friend"></friend-item> |
||||
</view> |
</view> |
||||
</up-index-item> |
</up-index-item> |
||||
</template> |
</template> |
||||
</up-index-list> |
</up-index-list> |
||||
</view> |
</view> |
||||
|
|
||||
</view> |
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
import { pinyin } from 'pinyin-pro'; |
import { pinyin } from 'pinyin-pro'; |
||||
|
|
||||
export default { |
export default { |
||||
data() { |
data() { |
||||
return { |
return { |
||||
searchText: '' |
showSearch: false, |
||||
} |
searchText: '' |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
onAddNewFriends() { |
||||
|
uni.navigateTo({ |
||||
|
url: "/pages/friend/friend-add" |
||||
|
}) |
||||
|
}, |
||||
|
firstLetter(strText) { |
||||
|
// 使用pinyin-pro库将中文转换为拼音 |
||||
|
let pinyinOptions = { |
||||
|
toneType: 'none', // 无声调 |
||||
|
type: 'normal' // 普通拼音 |
||||
|
}; |
||||
|
let pyText = pinyin(strText, pinyinOptions); |
||||
|
return pyText[0]; |
||||
}, |
}, |
||||
methods: { |
isEnglish(character) { |
||||
onAddNewFriends() { |
return /^[A-Za-z]+$/.test(character); |
||||
uni.navigateTo({ |
} |
||||
url: "/pages/friend/friend-add" |
}, |
||||
}) |
computed: { |
||||
}, |
friends() { |
||||
firstLetter(strText) { |
return this.friendStore.friends; |
||||
// 使用pinyin-pro库将中文转换为拼音 |
}, |
||||
let pinyinOptions = { |
friendGroupMap() { |
||||
toneType: 'none', // 无声调 |
// 按首字母分组 |
||||
type: 'normal' // 普通拼音 |
let groupMap = new Map(); |
||||
}; |
this.friends.forEach((f) => { |
||||
let pyText = pinyin(strText, pinyinOptions); |
if (this.searchText && !f.nickName.includes(this.searchText)) { |
||||
return pyText[0]; |
return; |
||||
}, |
} |
||||
isEnglish(character) { |
let letter = this.firstLetter(f.nickName).toUpperCase(); |
||||
return /^[A-Za-z]+$/.test(character); |
// 非英文一律为#组 |
||||
} |
if (!this.isEnglish(letter)) { |
||||
|
letter = "#" |
||||
|
} |
||||
|
if (f.online) { |
||||
|
letter = '*' |
||||
|
} |
||||
|
if (groupMap.has(letter)) { |
||||
|
groupMap.get(letter).push(f); |
||||
|
} else { |
||||
|
groupMap.set(letter, [f]); |
||||
|
} |
||||
|
}) |
||||
|
// 排序 |
||||
|
let arrayObj = Array.from(groupMap); |
||||
|
arrayObj.sort((a, b) => { |
||||
|
// #组在最后面 |
||||
|
if (a[0] == '#' || b[0] == '#') { |
||||
|
return b[0].localeCompare(a[0]) |
||||
|
} |
||||
|
return a[0].localeCompare(b[0]) |
||||
|
}) |
||||
|
groupMap = new Map(arrayObj.map(i => [i[0], i[1]])); |
||||
|
return groupMap; |
||||
|
}, |
||||
|
friendIdx() { |
||||
|
return Array.from(this.friendGroupMap.keys()); |
||||
}, |
}, |
||||
computed: { |
friendGroups() { |
||||
friends() { |
return Array.from(this.friendGroupMap.values()); |
||||
return this.friendStore.friends; |
|
||||
}, |
|
||||
friendGroupMap(){ |
|
||||
// 按首字母分组 |
|
||||
let groupMap = new Map(); |
|
||||
this.friends.forEach((f) => { |
|
||||
if(this.searchText && !f.nickName.includes(this.searchText)){ |
|
||||
return; |
|
||||
} |
|
||||
let letter = this.firstLetter(f.nickName).toUpperCase(); |
|
||||
// 非英文一律为#组 |
|
||||
if (!this.isEnglish(letter)) { |
|
||||
letter = "#" |
|
||||
} |
|
||||
if (f.online) { |
|
||||
letter = '*' |
|
||||
} |
|
||||
if (groupMap.has(letter)) { |
|
||||
groupMap.get(letter).push(f); |
|
||||
} else { |
|
||||
groupMap.set(letter, [f]); |
|
||||
} |
|
||||
}) |
|
||||
// 排序 |
|
||||
let arrayObj = Array.from(groupMap); |
|
||||
arrayObj.sort((a, b) => { |
|
||||
// #组在最后面 |
|
||||
if (a[0] == '#' || b[0] == '#') { |
|
||||
return b[0].localeCompare(a[0]) |
|
||||
} |
|
||||
return a[0].localeCompare(b[0]) |
|
||||
}) |
|
||||
groupMap = new Map(arrayObj.map(i => [i[0], i[1]])); |
|
||||
return groupMap; |
|
||||
}, |
|
||||
friendIdx(){ |
|
||||
return Array.from(this.friendGroupMap.keys()); |
|
||||
}, |
|
||||
friendGroups(){ |
|
||||
return Array.from(this.friendGroupMap.values()); |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style lang="scss" scoped> |
<style lang="scss" scoped> |
||||
.friend { |
.friend { |
||||
position: relative; |
position: relative; |
||||
border: #dddddd solid 1px; |
display: flex; |
||||
display: flex; |
flex-direction: column; |
||||
flex-direction: column; |
|
||||
|
|
||||
.friend-tip { |
:deep(.u-index-anchor) { |
||||
position: absolute; |
height: 60rpx !important; |
||||
top: 400rpx; |
background-color: unset !important; |
||||
padding: 50rpx; |
border-bottom: none !important; |
||||
text-align: center; |
} |
||||
line-height: 50rpx; |
|
||||
text-align: left; |
|
||||
color: darkblue; |
|
||||
font-size: 30rpx; |
|
||||
} |
|
||||
|
|
||||
.nav-bar { |
:deep(.u-index-anchor__text) { |
||||
padding: 2rpx 10rpx; |
color: $im-text-color !important; |
||||
display: flex; |
} |
||||
align-items: center; |
|
||||
background-color: white; |
|
||||
|
|
||||
.nav-search { |
:deep(.u-index-list__letter__item) { |
||||
flex: 1; |
width: 48rpx !important; |
||||
} |
height: 48rpx !important; |
||||
|
} |
||||
|
|
||||
.nav-add { |
:deep(.u-index-list__letter__item__index) { |
||||
cursor: pointer; |
font-size: $im-font-size-small !important; |
||||
} |
} |
||||
} |
|
||||
|
.friend-tip { |
||||
|
position: absolute; |
||||
|
top: 400rpx; |
||||
|
padding: 50rpx; |
||||
|
text-align: center; |
||||
|
line-height: 50rpx; |
||||
|
color: $im-text-color-lighter; |
||||
|
} |
||||
|
|
||||
|
.friend-items { |
||||
|
flex: 1; |
||||
|
padding: 0; |
||||
|
overflow: hidden; |
||||
|
position: relative; |
||||
|
|
||||
.friend-items { |
.scroll-bar { |
||||
flex: 1; |
height: 100%; |
||||
padding: 0; |
|
||||
border: #dddddd solid 1px; |
|
||||
overflow: hidden; |
|
||||
position: relative; |
|
||||
|
|
||||
.scroll-bar { |
|
||||
height: 100%; |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
</style> |
</style> |
||||
@ -1,150 +1,157 @@ |
|||||
<template> |
<template> |
||||
<view v-if="userStore.userInfo.type == 1" class="page group-edit"> |
<view v-if="userStore.userInfo.type == 1" class="page group-edit"> |
||||
<uni-forms ref="form" :modelValue="group" :rules="rules" validate-trigger="bind" label-position="top" |
<nav-bar back>修改群资料</nav-bar> |
||||
label-width="100%"> |
<uni-card :is-shadow="false" is-full :border="false"> |
||||
<uni-forms-item label="群聊头像:" name="headImage"> |
<uni-forms ref="form" :modelValue="group" :rules="rules" validate-trigger="bind" label-position="top" |
||||
<image-upload v-if="isOwner" :onSuccess="onUnloadImageSuccess"> |
label-width="100%"> |
||||
<image :src="group.headImageThumb" class="group-image"></image> |
<uni-forms-item name="headImage" class="avatar"> |
||||
</image-upload> |
<image-upload v-if="isOwner" :onSuccess="onUnloadImageSuccess"> |
||||
<head-image v-if="!isOwner" :name="group.showGroupName" |
<image :src="group.headImageThumb" class="group-image"></image> |
||||
:url="group.headImageThumb" :size="200"></head-image> |
</image-upload> |
||||
</uni-forms-item> |
<head-image v-if="!isOwner" :name="group.showGroupName" :url="group.headImageThumb" |
||||
<uni-forms-item label="群聊名称:" name="name" :required="true"> |
:size="200"></head-image> |
||||
<uni-easyinput type="text" v-model="group.name" :disabled="!isOwner" placeholder="请输入群聊名称" /> |
</uni-forms-item> |
||||
</uni-forms-item> |
<uni-forms-item label="群聊名称" name="name" :required="true"> |
||||
<uni-forms-item label="群聊备注:" name="remarkGroupName"> |
<uni-easyinput type="text" v-model="group.name" :disabled="!isOwner" placeholder="请输入群聊名称" /> |
||||
<uni-easyinput v-model="group.remarkGroupName" type="text" :placeholder="group.name" /> |
</uni-forms-item> |
||||
</uni-forms-item> |
<uni-forms-item label="群聊备注" name="remarkGroupName"> |
||||
<uni-forms-item label="我在本群的昵称:" name="remarkNickName"> |
<uni-easyinput v-model="group.remarkGroupName" type="text" :placeholder="group.name" /> |
||||
<uni-easyinput v-model="group.remarkNickName" type="text" :placeholder="userStore.userInfo.nickName" /> |
</uni-forms-item> |
||||
</uni-forms-item> |
<uni-forms-item label="我在本群的昵称" name="remarkNickName"> |
||||
<uni-forms-item label="群公告:" name="notice"> |
<uni-easyinput v-model="group.remarkNickName" type="text" |
||||
<uni-easyinput type="textarea" v-model="group.notice" :disabled="!isOwner" placeholder="请输入群公告" /> |
:placeholder="userStore.userInfo.nickName" /> |
||||
</uni-forms-item> |
</uni-forms-item> |
||||
</uni-forms> |
<uni-forms-item label="群公告" name="notice"> |
||||
<button type="primary" @click="submit()">提交</button> |
<uni-easyinput type="textarea" v-model="group.notice" :disabled="!isOwner" placeholder="请输入群公告" /> |
||||
|
</uni-forms-item> |
||||
|
</uni-forms> |
||||
|
</uni-card> |
||||
|
<button class="bottom-btn" type="primary" @click="submit()">提交</button> |
||||
</view> |
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
export default { |
export default { |
||||
data() { |
data() { |
||||
return { |
return { |
||||
group: {}, |
group: {}, |
||||
rules: { |
rules: { |
||||
name: { |
name: { |
||||
rules: [{ |
rules: [{ |
||||
required: true, |
required: true, |
||||
errorMessage: '请输入群聊名称', |
errorMessage: '请输入群聊名称', |
||||
}] |
}] |
||||
} |
|
||||
|
|
||||
} |
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
submit() { |
||||
|
if (this.group.id) { |
||||
|
this.modifyGroup(); |
||||
|
} else { |
||||
|
this.createNewGroup(); |
||||
} |
} |
||||
}, |
}, |
||||
|
onUnloadImageSuccess(file, res) { |
||||
|
this.group.headImage = res.data.originUrl; |
||||
|
this.group.headImageThumb = res.data.thumbUrl; |
||||
|
}, |
||||
|
modifyGroup() { |
||||
|
this.$http({ |
||||
|
url: "/group/modify", |
||||
|
method: "PUT", |
||||
|
data: this.group |
||||
|
}).then((group) => { |
||||
|
this.groupStore.updateGroup(group); |
||||
|
uni.showToast({ |
||||
|
title: "修改群聊信息成功", |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
setTimeout(() => { |
||||
|
let pages = getCurrentPages(); |
||||
|
let prevPage = pages[pages.length - 2]; |
||||
|
prevPage.$vm.loadGroupInfo(); |
||||
|
uni.navigateBack(); |
||||
|
}, 1000); |
||||
|
|
||||
methods: { |
}) |
||||
submit() { |
}, |
||||
if (this.group.id) { |
createNewGroup() { |
||||
this.modifyGroup(); |
this.$http({ |
||||
} else { |
url: "/group/create", |
||||
this.createNewGroup(); |
method: 'POST', |
||||
} |
data: this.group |
||||
}, |
}).then((group) => { |
||||
onUnloadImageSuccess(file, res) { |
this.groupStore.addGroup(group); |
||||
this.group.headImage = res.data.originUrl; |
uni.showToast({ |
||||
this.group.headImageThumb = res.data.thumbUrl; |
title: `群聊创建成功,快邀请小伙伴进群吧`, |
||||
}, |
icon: 'none', |
||||
modifyGroup() { |
duration: 1500 |
||||
this.$http({ |
}); |
||||
url: "/group/modify", |
setTimeout(() => { |
||||
method: "PUT", |
uni.navigateTo({ |
||||
data: this.group |
url: "/pages/group/group-info?id=" + group.id |
||||
}).then((group) => { |
|
||||
this.groupStore.updateGroup(group); |
|
||||
uni.showToast({ |
|
||||
title: "修改群聊信息成功", |
|
||||
icon: 'none' |
|
||||
}); |
|
||||
setTimeout(() => { |
|
||||
let pages = getCurrentPages(); |
|
||||
let prevPage = pages[pages.length - 2]; |
|
||||
prevPage.$vm.loadGroupInfo(); |
|
||||
uni.navigateBack(); |
|
||||
}, 1000); |
|
||||
|
|
||||
}) |
|
||||
}, |
|
||||
createNewGroup() { |
|
||||
this.$http({ |
|
||||
url: "/group/create", |
|
||||
method: 'POST', |
|
||||
data: this.group |
|
||||
}).then((group) => { |
|
||||
this.groupStore.addGroup(group); |
|
||||
uni.showToast({ |
|
||||
title: `群聊创建成功,快邀请小伙伴进群吧`, |
|
||||
icon: 'none', |
|
||||
duration: 1500 |
|
||||
}); |
}); |
||||
setTimeout(() => { |
}, 1500) |
||||
uni.navigateTo({ |
|
||||
url: "/pages/group/group-info?id=" + group.id |
|
||||
}); |
|
||||
}, 1500) |
|
||||
|
|
||||
}) |
|
||||
}, |
|
||||
loadGroupInfo(id) { |
|
||||
this.$http({ |
|
||||
url: `/group/find/${id}`, |
|
||||
method: 'GET' |
|
||||
}).then((group) => { |
|
||||
this.group = group; |
|
||||
// 更新聊天页面的群聊信息 |
|
||||
this.chatStore.updateChatFromGroup(group); |
|
||||
// 更新聊天列表的群聊信息 |
|
||||
this.groupStore.updateGroup(group); |
|
||||
|
|
||||
}); |
}) |
||||
}, |
|
||||
initNewGroup() { |
|
||||
let userInfo = this.userStore.userInfo; |
|
||||
this.group = { |
|
||||
name: `${userInfo.userName}创建的群聊`, |
|
||||
headImage: userInfo.headImage, |
|
||||
headImageThumb: userInfo.headImageThumb, |
|
||||
ownerId: this.userStore.userInfo.id |
|
||||
} |
|
||||
} |
|
||||
}, |
}, |
||||
computed: { |
loadGroupInfo(id) { |
||||
isOwner() { |
this.$http({ |
||||
return this.userStore.userInfo.id == this.group.ownerId |
url: `/group/find/${id}`, |
||||
} |
method: 'GET' |
||||
|
}).then((group) => { |
||||
|
this.group = group; |
||||
|
// 更新聊天页面的群聊信息 |
||||
|
this.chatStore.updateChatFromGroup(group); |
||||
|
// 更新聊天列表的群聊信息 |
||||
|
this.groupStore.updateGroup(group); |
||||
|
|
||||
|
}); |
||||
}, |
}, |
||||
onLoad(options) { |
initNewGroup() { |
||||
if (options.id) { |
let userInfo = this.userStore.userInfo; |
||||
// 修改群聊 |
this.group = { |
||||
this.loadGroupInfo(options.id); |
name: `${userInfo.userName}创建的群聊`, |
||||
} else { |
headImage: userInfo.headImage, |
||||
// 创建群聊 |
headImageThumb: userInfo.headImageThumb, |
||||
this.initNewGroup(); |
ownerId: this.userStore.userInfo.id |
||||
} |
} |
||||
|
|
||||
} |
} |
||||
|
}, |
||||
|
computed: { |
||||
|
isOwner() { |
||||
|
return this.userStore.userInfo.id == this.group.ownerId |
||||
|
} |
||||
|
}, |
||||
|
onLoad(options) { |
||||
|
if (options.id) { |
||||
|
// 修改群聊 |
||||
|
this.loadGroupInfo(options.id); |
||||
|
} else { |
||||
|
// 创建群聊 |
||||
|
this.initNewGroup(); |
||||
|
} |
||||
|
|
||||
} |
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style lang="scss" scoped> |
<style lang="scss" scoped> |
||||
.group-edit { |
.group-edit { |
||||
padding: 20rpx; |
//padding: 20rpx; |
||||
|
|
||||
.group-image { |
.group-image { |
||||
width: 200rpx; |
width: 200rpx; |
||||
height: 200rpx; |
height: 200rpx; |
||||
border: 1px solid #ccc; |
border: 1px solid #ccc; |
||||
border-radius: 5%; |
border-radius: 5%; |
||||
} |
|
||||
} |
} |
||||
</style> |
} |
||||
|
|
||||
|
.avatar { |
||||
|
margin-top: -30px; |
||||
|
} |
||||
|
</style> |
||||
|
|||||
@ -1,280 +1,266 @@ |
|||||
<template> |
<template> |
||||
<view v-if="userStore.userInfo.type == 1" class="page group-info"> |
<view v-if="userStore.userInfo.type == 1" class="page group-info"> |
||||
<view v-if="!group.quit" class="group-members"> |
<nav-bar back>群聊信息</nav-bar> |
||||
|
<view v-if="!group.quit" class="group-members"> |
||||
<view class="member-items"> |
<view class="member-items"> |
||||
<view v-for="(member,idx) in groupMembers" :key="idx"> |
<view v-for="(member, idx) in groupMembers" :key="idx"> |
||||
<view class="member-item" v-if="idx<9"> |
<view class="member-item" v-if="idx < 9"> |
||||
<head-image :id="member.userId" :name="member.showNickName" :url="member.headImage" |
<head-image :id="member.userId" :name="member.showNickName" :url="member.headImage" size="small" |
||||
:size="100" :online="member.online" ></head-image> |
:online="member.online"></head-image> |
||||
<view class="member-name"> |
<view class="member-name"> |
||||
<text>{{member.showNickName}}</text> |
<text>{{ member.showNickName }}</text> |
||||
</view> |
</view> |
||||
</view> |
</view> |
||||
</view> |
</view> |
||||
<view class="invite-btn" @click="onInviteMember()"> |
<view class="invite-btn" @click="onInviteMember()"> |
||||
<uni-icons type="plusempty" size="28" color="#888888"></uni-icons> |
<uni-icons type="plusempty" size="20" color="#888888"></uni-icons> |
||||
</view> |
</view> |
||||
</view> |
</view> |
||||
<view class="member-more" @click="onShowMoreMmeber()">{{`查看全部群成员${groupMembers.length}人`}}></view> |
<view class="member-more" @click="onShowMoreMmeber()">{{ `查看全部群成员${groupMembers.length}人` }}></view> |
||||
</view> |
</view> |
||||
<view class="group-detail"> |
<view class="group-detail"> |
||||
<uni-section title="群聊名称:" titleFontSize="14px"> |
|
||||
|
<uni-section title="群聊名称"> |
||||
<template v-slot:right> |
<template v-slot:right> |
||||
<text class="detail-text">{{group.name}}</text> |
<text class="detail-text">{{ group.name }}</text> |
||||
</template> |
</template> |
||||
</uni-section> |
</uni-section> |
||||
<uni-section title="群主:" titleFontSize="14px"> |
<uni-section title="群主"> |
||||
<template v-slot:right> |
<template v-slot:right> |
||||
<text class="detail-text">{{ownerName}}</text> |
<text class="detail-text">{{ ownerName }}</text> |
||||
</template> |
</template> |
||||
</uni-section> |
</uni-section> |
||||
|
<uni-section title="群名备注"> |
||||
<uni-section title="群名备注:" titleFontSize="14px"> |
|
||||
<template v-slot:right> |
<template v-slot:right> |
||||
<text class="detail-text"> {{group.remarkGroupName}}</text> |
<text class="detail-text"> {{ group.remarkGroupName }}</text> |
||||
</template> |
</template> |
||||
</uni-section> |
</uni-section> |
||||
<uni-section title="我在本群的昵称:" titleFontSize="14px"> |
<uni-section title="我在本群的昵称"> |
||||
<template v-slot:right> |
<template v-slot:right> |
||||
<text class="detail-text"> {{group.showNickName}}</text> |
<text class="detail-text"> {{ group.showNickName }}</text> |
||||
</template> |
</template> |
||||
</uni-section> |
</uni-section> |
||||
<uni-section v-if="group.notice" title="群公告:" titleFontSize="14px"> |
<uni-section v-if="group.notice" title="群公告"> |
||||
<uni-notice-bar :text="group.notice" /> |
<uni-notice-bar :text="group.notice" /> |
||||
</uni-section> |
</uni-section> |
||||
<view v-if="!group.quit" class="group-edit" @click="onEditGroup()">修改群聊资料 > </view> |
<view v-if="!group.quit" class="group-edit" @click="onEditGroup()">修改群聊资料 > </view> |
||||
</view> |
|
||||
<view v-if="!group.quit" class="btn-group"> |
|
||||
<button class="btn" type="primary" @click="onSendMessage()">发消息</button> |
|
||||
<button class="btn" v-show="!isOwner" type="warn" @click="onQuitGroup()">退出群聊</button> |
|
||||
<button class="btn" v-show="isOwner" type="warn" @click="onDissolveGroup()">解散群聊</button> |
|
||||
</view> |
</view> |
||||
|
<bar-group v-if="!group.quit"> |
||||
|
<btn-bar type="primary" title="发送消息" @click="onSendMessage()"></btn-bar> |
||||
|
<btn-bar v-if="!isOwner" type="danger" title="退出群聊" @click="onQuitGroup()"></btn-bar> |
||||
|
<btn-bar v-if="isOwner" type="danger" title="解散群聊" @click="onDissolveGroup()"></btn-bar> |
||||
|
</bar-group> |
||||
</view> |
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
export default { |
export default { |
||||
data() { |
data() { |
||||
return { |
return { |
||||
groupId: null, |
groupId: null, |
||||
group:{}, |
group: {}, |
||||
groupMembers: [] |
groupMembers: [] |
||||
} |
} |
||||
|
}, |
||||
|
methods: { |
||||
|
onFocusSearch() { }, |
||||
|
onInviteMember() { |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/group/group-invite?id=${this.groupId}` |
||||
|
}) |
||||
|
}, |
||||
|
onShowMoreMmeber() { |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/group/group-member?id=${this.groupId}` |
||||
|
}) |
||||
|
}, |
||||
|
onEditGroup() { |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/group/group-edit?id=${this.groupId}` |
||||
|
}) |
||||
|
}, |
||||
|
onSendMessage() { |
||||
|
let chat = { |
||||
|
type: 'GROUP', |
||||
|
targetId: this.group.id, |
||||
|
showName: this.group.showGroupName, |
||||
|
headImage: this.group.headImage, |
||||
|
}; |
||||
|
this.chatStore.openChat(chat); |
||||
|
let chatIdx = this.chatStore.findChatIdx(chat); |
||||
|
uni.navigateTo({ |
||||
|
url: "/pages/chat/chat-box?chatIdx=" + chatIdx |
||||
|
}) |
||||
}, |
}, |
||||
methods: { |
onQuitGroup() { |
||||
onFocusSearch() {}, |
uni.showModal({ |
||||
onInviteMember() { |
title: '确认退出?', |
||||
uni.navigateTo({ |
content: `退出群聊后将不再接受群里的消息,确认退出吗?`, |
||||
url: `/pages/group/group-invite?id=${this.groupId}` |
success: (res) => { |
||||
}) |
if (res.cancel) |
||||
}, |
return; |
||||
onShowMoreMmeber() { |
this.$http({ |
||||
uni.navigateTo({ |
url: `/group/quit/${this.groupId}`, |
||||
url: `/pages/group/group-member?id=${this.groupId}` |
method: 'DELETE' |
||||
}) |
}).then(() => { |
||||
}, |
uni.showModal({ |
||||
onEditGroup() { |
title: `退出成功`, |
||||
uni.navigateTo({ |
content: `您已退出群聊'${this.group.name}'`, |
||||
url: `/pages/group/group-edit?id=${this.groupId}` |
showCancel: false, |
||||
}) |
success: () => { |
||||
}, |
setTimeout(() => { |
||||
onSendMessage() { |
uni.switchTab({ |
||||
let chat = { |
url: "/pages/group/group" |
||||
type: 'GROUP', |
}); |
||||
targetId: this.group.id, |
this.groupStore.removeGroup(this.groupId); |
||||
showName: this.group.showGroupName, |
this.chatStore.removeGroupChat(this.groupId); |
||||
headImage: this.group.headImage, |
}, 100) |
||||
}; |
} |
||||
this.chatStore.openChat(chat); |
}) |
||||
let chatIdx = this.chatStore.findChatIdx(chat); |
}); |
||||
uni.navigateTo({ |
} |
||||
url: "/pages/chat/chat-box?chatIdx=" + chatIdx |
}); |
||||
}) |
}, |
||||
}, |
onDissolveGroup() { |
||||
onQuitGroup() { |
uni.showModal({ |
||||
uni.showModal({ |
title: '确认解散?', |
||||
title: '确认退出?', |
content: `确认要解散群聊'${this.group.name}'吗?`, |
||||
content: `退出群聊后将不再接受群里的消息,确认退出吗?`, |
success: (res) => { |
||||
success: (res) => { |
if (res.cancel) |
||||
if (res.cancel) |
return; |
||||
return; |
this.$http({ |
||||
this.$http({ |
url: `/group/delete/${this.groupId}`, |
||||
url: `/group/quit/${this.groupId}`, |
method: 'delete' |
||||
method: 'DELETE' |
}).then(() => { |
||||
}).then(() => { |
uni.showModal({ |
||||
uni.showModal({ |
title: `解散成功`, |
||||
title: `退出成功`, |
content: `群聊'${this.group.name}'已解散`, |
||||
content: `您已退出群聊'${this.group.name}'`, |
showCancel: false, |
||||
showCancel: false, |
success: () => { |
||||
success: () => { |
setTimeout(() => { |
||||
setTimeout(()=>{ |
uni.switchTab({ |
||||
uni.switchTab({ |
url: "/pages/group/group" |
||||
url:"/pages/group/group" |
}); |
||||
}); |
this.groupStore.removeGroup(this.groupId); |
||||
this.groupStore.removeGroup(this.groupId); |
this.chatStore.removeGroupChat(this.groupId); |
||||
this.chatStore.removeGroupChat(this.groupId); |
}, 100) |
||||
},100) |
} |
||||
} |
}) |
||||
}) |
}); |
||||
}); |
} |
||||
} |
}); |
||||
}); |
|
||||
}, |
|
||||
onDissolveGroup() { |
|
||||
uni.showModal({ |
|
||||
title: '确认解散?', |
|
||||
content: `确认要解散群聊'${this.group.name}'吗?`, |
|
||||
success: (res) => { |
|
||||
if (res.cancel) |
|
||||
return; |
|
||||
this.$http({ |
|
||||
url: `/group/delete/${this.groupId}`, |
|
||||
method: 'delete' |
|
||||
}).then(() => { |
|
||||
uni.showModal({ |
|
||||
title: `解散成功`, |
|
||||
content: `群聊'${this.group.name}'已解散`, |
|
||||
showCancel: false, |
|
||||
success: () => { |
|
||||
setTimeout(()=>{ |
|
||||
uni.switchTab({ |
|
||||
url:"/pages/group/group" |
|
||||
}); |
|
||||
this.groupStore.removeGroup(this.groupId); |
|
||||
this.chatStore.removeGroupChat(this.groupId); |
|
||||
},100) |
|
||||
} |
|
||||
}) |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
}, |
}, |
||||
loadGroupInfo() { |
loadGroupInfo() { |
||||
this.$http({ |
this.$http({ |
||||
url: `/group/find/${this.groupId}`, |
url: `/group/find/${this.groupId}`, |
||||
method: 'GET' |
method: 'GET' |
||||
}).then((group) => { |
}).then((group) => { |
||||
this.group = group; |
this.group = group; |
||||
// 更新聊天页面的群聊信息 |
// 更新聊天页面的群聊信息 |
||||
this.chatStore.updateChatFromGroup(group); |
this.chatStore.updateChatFromGroup(group); |
||||
// 更新聊天列表的群聊信息 |
// 更新聊天列表的群聊信息 |
||||
this.groupStore.updateGroup(group); |
this.groupStore.updateGroup(group); |
||||
|
|
||||
}); |
}); |
||||
}, |
|
||||
loadGroupMembers() { |
|
||||
console.log("loadGroupMembers") |
|
||||
this.$http({ |
|
||||
url: `/group/members/${this.groupId}`, |
|
||||
method: "GET" |
|
||||
}).then((members) => { |
|
||||
this.groupMembers = members.filter(m => !m.quit); |
|
||||
}) |
|
||||
} |
|
||||
}, |
}, |
||||
computed: { |
loadGroupMembers() { |
||||
ownerName() { |
console.log("loadGroupMembers") |
||||
let member = this.groupMembers.find((m) => m.userId == this.group.ownerId); |
this.$http({ |
||||
return member && member.showNickName; |
url: `/group/members/${this.groupId}`, |
||||
}, |
method: "GET" |
||||
isOwner() { |
}).then((members) => { |
||||
return this.group.ownerId == this.userStore.userInfo.id; |
this.groupMembers = members.filter(m => !m.quit); |
||||
} |
}) |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
ownerName() { |
||||
|
let member = this.groupMembers.find((m) => m.userId == this.group.ownerId); |
||||
|
return member && member.showNickName; |
||||
}, |
}, |
||||
onLoad(options) { |
isOwner() { |
||||
this.groupId = options.id; |
return this.group.ownerId == this.userStore.userInfo.id; |
||||
// 查询群聊信息 |
|
||||
this.loadGroupInfo(options.id); |
|
||||
// 查询群聊成员 |
|
||||
this.loadGroupMembers(options.id) |
|
||||
} |
} |
||||
|
}, |
||||
|
onLoad(options) { |
||||
|
this.groupId = options.id; |
||||
|
// 查询群聊信息 |
||||
|
this.loadGroupInfo(options.id); |
||||
|
// 查询群聊成员 |
||||
|
this.loadGroupMembers(options.id) |
||||
} |
} |
||||
|
|
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style lang="scss" scoped> |
<style lang="scss" scoped> |
||||
.group-info { |
.group-info { |
||||
.group-members { |
.group-members { |
||||
padding: 30rpx; |
padding: 30rpx; |
||||
background: white; |
background: white; |
||||
|
|
||||
.member-items { |
.member-items { |
||||
|
display: flex; |
||||
|
flex-wrap: wrap; |
||||
|
|
||||
|
.member-item { |
||||
|
width: 120rpx; |
||||
display: flex; |
display: flex; |
||||
flex-wrap: wrap; |
flex-direction: column; |
||||
|
margin: 8rpx 2rpx; |
||||
|
position: relative; |
||||
|
align-items: center; |
||||
|
padding-right: 5px; |
||||
|
white-space: nowrap; |
||||
|
|
||||
.member-item { |
.member-name { |
||||
width: 120rpx; |
width: 100%; |
||||
display: flex; |
flex: 1; |
||||
flex-direction: column; |
overflow: hidden; |
||||
margin: 8rpx 2rpx; |
text-align: center; |
||||
position: relative; |
|
||||
align-items: center; |
|
||||
padding-right: 5px; |
|
||||
background-color: #fafafa; |
|
||||
white-space: nowrap; |
white-space: nowrap; |
||||
|
padding-top: 8rpx; |
||||
.member-name { |
font-size: $im-font-size-smaller; |
||||
width: 100%; |
|
||||
flex: 1; |
|
||||
font-size: 14px; |
|
||||
overflow: hidden; |
|
||||
text-align: center; |
|
||||
white-space: nowrap; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.invite-btn { |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
align-items: center; |
|
||||
width: 100rpx; |
|
||||
height: 100rpx; |
|
||||
margin: 10rpx; |
|
||||
border: #686868 dashed 2px; |
|
||||
border-radius: 10%; |
|
||||
} |
} |
||||
} |
} |
||||
|
|
||||
.member-more { |
.invite-btn { |
||||
padding-top: 30rpx; |
display: flex; |
||||
text-align: center; |
justify-content: center; |
||||
font-size: 32rpx; |
align-items: center; |
||||
color: #333; |
width: 86rpx; |
||||
|
height: 86rpx; |
||||
|
margin: 10rpx; |
||||
|
border: $im-border solid 2rpx; |
||||
|
border-radius: 10%; |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
|
.member-more { |
||||
.group-detail { |
padding-top: 24rpx; |
||||
margin-top: 30rpx; |
text-align: center; |
||||
padding: 30rpx; |
font-size: $im-font-size-small; |
||||
background: white; |
color: $im-text-color-lighter; |
||||
|
|
||||
.detail-text{ |
|
||||
font-size: 28rpx; |
|
||||
font-weight: 600; |
|
||||
} |
|
||||
|
|
||||
.group-edit { |
|
||||
padding-top: 30rpx; |
|
||||
text-align: center; |
|
||||
font-size: 32rpx; |
|
||||
color: #333; |
|
||||
} |
|
||||
} |
} |
||||
|
} |
||||
|
|
||||
|
|
||||
.btn-group { |
.group-detail { |
||||
margin-top: 30rpx; |
margin-top: 30rpx; |
||||
padding: 30rpx; |
padding: 20rpx 20rpx; |
||||
background: white; |
background: white; |
||||
|
|
||||
|
|
||||
.btn { |
.detail-text { |
||||
margin: 10rpx; |
font-size: $im-font-size; |
||||
} |
|
||||
} |
} |
||||
|
|
||||
|
.group-edit { |
||||
|
padding-top: 30rpx; |
||||
|
text-align: center; |
||||
|
font-size: $im-font-size-small; |
||||
|
color: $im-text-color-lighter; |
||||
|
} |
||||
} |
} |
||||
|
} |
||||
</style> |
</style> |
||||
@ -1,160 +1,165 @@ |
|||||
<template> |
<template> |
||||
<view class="page group-invite"> |
<view class="page group-invite"> |
||||
<view class="search-bar"> |
<view class="nav-bar"> |
||||
<uni-search-bar v-model="searchText" radius="100" cancelButton="none" placeholder="输入好友昵称搜索"></uni-search-bar> |
<view class="nav-search"> |
||||
|
<uni-search-bar v-model="searchText" radius="100" cancelButton="none" |
||||
|
placeholder="输入好友昵称搜索"></uni-search-bar> |
||||
|
</view> |
||||
</view> |
</view> |
||||
<view class="friend-items"> |
<view class="friend-items"> |
||||
<scroll-view class="scroll-bar" scroll-with-animation="true" scroll-y="true"> |
<scroll-view class="scroll-bar" scroll-with-animation="true" scroll-y="true"> |
||||
<view v-for="friend in friendItems" v-show="!searchText || friend.nickName.includes(searchText)" |
<view v-for="friend in friendItems" v-show="!searchText || friend.nickName.includes(searchText)" |
||||
:key="friend.id"> |
:key="friend.id"> |
||||
<view class="friend-item" @click="onSwitchChecked(friend)"> |
<view class="friend-item" @click="onSwitchChecked(friend)" |
||||
<head-image :name="friend.nickName" |
:class="{ checked: friend.checked, disabled: friend.disabled }"> |
||||
:online="friend.online" :url="friend.headImage" |
<head-image :name="friend.nickName" :online="friend.online" |
||||
:size="100"></head-image> |
:url="friend.headImage"></head-image> |
||||
|
<view class="friend-name">{{ friend.nickName }}</view> |
||||
<view class="friend-name">{{ friend.nickName}}</view> |
|
||||
<view class="friend-checked"> |
|
||||
<radio :checked="friend.checked" :disabled="friend.disabled" @click.stop="onSwitchChecked(friend)"/> |
|
||||
</view> |
|
||||
</view> |
</view> |
||||
</view> |
</view> |
||||
</scroll-view> |
</scroll-view> |
||||
</view> |
</view> |
||||
<view> |
<button class="bottom-btn" type="primary" :disabled="inviteSize == 0" |
||||
<button type="primary" :disabled="inviteSize==0" @click="onInviteFriends()">邀请({{inviteSize}}) </button> |
@click="onInviteFriends()">邀请({{ inviteSize }}) </button> |
||||
</view> |
|
||||
</view> |
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
export default { |
export default { |
||||
data() { |
data() { |
||||
return { |
return { |
||||
groupId: null, |
groupId: null, |
||||
searchText: "", |
searchText: "", |
||||
groupMembers: [], |
groupMembers: [], |
||||
friendItems: [] |
friendItems: [] |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
onInviteFriends() { |
||||
|
let inviteVo = { |
||||
|
groupId: this.groupId, |
||||
|
friendIds: [] |
||||
} |
} |
||||
}, |
this.friendItems.forEach((f) => { |
||||
methods: { |
if (f.checked && !f.disabled) { |
||||
onInviteFriends() { |
inviteVo.friendIds.push(f.id); |
||||
let inviteVo = { |
|
||||
groupId: this.groupId, |
|
||||
friendIds: [] |
|
||||
} |
|
||||
this.friendItems.forEach((f) => { |
|
||||
if (f.checked && !f.disabled) { |
|
||||
inviteVo.friendIds.push(f.id); |
|
||||
} |
|
||||
}) |
|
||||
if (inviteVo.friendIds.length > 0) { |
|
||||
this.$http({ |
|
||||
url: "/group/invite", |
|
||||
method: 'POST', |
|
||||
data: inviteVo |
|
||||
}).then(() => { |
|
||||
uni.showToast({ |
|
||||
title: "邀请成功", |
|
||||
icon: 'none' |
|
||||
}) |
|
||||
setTimeout(() => { |
|
||||
// 回退并刷新 |
|
||||
let pages = getCurrentPages(); |
|
||||
let prevPage = pages[pages.length - 2]; |
|
||||
prevPage.$vm.loadGroupMembers(); |
|
||||
uni.navigateBack(); |
|
||||
}, 1000); |
|
||||
|
|
||||
}) |
|
||||
} |
} |
||||
}, |
}) |
||||
onShowUserInfo(userId) { |
if (inviteVo.friendIds.length > 0) { |
||||
uni.navigateTo({ |
|
||||
url: "/pages/common/user-info?id=" + userId |
|
||||
}) |
|
||||
}, |
|
||||
onSwitchChecked(friend) { |
|
||||
if (!friend.disabled) { |
|
||||
friend.checked = !friend.checked; |
|
||||
} |
|
||||
}, |
|
||||
initFriendItems() { |
|
||||
this.friendItems = []; |
|
||||
let friends = this.friendStore.friends; |
|
||||
friends.forEach((f => { |
|
||||
let item = { |
|
||||
id: f.id, |
|
||||
headImage: f.headImage, |
|
||||
nickName: f.nickName, |
|
||||
online: f.online |
|
||||
} |
|
||||
item.disabled = this.isGroupMember(f.id); |
|
||||
item.checked = item.disabled; |
|
||||
this.friendItems.push(item); |
|
||||
})) |
|
||||
}, |
|
||||
loadGroupMembers(id) { |
|
||||
this.$http({ |
this.$http({ |
||||
url: `/group/members/${id}`, |
url: "/group/invite", |
||||
method: "GET" |
method: 'POST', |
||||
}).then((members) => { |
data: inviteVo |
||||
this.groupMembers = members.filter(m => !m.quit); |
}).then(() => { |
||||
this.initFriendItems(); |
uni.showToast({ |
||||
}) |
title: "邀请成功", |
||||
}, |
icon: 'none' |
||||
|
}) |
||||
|
setTimeout(() => { |
||||
|
// 回退并刷新 |
||||
|
let pages = getCurrentPages(); |
||||
|
let prevPage = pages[pages.length - 2]; |
||||
|
prevPage.$vm.loadGroupMembers(); |
||||
|
uni.navigateBack(); |
||||
|
}, 1000); |
||||
|
|
||||
isGroupMember(id) { |
}) |
||||
return this.groupMembers.some(m => m.userId == id); |
|
||||
} |
} |
||||
}, |
}, |
||||
computed: { |
onShowUserInfo(userId) { |
||||
inviteSize() { |
uni.navigateTo({ |
||||
return this.friendItems.filter(f => !f.disabled && f.checked).length; |
url: "/pages/common/user-info?id=" + userId |
||||
|
}) |
||||
|
}, |
||||
|
onSwitchChecked(friend) { |
||||
|
if (!friend.disabled) { |
||||
|
friend.checked = !friend.checked; |
||||
} |
} |
||||
}, |
}, |
||||
onLoad(options) { |
initFriendItems() { |
||||
this.groupId = options.id; |
this.friendItems = []; |
||||
this.loadGroupMembers(options.id); |
let friends = this.friendStore.friends; |
||||
|
friends.forEach((f => { |
||||
|
let item = { |
||||
|
id: f.id, |
||||
|
headImage: f.headImage, |
||||
|
nickName: f.nickName, |
||||
|
online: f.online |
||||
|
} |
||||
|
item.disabled = this.isGroupMember(f.id); |
||||
|
item.checked = item.disabled; |
||||
|
this.friendItems.push(item); |
||||
|
})) |
||||
|
}, |
||||
|
loadGroupMembers(id) { |
||||
|
this.$http({ |
||||
|
url: `/group/members/${id}`, |
||||
|
method: "GET" |
||||
|
}).then((members) => { |
||||
|
this.groupMembers = members.filter(m => !m.quit); |
||||
|
this.initFriendItems(); |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
isGroupMember(id) { |
||||
|
return this.groupMembers.some(m => m.userId == id); |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
inviteSize() { |
||||
|
return this.friendItems.filter(f => !f.disabled && f.checked).length; |
||||
} |
} |
||||
|
}, |
||||
|
onLoad(options) { |
||||
|
this.groupId = options.id; |
||||
|
this.loadGroupMembers(options.id); |
||||
} |
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style lang="scss" scoped> |
<style lang="scss" scoped> |
||||
.group-invite { |
.group-invite { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
|
||||
|
.friend-items { |
||||
position: relative; |
position: relative; |
||||
border: #dddddd solid 1px; |
flex: 1; |
||||
display: flex; |
overflow: hidden; |
||||
flex-direction: column; |
|
||||
|
|
||||
.friend-items { |
.friend-item { |
||||
|
height: 120rpx; |
||||
|
display: flex; |
||||
|
margin-bottom: 1rpx; |
||||
position: relative; |
position: relative; |
||||
flex: 1; |
padding: 0 30rpx; |
||||
overflow: hidden; |
align-items: center; |
||||
|
background-color: white; |
||||
|
white-space: nowrap; |
||||
|
|
||||
.friend-item { |
&.disabled { |
||||
height: 120rpx; |
background-color: $im-bg-active !important; |
||||
display: flex; |
|
||||
margin-bottom: 1rpx; |
|
||||
position: relative; |
|
||||
padding: 0 30rpx ; |
|
||||
align-items: center; |
|
||||
background-color: white; |
|
||||
white-space: nowrap; |
|
||||
|
|
||||
.friend-name { |
|
||||
flex:1; |
|
||||
padding-left: 20rpx; |
|
||||
font-size: 30rpx; |
|
||||
font-weight: 600; |
|
||||
line-height: 60rpx; |
|
||||
white-space: nowrap; |
|
||||
overflow: hidden; |
|
||||
} |
|
||||
} |
} |
||||
|
|
||||
.scroll-bar { |
&.checked { |
||||
height: 100%; |
background-color: $im-color-primary-light-9; |
||||
|
} |
||||
|
|
||||
|
.friend-name { |
||||
|
flex: 1; |
||||
|
padding-left: 20rpx; |
||||
|
font-size: 30rpx; |
||||
|
font-weight: 600; |
||||
|
line-height: 60rpx; |
||||
|
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
|
.scroll-bar { |
||||
|
height: 100%; |
||||
|
} |
||||
} |
} |
||||
|
} |
||||
</style> |
</style> |
||||
@ -1,79 +1,81 @@ |
|||||
<template> |
<template> |
||||
<view class="page mine-edit"> |
<view class="page mine-edit"> |
||||
<uni-forms ref="form" :modelValue="userInfo" label-position="top" |
<nav-bar back>修改我的信息</nav-bar> |
||||
label-width="100%"> |
<uni-card :is-shadow="false" is-full :border="false"> |
||||
<uni-forms-item label="头像:" name="headImage"> |
<uni-forms ref="form" :modelValue="userInfo" label-position="top" label-width="100%"> |
||||
<image-upload :onSuccess="onUnloadImageSuccess"> |
<uni-forms-item name="headImage" class="avatar"> |
||||
<image :src="userInfo.headImageThumb" class="head-image"></image> |
<image-upload :onSuccess="onUnloadImageSuccess"> |
||||
</image-upload> |
<image :src="userInfo.headImageThumb" class="head-image"></image> |
||||
</uni-forms-item> |
</image-upload> |
||||
<uni-forms-item label="用户名:" name="userName"> |
</uni-forms-item> |
||||
<uni-easyinput type="text" v-model="userInfo.userName" :disabled="true" /> |
<uni-forms-item label="用户名" name="userName"> |
||||
</uni-forms-item> |
<uni-easyinput type="text" v-model="userInfo.userName" :disabled="true" /> |
||||
<uni-forms-item label="昵称:" name="nickName"> |
</uni-forms-item> |
||||
<uni-easyinput v-model="userInfo.nickName" type="text" :placeholder="userInfo.userName" /> |
<uni-forms-item label="昵称" name="nickName"> |
||||
</uni-forms-item> |
<uni-easyinput v-model="userInfo.nickName" type="text" :placeholder="userInfo.userName" /> |
||||
<uni-forms-item label="性别:" name="sex"> |
</uni-forms-item> |
||||
<radio-group @change="onSexchange"> |
<uni-forms-item label="性别" name="sex"> |
||||
<label class="radio"><radio value="0" :checked="userInfo.sex==0" />男</label> |
<uni-data-checkbox v-model="userInfo.sex" |
||||
<label class="radio"><radio value="1" :checked="userInfo.sex==1" />女 </label> |
:localdata="[{ text: '男', value: 0 }, { text: '女', value: 1 }]"></uni-data-checkbox> |
||||
</radio-group> |
</uni-forms-item> |
||||
</uni-forms-item> |
<uni-forms-item label="签名" name="signature"> |
||||
<uni-forms-item label="签名:" name="signature"> |
<uni-easyinput type="textarea" v-model="userInfo.signature" placeholder="编辑个性标签,展示我的独特态度" /> |
||||
<uni-easyinput type="textarea" v-model="userInfo.signature" placeholder="编辑个性标签,展示我的独特态度" /> |
</uni-forms-item> |
||||
</uni-forms-item> |
</uni-forms> |
||||
</uni-forms> |
</uni-card> |
||||
<button type="primary" @click="onSubmit()">提交</button> |
<button type="primary" class="bottom-btn" @click="onSubmit()">提交</button> |
||||
</view> |
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
export default { |
export default { |
||||
data() { |
data() { |
||||
return { |
return { |
||||
userInfo: {} |
userInfo: {} |
||||
} |
} |
||||
|
}, |
||||
|
methods: { |
||||
|
onSexchange(e) { |
||||
|
this.userInfo.sex = e.detail.value; |
||||
}, |
}, |
||||
methods:{ |
onUnloadImageSuccess(file, res) { |
||||
onSexchange(e){ |
this.userInfo.headImage = res.data.originUrl; |
||||
this.userInfo.sex=e.detail.value; |
this.userInfo.headImageThumb = res.data.thumbUrl; |
||||
}, |
|
||||
onUnloadImageSuccess(file, res) { |
|
||||
this.userInfo.headImage = res.data.originUrl; |
|
||||
this.userInfo.headImageThumb = res.data.thumbUrl; |
|
||||
}, |
|
||||
onSubmit(){ |
|
||||
this.$http({ |
|
||||
url: "/user/update", |
|
||||
method: "PUT", |
|
||||
data: this.userInfo |
|
||||
}).then(()=>{ |
|
||||
this.userStore.setUserInfo(this.userInfo); |
|
||||
uni.showToast({ |
|
||||
title:"修改成功", |
|
||||
icon: 'none' |
|
||||
}); |
|
||||
setTimeout(()=>{ |
|
||||
uni.navigateBack(); |
|
||||
},1000); |
|
||||
}) |
|
||||
} |
|
||||
}, |
}, |
||||
onLoad() { |
onSubmit() { |
||||
// 深拷贝一份数据 |
this.$http({ |
||||
let mine = this.userStore.userInfo; |
url: "/user/update", |
||||
this.userInfo = JSON.parse(JSON.stringify(mine)); |
method: "PUT", |
||||
|
data: this.userInfo |
||||
|
}).then(() => { |
||||
|
this.userStore.setUserInfo(this.userInfo); |
||||
|
uni.showToast({ |
||||
|
title: "修改成功", |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
setTimeout(() => { |
||||
|
uni.navigateBack(); |
||||
|
}, 1000); |
||||
|
}) |
||||
} |
} |
||||
|
}, |
||||
|
onLoad() { |
||||
|
// 深拷贝一份数据 |
||||
|
let mine = this.userStore.userInfo; |
||||
|
this.userInfo = JSON.parse(JSON.stringify(mine)); |
||||
} |
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style scoped lang="scss"> |
<style scoped lang="scss"> |
||||
.mine-edit { |
.mine-edit { |
||||
padding: 20rpx; |
.head-image { |
||||
|
width: 200rpx; |
||||
.head-image { |
height: 200rpx; |
||||
width: 200rpx; |
|
||||
height: 200rpx; |
|
||||
} |
|
||||
} |
} |
||||
</style> |
} |
||||
|
|
||||
|
.avatar { |
||||
|
margin-top: -30px; |
||||
|
} |
||||
|
</style> |
||||
|
|||||
@ -1,104 +1,101 @@ |
|||||
<template> |
<template> |
||||
<view class="page mine-password"> |
<view class="page mine-password"> |
||||
<uni-forms ref="form" :modelValue="formData" label-position="top" label-width="100%" > |
<nav-bar back>修改密码</nav-bar> |
||||
<uni-forms-item label="原密码:" name="oldPassword"> |
<uni-card :is-shadow="false" is-full :border="false"> |
||||
<uni-easyinput type="password" v-model="formData.oldPassword" /> |
<uni-forms ref="form" :modelValue="formData" label-position="top" label-width="100%"> |
||||
</uni-forms-item> |
<uni-forms-item label="原密码" name="oldPassword"> |
||||
<uni-forms-item label="新密码:" name="newPassword"> |
<uni-easyinput type="password" v-model="formData.oldPassword" /> |
||||
<uni-easyinput type="password" v-model="formData.newPassword" /> |
</uni-forms-item> |
||||
</uni-forms-item> |
<uni-forms-item label="新密码" name="newPassword"> |
||||
<uni-forms-item label="确认密码:" name="confirmPassword"> |
<uni-easyinput type="password" v-model="formData.newPassword" /> |
||||
<uni-easyinput type="password" v-model="formData.confirmPassword" /> |
</uni-forms-item> |
||||
</uni-forms-item> |
<uni-forms-item label="确认密码" name="confirmPassword"> |
||||
<button type="primary" @click="onSubmit()">提交</button> |
<uni-easyinput type="password" v-model="formData.confirmPassword" /> |
||||
</uni-forms> |
</uni-forms-item> |
||||
|
</uni-forms> |
||||
|
</uni-card> |
||||
|
<button class="bottom-btn" type="primary" @click="onSubmit()">提交</button> |
||||
</view> |
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
export default { |
export default { |
||||
data() { |
data() { |
||||
return { |
return { |
||||
formData: { |
formData: { |
||||
oldPassword: "", |
oldPassword: "", |
||||
newPassword: "", |
newPassword: "", |
||||
confirmPassword: "" |
confirmPassword: "" |
||||
|
}, |
||||
|
rules: { |
||||
|
oldPassword: { |
||||
|
rules: [{ |
||||
|
required: true, |
||||
|
errorMessage: '请输入原密码', |
||||
|
}] |
||||
}, |
}, |
||||
rules: { |
newPassword: { |
||||
oldPassword: { |
rules: [{ |
||||
rules: [{ |
required: true, |
||||
required: true, |
errorMessage: '请输入新密码', |
||||
errorMessage: '请输入原密码', |
}, { |
||||
}] |
validateFunction: function (rule, value, data, callback) { |
||||
}, |
if (data.confirmPassword != data.newPassword) { |
||||
newPassword: { |
callback("两次输入的密码不一致"); |
||||
rules: [{ |
} |
||||
required: true, |
if (data.newPassword == data.oldPassword) { |
||||
errorMessage: '请输入新密码', |
callback("新密码不能和原密码一致"); |
||||
}, { |
|
||||
validateFunction: function(rule, value, data, callback) { |
|
||||
if (data.confirmPassword != data.newPassword) { |
|
||||
callback("两次输入的密码不一致"); |
|
||||
} |
|
||||
if (data.newPassword == data.oldPassword) { |
|
||||
callback("新密码不能和原密码一致"); |
|
||||
} |
|
||||
return true; |
|
||||
} |
} |
||||
}] |
return true; |
||||
}, |
} |
||||
confirmPassword: { |
}] |
||||
rules: [{ |
}, |
||||
required: true, |
confirmPassword: { |
||||
errorMessage: '请输入确认密码', |
rules: [{ |
||||
}, { |
required: true, |
||||
validateFunction: function(rule, value, data, callback) { |
errorMessage: '请输入确认密码', |
||||
if (data.confirmPassword != data.newPassword) { |
}, { |
||||
callback("两次输入的密码不一致"); |
validateFunction: function (rule, value, data, callback) { |
||||
} |
if (data.confirmPassword != data.newPassword) { |
||||
|
callback("两次输入的密码不一致"); |
||||
return true; |
|
||||
} |
} |
||||
}] |
|
||||
} |
|
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
}] |
||||
} |
} |
||||
|
|
||||
} |
} |
||||
}, |
} |
||||
methods: { |
}, |
||||
onSubmit() { |
methods: { |
||||
this.$refs.form.validate().then(res => { |
onSubmit() { |
||||
this.$http({ |
this.$refs.form.validate().then(res => { |
||||
url: "/modifyPwd", |
this.$http({ |
||||
method: "PUT", |
url: "/modifyPwd", |
||||
data: this.formData |
method: "PUT", |
||||
}).then((res) => { |
data: this.formData |
||||
uni.showToast({ |
}).then((res) => { |
||||
title: "修改密码成功", |
uni.showToast({ |
||||
icon: 'none' |
title: "修改密码成功", |
||||
}) |
icon: 'none' |
||||
setTimeout(()=>{ |
|
||||
uni.navigateBack(); |
|
||||
},1000); |
|
||||
}) |
}) |
||||
}).catch(err => { |
setTimeout(() => { |
||||
console.log('表单错误信息:', err); |
uni.navigateBack(); |
||||
|
}, 1000); |
||||
}) |
}) |
||||
|
}).catch(err => { |
||||
|
console.log('表单错误信息:', err); |
||||
|
}) |
||||
|
|
||||
} |
|
||||
}, |
|
||||
onReady() { |
|
||||
// 需要在onReady中设置规则 |
|
||||
this.$refs.form.setRules(this.rules) |
|
||||
} |
} |
||||
|
}, |
||||
|
onReady() { |
||||
|
// 需要在onReady中设置规则 |
||||
|
this.$refs.form.setRules(this.rules) |
||||
} |
} |
||||
</script> |
|
||||
|
|
||||
<style scoped lang="scss"> |
} |
||||
.mine-password { |
</script> |
||||
padding: 20rpx; |
|
||||
|
|
||||
} |
<style scoped lang="scss"></style> |
||||
</style> |
|
||||
@ -1,127 +1,149 @@ |
|||||
<template> |
<template> |
||||
<view class="page mine"> |
<view class="page mine"> |
||||
<view class="content" @click="onModifyInfo()"> |
<nav-bar>我的</nav-bar> |
||||
<head-image :name="userInfo.nickName" |
<uni-card :is-shadow="false" is-full :border="false"> |
||||
:url="userInfo.headImage" |
<view class="content" @click="onModifyInfo()"> |
||||
:size="160"></head-image> |
<head-image :name="userInfo.nickName" :url="userInfo.headImage" :size="160"></head-image> |
||||
<view class="info-item"> |
<view class="info-item"> |
||||
<view class="info-primary"> |
<view class="info-primary"> |
||||
<text class="info-username"> |
<text class="info-username"> |
||||
{{userInfo.userName}} |
{{ userInfo.userName }} |
||||
</text> |
</text> |
||||
<text v-show="userInfo.sex==0" class="iconfont icon-man" |
<text v-show="userInfo.sex == 0" class="iconfont icon-man" color="darkblue"></text> |
||||
color="darkblue"></text> |
<text v-show="userInfo.sex == 1" class="iconfont icon-girl" color="darkred"></text> |
||||
<text v-show="userInfo.sex==1" class="iconfont icon-girl" |
</view> |
||||
color="darkred"></text> |
<view class="info-text"> |
||||
|
<text class="label-text"> |
||||
|
昵称: |
||||
|
</text> |
||||
|
<text class="content-text"> |
||||
|
{{ userInfo.nickName }} |
||||
|
</text> |
||||
|
</view> |
||||
|
<view class="info-text"> |
||||
|
<view> |
||||
|
<text class="label-text"> |
||||
|
签名: |
||||
|
</text> |
||||
|
<text class="content-text"> |
||||
|
{{ userInfo.signature }} |
||||
|
</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="info-arrow"> |
||||
|
|
||||
</view> |
</view> |
||||
<text> |
|
||||
昵称 :{{userInfo.nickName}} |
|
||||
</text> |
|
||||
<text> |
|
||||
签名 :{{userInfo.signature}} |
|
||||
</text> |
|
||||
</view> |
</view> |
||||
<view class="info-arrow">></view> |
</uni-card> |
||||
</view> |
<bar-group> |
||||
<view class="line"></view> |
<arrow-bar title="修改密码" @click="onModifyPassword()"></arrow-bar> |
||||
<view class="btn-group"> |
</bar-group> |
||||
<button class="btn" type="primary" @click="onModifyPassword()">修改密码</button> |
<bar-group> |
||||
<button class="btn" type="warn" @click="onQuit()">退出</button> |
<btn-bar title="退出登陆" type="danger" @click="onQuit()"></btn-bar> |
||||
</view> |
</bar-group> |
||||
</view> |
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
export default { |
export default { |
||||
data() { |
data() { |
||||
return {} |
return {} |
||||
|
}, |
||||
|
methods: { |
||||
|
onModifyInfo() { |
||||
|
uni.navigateTo({ |
||||
|
url: "/pages/mine/mine-edit" |
||||
|
}) |
||||
}, |
}, |
||||
methods: { |
onModifyPassword() { |
||||
onModifyInfo() { |
uni.navigateTo({ |
||||
uni.navigateTo({ |
url: "/pages/mine/mine-password" |
||||
url: "/pages/mine/mine-edit" |
}) |
||||
}) |
|
||||
}, |
|
||||
onModifyPassword() { |
|
||||
uni.navigateTo({ |
|
||||
url: "/pages/mine/mine-password" |
|
||||
}) |
|
||||
}, |
|
||||
onQuit() { |
|
||||
uni.showModal({ |
|
||||
title: '确认退出?', |
|
||||
success: (res) => { |
|
||||
if (res.confirm) { |
|
||||
console.log(getApp()) |
|
||||
getApp().$vm.exit() |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
}, |
}, |
||||
computed: { |
onQuit() { |
||||
userInfo() { |
uni.showModal({ |
||||
return this.userStore.userInfo; |
title: '确认退出?', |
||||
} |
success: (res) => { |
||||
|
if (res.confirm) { |
||||
|
console.log(getApp()) |
||||
|
getApp().$vm.exit() |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
} |
} |
||||
|
}, |
||||
|
computed: { |
||||
|
userInfo() { |
||||
|
return this.userStore.userInfo; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
} |
} |
||||
</script> |
</script> |
||||
|
|
||||
<style scoped lang="scss"> |
<style scoped lang="scss"> |
||||
.mine { |
.mine { |
||||
.content { |
.content { |
||||
height: 200rpx; |
//height: 200rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
padding: 20rpx; |
||||
|
|
||||
|
.info-item { |
||||
display: flex; |
display: flex; |
||||
align-items: center; |
align-items: flex-start; |
||||
justify-content: space-between; |
flex-direction: column; |
||||
padding: 20rpx; |
padding-left: 40rpx; |
||||
|
flex: 1; |
||||
|
|
||||
.info-item { |
.info-text { |
||||
display: flex; |
line-height: 1.5; |
||||
align-items: flex-start; |
//margin-bottom: 10rpx; |
||||
flex-direction: column; |
|
||||
padding-left: 40rpx; |
|
||||
flex: 1; |
|
||||
|
|
||||
.info-primary { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
.info-username { |
|
||||
font-size: 40rpx; |
|
||||
font-weight: 600; |
|
||||
} |
|
||||
|
|
||||
.icon-man { |
|
||||
color: darkblue; |
|
||||
} |
|
||||
|
|
||||
.icon-girl { |
|
||||
color: darkred; |
|
||||
} |
|
||||
} |
|
||||
} |
} |
||||
|
|
||||
.info-arrow { |
.label-text { |
||||
width: 50rpx; |
font-size: $im-font-size-small; |
||||
font-size: 30rpx; |
color: $im-text-color-light; |
||||
font-weight: 600; |
|
||||
} |
} |
||||
} |
|
||||
|
|
||||
.line { |
.content-text { |
||||
margin: 20rpx; |
font-size: $im-font-size-small; |
||||
border-bottom: 1px solid #aaaaaa; |
color: $im-text-color-light; |
||||
} |
} |
||||
|
|
||||
|
.info-primary { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin-bottom: 10rpx; |
||||
|
|
||||
|
.info-username { |
||||
|
font-size: $im-font-size-large; |
||||
|
font-weight: 600; |
||||
|
} |
||||
|
|
||||
.btn-group { |
.icon-man { |
||||
margin: 100rpx; |
color: $im-text-color; |
||||
|
font-size: $im-font-size-large; |
||||
|
padding-left: 10rpx; |
||||
|
} |
||||
|
|
||||
.btn { |
.icon-girl { |
||||
margin-top: 20rpx; |
color: darkred; |
||||
|
} |
||||
} |
} |
||||
|
} |
||||
|
|
||||
|
.info-arrow { |
||||
|
width: 50rpx; |
||||
|
font-size: 30rpx; |
||||
|
position: relative; |
||||
|
left: 30rpx; |
||||
} |
} |
||||
} |
} |
||||
</style> |
|
||||
|
} |
||||
|
</style> |
||||
|
|||||
|
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 238 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 205 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 839 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 141 KiB |
@ -1,115 +1,24 @@ |
|||||
<template> |
<template> |
||||
<div id="app"> |
<div id="app"> |
||||
<router-view></router-view> |
<router-view></router-view> |
||||
</div> |
</div> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
export default { |
export default { |
||||
name: 'App', |
name: 'App', |
||||
components: {} |
components: {} |
||||
} |
} |
||||
</script> |
</script> |
||||
|
|
||||
<style lang="scss"> |
<style lang="scss"> |
||||
@import './assets/style/global.css'; |
|
||||
|
|
||||
#app { |
#app { |
||||
font-family: 'Avenir', Helvetica, Arial, sans-serif; |
-webkit-font-smoothing: antialiased; |
||||
-webkit-font-smoothing: antialiased; |
-moz-osx-font-smoothing: grayscale; |
||||
-moz-osx-font-smoothing: grayscale; |
position: absolute; |
||||
position: absolute; |
height: 100%; |
||||
height: 100%; |
width: 100%; |
||||
width: 100%; |
color: var(--im-text-color); |
||||
} |
font-family: var(--im-font-family); |
||||
|
} |
||||
.el-message { |
</style> |
||||
z-index: 99999999 !important; |
|
||||
} |
|
||||
|
|
||||
.el-scrollbar__thumb { |
|
||||
background-color: #A0A8AF !important; |
|
||||
} |
|
||||
|
|
||||
.el-dialog { |
|
||||
border-radius: 8px !important; |
|
||||
overflow: hidden !important; |
|
||||
} |
|
||||
|
|
||||
.el-dialog__header { |
|
||||
background-color: #5870e6 !important; |
|
||||
|
|
||||
} |
|
||||
|
|
||||
.el-dialog__title { |
|
||||
color: #f8f8f8 !important; |
|
||||
} |
|
||||
|
|
||||
.el-dialog__close { |
|
||||
color: white !important; |
|
||||
font-size: 20px; |
|
||||
} |
|
||||
|
|
||||
.el-checkbox__inner { |
|
||||
border-radius: 50% !important; |
|
||||
} |
|
||||
|
|
||||
.el-input__inner { |
|
||||
border-radius: 5px !important; |
|
||||
border: #587FF0 1px solid !important; |
|
||||
} |
|
||||
|
|
||||
.el-textarea__inner{ |
|
||||
border-radius: 5px !important; |
|
||||
border: #587FF0 1px solid !important; |
|
||||
} |
|
||||
|
|
||||
.el-icon-search { |
|
||||
color:#587FF0 !important; |
|
||||
} |
|
||||
|
|
||||
.el-button--primary { |
|
||||
background-color: #687Ff0 !important; |
|
||||
border-color: #687Ff0 !important; |
|
||||
} |
|
||||
|
|
||||
.el-button--success { |
|
||||
background-color: #4cae1b !important; |
|
||||
border-color: #4cae1b !important; |
|
||||
} |
|
||||
.el-button--danger { |
|
||||
background-color: #ea4949 !important; |
|
||||
border-color: #ea4949 !important; |
|
||||
} |
|
||||
|
|
||||
.el-button { |
|
||||
padding: 8px 15px !important; |
|
||||
} |
|
||||
|
|
||||
.el-checkbox { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
|
|
||||
//修改选中框的大小 |
|
||||
.el-checkbox__inner { |
|
||||
width: 20px; |
|
||||
height: 20px; |
|
||||
|
|
||||
//修改选中框中的对勾的大小和位置 |
|
||||
&::after { |
|
||||
height: 12px; |
|
||||
left: 7px; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
//修改点击文字颜色不变 |
|
||||
.el-checkbox__input.is-checked+.el-checkbox__label { |
|
||||
color: #333333; |
|
||||
} |
|
||||
|
|
||||
.el-checkbox__label { |
|
||||
line-height: 20px; |
|
||||
padding-left: 8px; |
|
||||
} |
|
||||
} |
|
||||
</style> |
|
||||
|
|||||
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
@ -0,0 +1,112 @@ |
|||||
|
/* 改变 icon 字体路径变量,必需 */ |
||||
|
$--font-path: '~element-ui/lib/theme-chalk/fonts'; |
||||
|
|
||||
|
// 文字 |
||||
|
$--font-family: Microsoft YaHei, 'Avenir', Helvetica, Arial, sans-serif; |
||||
|
@import "thems"; |
||||
|
@import "~element-ui/packages/theme-chalk/src/index"; |
||||
|
|
||||
|
.el-message { |
||||
|
z-index: 99999999 !important; |
||||
|
background: #fff !important; |
||||
|
box-shadow: 0 4px 12px 0 rgb(0 0 0 / 15%); |
||||
|
border: none !important; |
||||
|
min-width: unset !important; |
||||
|
border-radius: 3px !important; |
||||
|
padding: 14px 18px 14px 16px !important; |
||||
|
|
||||
|
.el-message__content { |
||||
|
color: #000 !important; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.el-scrollbar__thumb { |
||||
|
background-color: #A0A8AF !important; |
||||
|
} |
||||
|
|
||||
|
.el-dialog__title { |
||||
|
font-size: var(--im-font-size-larger); |
||||
|
color: var(--im-text-color); |
||||
|
} |
||||
|
|
||||
|
.el-dialog__header { |
||||
|
padding: 12px 18px !important; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
.el-dialog__headerbtn { |
||||
|
top: 15px; |
||||
|
right: 20px; |
||||
|
font-size: 18px; |
||||
|
} |
||||
|
|
||||
|
.el-checkbox__inner { |
||||
|
border-radius: 50% !important; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
.el-button--success { |
||||
|
//background-color: #688758 !important; |
||||
|
//border-color: #4cae1b !important; |
||||
|
} |
||||
|
|
||||
|
.el-button--danger { |
||||
|
//background-color: #ea4949 !important; |
||||
|
//border-color: #ea4949 !important; |
||||
|
} |
||||
|
|
||||
|
.el-button { |
||||
|
padding: 8px 15px !important; |
||||
|
} |
||||
|
|
||||
|
.el-checkbox { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
|
||||
|
//修改选中框的大小 |
||||
|
.el-checkbox__inner { |
||||
|
width: 16px; |
||||
|
height: 16px; |
||||
|
|
||||
|
//修改选中框中的对勾的大小和位置 |
||||
|
&::after { |
||||
|
height: 7px; |
||||
|
left: 5px; |
||||
|
top: 2px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 修改点击文字颜色不变 |
||||
|
.el-checkbox__input.is-checked + .el-checkbox__label { |
||||
|
color: #333333; |
||||
|
} |
||||
|
|
||||
|
.el-checkbox__label { |
||||
|
line-height: 20px; |
||||
|
padding-left: 8px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.el-form-item { |
||||
|
margin-bottom: 15px !important; |
||||
|
} |
||||
|
|
||||
|
.el-input--small { |
||||
|
font-size: $--font-size-base; |
||||
|
} |
||||
|
|
||||
|
.el-input__inner { |
||||
|
padding: 0 10px; |
||||
|
} |
||||
|
|
||||
|
.el-textarea__inner { |
||||
|
padding: 5px 10px; |
||||
|
font-family: $--font-family; |
||||
|
} |
||||
|
|
||||
|
.el-tag--mini { |
||||
|
height: 18px; |
||||
|
padding: 0 2px; |
||||
|
line-height: 16px; |
||||
|
border-radius: 2px; |
||||
|
} |
||||
@ -1,43 +0,0 @@ |
|||||
@charset "UTF-8"; |
|
||||
|
|
||||
html { |
|
||||
height: 100%; |
|
||||
overflow: hidden; |
|
||||
|
|
||||
} |
|
||||
|
|
||||
body { |
|
||||
height: 100%; |
|
||||
margin: 0; |
|
||||
overflow: hidden; |
|
||||
|
|
||||
} |
|
||||
|
|
||||
section { |
|
||||
height: 100%; |
|
||||
} |
|
||||
|
|
||||
.el-dialog__body{ |
|
||||
padding: 10px 15px !important; |
|
||||
} |
|
||||
|
|
||||
::-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,91 @@ |
|||||
|
@charset "UTF-8"; |
||||
|
@import "element"; |
||||
|
|
||||
|
// im全局样式变量 |
||||
|
:root { |
||||
|
// 主色 |
||||
|
--im-color-primary: #{$--color-primary}; |
||||
|
--im-color-primary-light-1: #{$--color-primary-light-1}; |
||||
|
--im-color-primary-light-2: #{$--color-primary-light-2}; |
||||
|
--im-color-primary-light-3: #{$--color-primary-light-3}; |
||||
|
--im-color-primary-light-4: #{$--color-primary-light-4}; |
||||
|
--im-color-primary-light-5: #{$--color-primary-light-5}; |
||||
|
--im-color-primary-light-6: #{$--color-primary-light-6}; |
||||
|
--im-color-primary-light-7: #{$--color-primary-light-7}; |
||||
|
--im-color-primary-light-8: #{$--color-primary-light-8}; |
||||
|
--im-color-primary-light-9: #{$--color-primary-light-9}; |
||||
|
|
||||
|
--im-color-sucess: #{$--color-success}; |
||||
|
--im-color-warning: #{$--color-warning}; |
||||
|
--im-color-danger: #{$--color-danger}; |
||||
|
--im-color-info: #{$--color-info}; |
||||
|
|
||||
|
// 文字颜色 |
||||
|
--im-text-color: #{$--color-text-regular}; |
||||
|
--im-text-color-light: #999999; |
||||
|
--im-text-color-lighter: #C0C4CC; |
||||
|
|
||||
|
// 文字大小 |
||||
|
--im-font-size: #{$--font-size-base}; |
||||
|
--im-font-size-small: #{$--font-size-small}; |
||||
|
--im-font-size-smaller: #{$--font-size-extra-small}; |
||||
|
--im-font-size-large: #{$--font-size-medium}; |
||||
|
--im-font-size-larger: #{$--font-size-large}; |
||||
|
--im-font-family: #{$--font-family}; |
||||
|
|
||||
|
|
||||
|
// 边框颜色 |
||||
|
--im-border: 1px solid #EBEEF5; |
||||
|
|
||||
|
// 阴影 |
||||
|
--im-box-shadow: #{$--box-shadow-base}; |
||||
|
--im-box-shadow-light: #{$--box-shadow-light}; |
||||
|
--im-box-shadow-lighter: 0px 0px 6px rgba(0, 0, 0, .12); |
||||
|
--im-box-shadow-dark: 0px 16px 48px 16px rgba(0, 0, 0, .08), 0px 12px 32px rgba(0, 0, 0, .12), 0px 8px 16px -8px rgba(0, 0, 0, .16); |
||||
|
|
||||
|
// 背景色 |
||||
|
--im-background: #F3F3F3; |
||||
|
--im-background-active: #F1F1F1; |
||||
|
--im-background-active-dark: #E9E9E9; |
||||
|
} |
||||
|
|
||||
|
html { |
||||
|
height: 100%; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
body { |
||||
|
height: 100%; |
||||
|
margin: 0; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
section { |
||||
|
height: 100%; |
||||
|
} |
||||
|
|
||||
|
.el-dialog__body { |
||||
|
padding: 10px 20px !important; |
||||
|
} |
||||
|
|
||||
|
// 滚动条样式 |
||||
|
::-webkit-scrollbar { |
||||
|
width: 8px; |
||||
|
height: 1px; |
||||
|
} |
||||
|
|
||||
|
::-webkit-scrollbar-thumb { |
||||
|
border-radius: 4px; |
||||
|
background: hsla(0, 0%, 73%, .5); |
||||
|
} |
||||
|
|
||||
|
::-webkit-scrollbar-track { |
||||
|
border-radius: 4px; |
||||
|
} |
||||
|
|
||||
|
.search-input { |
||||
|
.el-input__inner { |
||||
|
border: unset !important; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
// 主题色 |
||||
|
$--color-primary: #2830d3; |
||||
|
//$--color-primary: #687ff0; |
||||
|
//$--color-primary: #096bff; |
||||
|
$--font-size-base: 14px; |
||||
|
$--color-text-regular: #000000; |
||||
@ -1,131 +1,131 @@ |
|||||
<template> |
<template> |
||||
<el-scrollbar v-show="show&&showMembers.length" ref="scrollBox" class="group-member-choose" |
<el-scrollbar v-show="show && showMembers.length" ref="scrollBox" class="group-member-choose" |
||||
:style="{'left':pos.x+'px','top':pos.y-300+'px'}"> |
:style="{ 'left': pos.x + 'px', 'top': pos.y - 300 + 'px' }"> |
||||
<div v-for="(member,idx) in showMembers" :key="member.id"> |
<div v-for="(member, idx) in showMembers" :key="member.id"> |
||||
<chat-group-member :member="member" :height="40" :active='activeIdx==idx' |
<chat-group-member :member="member" :height="40" :active='activeIdx == idx' |
||||
@click.native="onSelectMember(member)"></chat-group-member> |
@click.native="onSelectMember(member)"></chat-group-member> |
||||
</div> |
</div> |
||||
</el-scrollbar> |
</el-scrollbar> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script> |
||||
import ChatGroupMember from "./ChatGroupMember.vue"; |
import ChatGroupMember from "./ChatGroupMember.vue"; |
||||
export default { |
export default { |
||||
name: "chatAtBox", |
name: "chatAtBox", |
||||
components: { |
components: { |
||||
ChatGroupMember |
ChatGroupMember |
||||
|
}, |
||||
|
props: { |
||||
|
searchText: { |
||||
|
type: String, |
||||
|
default: "" |
||||
}, |
}, |
||||
props: { |
ownerId: { |
||||
searchText: { |
type: Number, |
||||
type: String, |
}, |
||||
default: "" |
members: { |
||||
}, |
type: Array |
||||
ownerId: { |
} |
||||
type: Number, |
}, |
||||
|
data() { |
||||
|
return { |
||||
|
show: false, |
||||
|
pos: { |
||||
|
x: 0, |
||||
|
y: 0 |
||||
}, |
}, |
||||
members: { |
activeIdx: 0, |
||||
type: Array |
showMembers: [] |
||||
|
}; |
||||
|
}, |
||||
|
methods: { |
||||
|
init() { |
||||
|
this.$refs.scrollBox.wrap.scrollTop = 0; |
||||
|
this.showMembers = []; |
||||
|
let userId = this.$store.state.userStore.userInfo.id; |
||||
|
let name = "全体成员"; |
||||
|
if (this.ownerId == userId && name.startsWith(this.searchText)) { |
||||
|
this.showMembers.push({ |
||||
|
userId: -1, |
||||
|
showNickName: name |
||||
|
}) |
||||
} |
} |
||||
|
this.members.forEach((m) => { |
||||
|
if (m.userId != userId && !m.quit && m.showNickName.startsWith(this.searchText)) { |
||||
|
this.showMembers.push(m); |
||||
|
} |
||||
|
}) |
||||
|
this.activeIdx = this.showMembers.length > 0 ? 0 : -1; |
||||
}, |
}, |
||||
data() { |
open(pos) { |
||||
return { |
this.show = true; |
||||
show: false, |
this.pos = pos; |
||||
pos: { |
this.init(); |
||||
x: 0, |
|
||||
y: 0 |
|
||||
}, |
|
||||
activeIdx: 0, |
|
||||
showMembers: [] |
|
||||
}; |
|
||||
}, |
}, |
||||
methods: { |
close() { |
||||
init() { |
this.show = false; |
||||
this.$refs.scrollBox.wrap.scrollTop = 0; |
}, |
||||
this.showMembers = []; |
moveUp() { |
||||
let userId = this.$store.state.userStore.userInfo.id; |
if (this.activeIdx > 0) { |
||||
let name = "全体成员"; |
this.activeIdx--; |
||||
if (this.ownerId == userId && name.startsWith(this.searchText)) { |
this.scrollToActive() |
||||
this.showMembers.push({ |
|
||||
userId: -1, |
|
||||
showNickName: name |
|
||||
}) |
|
||||
} |
|
||||
this.members.forEach((m) => { |
|
||||
if (m.userId != userId && !m.quit && m.showNickName.startsWith(this.searchText)) { |
|
||||
this.showMembers.push(m); |
|
||||
} |
|
||||
}) |
|
||||
this.activeIdx = this.showMembers.length > 0 ? 0: -1; |
|
||||
}, |
|
||||
open(pos) { |
|
||||
this.show = true; |
|
||||
this.pos = pos; |
|
||||
this.init(); |
|
||||
}, |
|
||||
close() { |
|
||||
this.show = false; |
|
||||
}, |
|
||||
moveUp() { |
|
||||
if (this.activeIdx > 0) { |
|
||||
this.activeIdx--; |
|
||||
this.scrollToActive() |
|
||||
} |
|
||||
}, |
|
||||
moveDown() { |
|
||||
if (this.activeIdx < this.showMembers.length - 1) { |
|
||||
this.activeIdx++; |
|
||||
this.scrollToActive() |
|
||||
} |
|
||||
}, |
|
||||
select() { |
|
||||
if (this.activeIdx >= 0) { |
|
||||
this.onSelectMember(this.showMembers[this.activeIdx]) |
|
||||
} |
|
||||
this.close(); |
|
||||
}, |
|
||||
scrollToActive() { |
|
||||
if (this.activeIdx * 35 - this.$refs.scrollBox.wrap.clientHeight > this.$refs.scrollBox.wrap.scrollTop) { |
|
||||
this.$refs.scrollBox.wrap.scrollTop += 140; |
|
||||
if (this.$refs.scrollBox.wrap.scrollTop > this.$refs.scrollBox.wrap.scrollHeight) { |
|
||||
this.$refs.scrollBox.wrap.scrollTop = this.$refs.scrollBox.wrap.scrollHeight |
|
||||
} |
|
||||
} |
|
||||
if (this.activeIdx * 35 < this.$refs.scrollBox.wrap.scrollTop) { |
|
||||
this.$refs.scrollBox.wrap.scrollTop -= 140; |
|
||||
if (this.$refs.scrollBox.wrap.scrollTop < 0) { |
|
||||
this.$refs.scrollBox.wrap.scrollTop = 0; |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
onSelectMember(member) { |
|
||||
this.$emit("select", member); |
|
||||
this.show = false; |
|
||||
} |
} |
||||
}, |
}, |
||||
computed: { |
moveDown() { |
||||
isOwner() { |
if (this.activeIdx < this.showMembers.length - 1) { |
||||
return this.$store.state.userStore.userInfo.id == this.ownerId; |
this.activeIdx++; |
||||
|
this.scrollToActive() |
||||
} |
} |
||||
}, |
}, |
||||
watch: { |
select() { |
||||
searchText: { |
if (this.activeIdx >= 0) { |
||||
handler(newText, oldText) { |
this.onSelectMember(this.showMembers[this.activeIdx]) |
||||
this.init(); |
} |
||||
|
this.close(); |
||||
|
}, |
||||
|
scrollToActive() { |
||||
|
if (this.activeIdx * 35 - this.$refs.scrollBox.wrap.clientHeight > this.$refs.scrollBox.wrap.scrollTop) { |
||||
|
this.$refs.scrollBox.wrap.scrollTop += 140; |
||||
|
if (this.$refs.scrollBox.wrap.scrollTop > this.$refs.scrollBox.wrap.scrollHeight) { |
||||
|
this.$refs.scrollBox.wrap.scrollTop = this.$refs.scrollBox.wrap.scrollHeight |
||||
|
} |
||||
|
} |
||||
|
if (this.activeIdx * 35 < this.$refs.scrollBox.wrap.scrollTop) { |
||||
|
this.$refs.scrollBox.wrap.scrollTop -= 140; |
||||
|
if (this.$refs.scrollBox.wrap.scrollTop < 0) { |
||||
|
this.$refs.scrollBox.wrap.scrollTop = 0; |
||||
} |
} |
||||
} |
} |
||||
|
}, |
||||
|
onSelectMember(member) { |
||||
|
this.$emit("select", member); |
||||
|
this.show = false; |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
isOwner() { |
||||
|
return this.$store.state.userStore.userInfo.id == this.ownerId; |
||||
|
} |
||||
|
}, |
||||
|
watch: { |
||||
|
searchText: { |
||||
|
handler(newText, oldText) { |
||||
|
this.init(); |
||||
|
} |
||||
} |
} |
||||
|
|
||||
} |
} |
||||
|
|
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style scoped lang="scss"> |
<style scoped lang="scss"> |
||||
.group-member-choose { |
.group-member-choose { |
||||
position: fixed; |
position: fixed; |
||||
width: 200px; |
width: 200px; |
||||
height: 300px; |
height: 300px; |
||||
border: 1px solid #53a0e79c; |
//border: 1px solid #53a0e79c; |
||||
border-radius: 5px; |
//border-radius: 5px; |
||||
background-color: #f5f5f5; |
background-color: #fff; |
||||
box-shadow: 0px 0px 10px #ccc; |
box-shadow: var(--im-box-shadow); |
||||
} |
} |
||||
</style> |
</style> |
||||
|
|||||