Compare commits

..

23 Commits

Author SHA1 Message Date
cansnow 271ec4b9c2 增加音乐,摇一摇,服务等页面 2026-02-15 19:41:13 +08:00
cansnow abd279e7a7 恢复视频的发布 2026-02-15 19:40:36 +08:00
cansnow 7c6656d1fc 350 2026-02-13 08:12:56 +08:00
cansnow 6720c15e30 27 2026-02-09 07:29:02 +08:00
cansnow 2860c46ec1 Update App.vue 2026-02-09 03:03:35 +08:00
cansnow 47d10945a6 26 2026-02-09 03:03:22 +08:00
cansnow 560b214af4 25 2026-02-08 16:27:14 +08:00
cansnow 1f4a588d3b 24 2026-01-30 14:45:45 +08:00
cansnow 35a41d8358 23 2026-01-20 21:14:57 +08:00
cansnow 37b53b54ff 增加群相册功能,长按朋友圈文字可以复制 2026-01-20 18:09:59 +08:00
cansnow db99bebcb4 22 2026-01-15 22:50:35 +08:00
commie dd16348558 admin revoke 2026-01-12 18:07:21 +08:00
cansnow e39c06526d 21 2026-01-11 15:48:41 +08:00
cansnow 2a0677014a 朋友圈 2026-01-11 14:00:09 +08:00
cansnow c38846f13b APP热更新 2026-01-11 13:51:16 +08:00
cansnow 941464c330 group mangage 2026-01-10 15:40:38 +08:00
cansnow 825ac3457d deletemsg 2026-01-09 20:22:25 +08:00
cansnow 7913a63a39 20 2026-01-09 09:15:59 +08:00
cansnow 78386d4cc1 19 2026-01-01 04:15:30 +08:00
cansnow 09c7889525 filecache 2025-12-27 07:08:30 +08:00
cansnow 974d149d25 remove img 2025-12-24 04:40:52 +08:00
cansnow f289f79813 18 2025-12-24 04:12:56 +08:00
cansnow f49f1f1ad1 17 2025-12-23 00:18:46 +08:00
336 changed files with 57412 additions and 3859 deletions
+2
View File
@@ -44,3 +44,5 @@ OpenIM_*
# plugin
/nativeplugins
android-keeplive
sl-notify
+327 -132
View File
@@ -1,36 +1,41 @@
<script>
import {mapGetters,mapActions} from "vuex";
// #ifdef APP
import IMSDK, {IMMethods,MessageType,SessionType,} from "openim-uniapp-polyfill";
import config from "./common/config";
// #endif
import config from "@/common/config";
import {getDbDir,toastWithCallback} from "@/util/common.js";
import {conversationSort} from "@/util/imCommon";
import {checkUpgrade} from "@/api/login.js"
import {getConversationContent,conversationSort,prepareConversationState,updateTabbar} from "@/util/imCommon";
import {PageEvents,UpdateMessageTypes} from "@/constant";
import checkUpgrade from "@/util/app_update.js"
export default {
onLaunch: function() {
console.log("App Launch");
this.$store.dispatch("system/getConfig");
// #ifdef APP
plus.screen.lockOrientation("portrait-primary");
// #endif
// #ifdef H5
//screen.orientation.type = "";
screen.orientation.lock('portrait');
// #endif
// #ifdef MP
console.error(
`暂时不支持运行到 Web,如果需要移动端的 Web 项目,参考 [H5 demo](https://github.com/openimsdk/openim-h5-demo)`
`暂时不支持运行到小程序端`
);
return ;
// #endif
// #ifdef MP-WEIXIN
console.error(`暂时不支持运行到小程序端`);
// #endif
this.checkUpdate();
this.setGlobalIMlistener();
this.tryLogin();
this.init();
},
onShow: function() {
console.log("App Show");
//console.log("App Show");
// #ifdef APP
IMSDK.asyncApi(IMSDK.IMMethods.SetAppBackgroundStatus, IMSDK.uuid(), false);
// #endif
this.handleArguments();
//console.log(this.$store.state.contact);
},
onHide: function() {
console.log("App Hide");
//console.log("App Hide");
// #ifdef APP
IMSDK.asyncApi(IMSDK.IMMethods.SetAppBackgroundStatus, IMSDK.uuid(), true);
// #endif
@@ -46,8 +51,27 @@
"storeHistoryMessageList",
"storeIsSyncing",
"storeGroupList",
"config",
'storeUnHandleFriendApplicationNum',
'storeUnHandleGroupApplicationNum',
'storeUnReadCount',
'storeCircleUnreadCount'
]),
},
watch:{
storeUnReadCount(){
updateTabbar(0);
},
storeUnHandleFriendApplicationNum(){
updateTabbar(1);
},
storeUnHandleGroupApplicationNum(){
updateTabbar(1);
},
storeCircleUnreadCount(){
updateTabbar(2);
}
},
methods: {
...mapActions("message", ["pushNewMessage", "updateOneMessage"]),
...mapActions("conversation", ["updateCurrentMemberInGroup"]),
@@ -68,9 +92,23 @@
"pushNewSentGroupApplition",
"updateSentGroupApplition",
]),
init(){
//uni.setStorageSync('BusinessToken','eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ3ZWJtYW4udGlueXdhbi5jbiIsImF1ZCI6IndlYm1hbi50aW55d2FuLmNuIiwiaWF0IjoxNzcwMTAyMjc2LCJuYmYiOjE3NzAxMDIyNzYsImV4cCI6MTc3MDcwNzA3NiwiZXh0ZW5kIjp7ImlkIjoxMDA3MDMsInVzZXJuYW1lIjoiMTg2MTE4ODI2MjMiLCJjbGllbnQiOiJXRUIifX0.yZ2u4cCZq2hLRAIpCKUs8GZkut7CObmfApIZGP_L2ro');
//uni.setStorageSync('IMToken','eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOiJKTTZHMEVWMjVSIiwiUGxhdGZvcm1JRCI6MiwiZXhwIjoxNzc3ODc4Mjc2LCJpYXQiOjE3NzAxMDIyNzF9.rKWz00yCB36CexCahW-HhsFZfKIFcfkuOYLKApQWQT0 ');
//uni.setStorageSync('IMUserID','JM6G0EV25R');
const IMToken = uni.getStorageSync("IMToken");
const IMUserID = uni.getStorageSync("IMUserID")+'';
if (IMToken && IMUserID) {
// #ifdef APP
this.tryLogin();
// #endif
}else{
plus.navigator.closeSplashscreen();
//uni.$u.route("/pages/common/login/index");
}
},
setGlobalIMlistener() {
this.$store.dispatch("system/getConfig");
console.log("setGlobalIMlistener");
//console.log("setGlobalIMlistener");
// init
const kickHander = (message) => {
toastWithCallback(message, () => {
@@ -80,27 +118,32 @@
uni.removeStorage({
key: "BusinessToken",
});
uni.$u.route("/pages/login/index");
uni.$u.route("/pages/common/login/index");
});
};
//由于 APP 管理员强制用户下线,或由于登录策略导致用户被踢下线
IMSDK.subscribe(IMSDK.IMEvents.OnKickedOffline, (data) => {
kickHander("您的账号在其他设备登录,请重新登陆!");
});
//token无效回调。
IMSDK.subscribe(IMSDK.IMEvents.OnUserTokenExpired, (data) => {
kickHander("您的登录已过期,请重新登陆!");
});
IMSDK.subscribe(IMSDK.IMEvents.OnUserTokenInvalid, (data) => {
kickHander("您的登录已无效,请重新登陆!");
});
// sync
//向服务器同步会话开始时的回调。
const syncStartHandler = ({data}) => {
this.$store.commit("user/SET_IS_SYNCING", true);
this.$store.commit("user/SET_REINSTALL", data);
};
//同步中
const syncProgressHandler = ({data}) => {
this.$store.commit("user/SET_PROGRESS", data);
};
//向服务器同步会话成功时的回调。
const syncFinishHandler = () => {
uni.hideLoading();
this.$store.dispatch("conversation/getConversationList");
@@ -109,6 +152,7 @@
this.$store.dispatch("conversation/getUnReadCount");
this.$store.commit("user/SET_IS_SYNCING", false);
};
//向服务器同步会话失败时的回调。
const syncFailedHandler = () => {
uni.hideLoading();
uni.$u.toast("同步消息失败");
@@ -116,65 +160,67 @@
this.$store.dispatch("conversation/getUnReadCount");
this.$store.commit("user/SET_IS_SYNCING", false);
};
//向服务器同步会话开始时的回调。
IMSDK.subscribe(IMSDK.IMEvents.OnSyncServerStart, syncStartHandler);
//向服务器同步会话成功时的回调。
IMSDK.subscribe(IMSDK.IMEvents.OnSyncServerFinish, syncFinishHandler);
//向服务器同步会话失败时的回调。
IMSDK.subscribe(IMSDK.IMEvents.OnSyncServerFailed, syncFailedHandler);
//同步中
IMSDK.subscribe(IMSDK.IMEvents.OnSyncServerProgress, syncProgressHandler);
// self
const selfInfoUpdateHandler = ({data}) => {
// 当前登录用户个人信息改变时会收到此回调。
IMSDK.subscribe(IMSDK.IMEvents.OnSelfInfoUpdated, ({data}) => {
this.$store.commit("user/SET_SELF_INFO", {
...this.storeSelfInfo,
...data,
});
};
IMSDK.subscribe(IMSDK.IMEvents.OnSelfInfoUpdated, selfInfoUpdateHandler);
});
// message
const newMessagesHandler = ({data}) => {
//接收到新消息时会收到此回调,回调中只会携带一条消息。
//设置了批量消息监听setBatchMsgListener时,此回调不会触发。
//IMSDK.subscribe(IMSDK.IMEvents.OnRecvNewMessage, ({data}) =>{});
IMSDK.subscribe(IMSDK.IMEvents.OnRecvNewMessages, ({data}) => {
if (this.storeIsSyncing) {
return;
}
console.log(data);
data.forEach(this.handleNewMessage);
};
IMSDK.subscribe(IMSDK.IMEvents.OnRecvNewMessages, newMessagesHandler);
// friend
const friendInfoChangeHandler = ({data}) => {
console.log('friendInfoChangeHandler',data);
});
//好友个人信息(包括备注)改变时会收到此回调。
IMSDK.subscribe(IMSDK.IMEvents.OnFriendInfoChanged,({data}) => {
//console.log('friendInfoChangeHandler',data);
uni.$emit(IMSDK.IMEvents.OnFriendInfoChanged, {data});
this.updateFriendInfo({friendInfo: data,});
};
const friendAddedHandler = ({data}) => {
});
//两个用户成功建立好友关系后双方都会收到该回调。
IMSDK.subscribe(IMSDK.IMEvents.OnFriendAdded, ({data}) => {
this.pushNewFriend(data);
};
const friendDeletedHander = ({data}) => {
});
//某个用户的好友列表减少时会收到该回调。
IMSDK.subscribe(IMSDK.IMEvents.OnFriendDeleted, ({data}) => {
this.updateFriendInfo({
friendInfo: data,
isRemove: true,
});
};
IMSDK.subscribe(IMSDK.IMEvents.OnFriendInfoChanged,friendInfoChangeHandler);
IMSDK.subscribe(IMSDK.IMEvents.OnFriendAdded, friendAddedHandler);
IMSDK.subscribe(IMSDK.IMEvents.OnFriendDeleted, friendDeletedHander);
});
// blacklist
const blackAddedHandler = ({data}) => {
//某个用户的黑名单列表增加时会收到该回调。
IMSDK.subscribe(IMSDK.IMEvents.OnBlackAdded, ({data}) => {
this.pushNewBlack(data);
};
const blackDeletedHandler = ({data}) => {
});
//某个用户的黑名单列表减少时会收到该回调。
IMSDK.subscribe(IMSDK.IMEvents.OnBlackDeleted, ({data}) => {
this.updateBlackInfo({
blackInfo: data,
isRemove: true,
});
};
IMSDK.subscribe(IMSDK.IMEvents.OnBlackAdded, blackAddedHandler);
IMSDK.subscribe(IMSDK.IMEvents.OnBlackDeleted, blackDeletedHandler);
});
// group
const joinedGroupAddedHandler = ({data}) => {
this.pushNewGroup(data);
@@ -196,12 +242,15 @@
this.updateCurrentMemberInGroup(data);
}
};
//用户所在群组的数量增加时(被邀请入群、入群申请被同意等),会收到此回调。
IMSDK.subscribe(IMSDK.IMEvents.OnJoinedGroupAdded,joinedGroupAddedHandler);
//用户所在群组的数量减少时(主动退群、群被解散等),会收到此回调。
IMSDK.subscribe(IMSDK.IMEvents.OnJoinedGroupDeleted,joinedGroupDeletedHandler);
//群组信息(头像、群名称等,也包括群主变化)改变时,该群所有群成员会收到此回调。
IMSDK.subscribe(IMSDK.IMEvents.OnGroupInfoChanged,groupInfoChangedHandler);
//群成员信息改变(群昵称、头像等)后回调,该群所有群成员会收到此回调。
IMSDK.subscribe(IMSDK.IMEvents.OnGroupMemberInfoChanged,groupMemberInfoChangedHandler);
// application
const friendApplicationNumHandler = ({data}) => {
const isRecv = data.toUserID === this.storeCurrentUserID;
@@ -212,6 +261,7 @@
}
};
const friendApplicationAccessHandler = ({data}) => {
console.log(data);
const isRecv = data.toUserID === this.storeCurrentUserID;
if (isRecv) {
this.updateRecvFriendApplition({
@@ -243,14 +293,61 @@
});
}
};
//用户发起好友申请后,申请发起者和接收者都会收到此回调,接收者可以选择同意或拒绝好友申请。
IMSDK.subscribe(IMSDK.IMEvents.OnFriendApplicationAdded,friendApplicationNumHandler);
//好友申请被同意时,申请发起方和接收方都会收到该回调,双方成功建立好友关系。
IMSDK.subscribe(IMSDK.IMEvents.OnFriendApplicationAccepted,friendApplicationAccessHandler);
//好友申请被拒绝时,申请发起方和接收方都会收到该回调。
IMSDK.subscribe(IMSDK.IMEvents.OnFriendApplicationRejected,friendApplicationAccessHandler);
//用户发起好友申请后,申请发起者和接收者都会收到此回调,接收者可以选择同意或拒绝好友申请。
IMSDK.subscribe(IMSDK.IMEvents.OnGroupApplicationAdded,groupApplicationNumHandler);
//好友申请被同意时,申请发起方和接收方都会收到该回调,双方成功建立好友关系。
IMSDK.subscribe(IMSDK.IMEvents.OnGroupApplicationAccepted,groupApplicationAccessHandler);
//好友申请被拒绝时,申请发起方和接收方都会收到该回调。
IMSDK.subscribe(IMSDK.IMEvents.OnGroupApplicationRejected,groupApplicationAccessHandler);
//群组被解散时,该群所有群成员会收到此回调。
//IMSDK.subscribe(IMSDK.IMEvents.OnGroupDismissed,({ data })=>{});
//群成员增加(如用户被邀请进群),其他群成员会收到此回调。
//IMSDK.subscribe(IMSDK.IMEvents.OnGroupMemberAdded,({ data })=>{});
//群成员增加(如用户被邀请进群),群成员减少(如群成员退群), 其他群成员会收到此回调。。
//IMSDK.subscribe(IMSDK.IMEvents.OnGroupMemberDeleted,({ data })=>{});
const deleteLocalMsg = (clientMsgID)=>{
let list = this.storeHistoryMessageList;
//console.log(data);
list = list.filter((item)=>{
return item.clientMsgID != clientMsgID;
})
this.$store.commit('message/SET_HISTORY_MESSAGE_LIST',list);
}
//收到的消息被撤回或自己发出的消息被撤回时,会收到此回调。
IMSDK.subscribe(IMSDK.IMEvents.OnNewRecvMessageRevoked,({data})=>{
//console.log('onNewRecvMessageRevoked',res);
deleteLocalMsg(data.clientMsgID);
});
//自己发出的单聊消息被对方标记为已读后,消息发送者会收到此回调。
//IMSDK.subscribe(IMSDK.IMEvents.OnRecvC2CReadReceipt,({ data })=>{});
//自己发出的群聊消息被群成员标记为已读后,消息发送者和标记者均会收到此回调。
//IMSDK.subscribe(IMSDK.IMEvents.OnRecvGroupReadReceipt,({ data })=>{});
//当应用在后台运行,接收到新消息时,会收到该回调,回调中只会携带一条消息。
//设置了批量消息监听setBatchMsgListener时,此回调不会触发。
//IMSDK.subscribe(IMSDK.IMEvents.OnRecvOfflineNewMessage,({ data })=>{});
//当应用在后台运行,接收到新消息时,会收到该回调,回调中可能会携带多条消息。
IMSDK.subscribe(IMSDK.IMEvents.OnRecvOfflineNewMessages,({data})=>{
data.forEach(this.handleOfflineNewMessages);
});
IMSDK.subscribe(IMSDK.IMEvents.OnMsgDeleted,({data})=>{
deleteLocalMsg(data.clientMsgID)
});
//已订阅用户的在线状态发生变化时,会触发此回调。
//IMSDK.subscribe(IMSDK.IMEvents.OnUserStatusChanged,({ data })=>{});
//建立WebSocket连接失败返回后,触发此回调
//IMSDK.subscribe(IMSDK.IMEvents.OnConnectFailed,({ data })=>{});
//建立WebSocket连接成功返回后,触发此回调
//IMSDK.subscribe(IMSDK.IMEvents.OnConnectSuccess,({ data })=>{});
//建立WebSocket连接中,触发此回调
//IMSDK.subscribe(IMSDK.IMEvents.OnConnecting,({ data })=>{});
//正在输入状态回调。
//IMSDK.subscribe('onInputStatusChanged',({ data })=>{});
// conversation
const totalUnreadCountChangedHandler = ({data}) => {
if (this.storeIsSyncing) {
@@ -284,79 +381,106 @@
const result = [...data, ...filterArr];
this.$store.commit("conversation/SET_CONVERSATION_LIST",conversationSort(result));
};
//会话总未读发生变化时的回调。
IMSDK.subscribe(IMSDK.IMEvents.OnTotalUnreadMessageCountChanged,totalUnreadCountChangedHandler);
//有新会话产生时,会收到此回调。
IMSDK.subscribe(IMSDK.IMEvents.OnNewConversation, newConversationHandler);
//某些会话的关键信息发生变化时,会触发该回调,例如会话的未读数发生变化,会话的最后一条消息发生变化等。
IMSDK.subscribe(IMSDK.IMEvents.OnConversationChanged,conversationChangedHandler);
},
tryLogin() {
const initStore = () => {
this.$store.dispatch("user/getSelfInfo");
this.$store.dispatch("conversation/getConversationList");
this.$store.dispatch("conversation/getUnReadCount");
this.$store.dispatch("contact/getBlacklist");
this.$store.dispatch("contact/getRecvFriendApplications");
this.$store.dispatch("contact/getSentFriendApplications");
this.$store.dispatch("contact/getRecvGroupApplications");
this.$store.dispatch("contact/getSentGroupApplications");
this.$store.dispatch("contact/getFriendList");
this.$store.dispatch("circle/getFriendCircleInfo");
uni.switchTab({
url: "/pages/conversation/conversationList/index?isRedirect=true"
});
async tryLogin() {
const _this = this;
const IMToken = uni.getStorageSync("IMToken");
const IMUserID = uni.getStorageSync("IMUserID")+'';
//console.log('IMToken:',IMToken);
//console.log('IMUserID:',IMUserID);
const path = await getDbDir();
//console.log('path:',path);
const IMConfig = {
systemType: "uni-app",
apiAddr: config.getApiUrl(), // SDK的API接口地址。如:http://xxx:10002
wsAddr: config.getWsUrl(), // SDK的websocket地址。如: ws://xxx:10001
dataDir: path, // 数据存储路径
logLevel: 6,
logFilePath: path,
isLogStandardOutput: true,
isExternalExtensions: false,
};
getDbDir()
.then(async (path) => {
const flag = await IMSDK.asyncApi(IMMethods.InitSDK, IMSDK.uuid(), {
systemType: "uni-app",
apiAddr: config.getApiUrl(), // SDK的API接口地址。如:http://xxx:10002
wsAddr: config.getWsUrl(), // SDK的websocket地址。如: ws://xxx:10001
dataDir: path, // 数据存储路径
logLevel: 6,
logFilePath: path,
isLogStandardOutput: true,
isExternalExtensions: false,
//console.log('IMConfig:',IMConfig);
const flag = await IMSDK.asyncApi(IMMethods.InitSDK, IMSDK.uuid(), IMConfig);
//console.log('flag:',flag);
if (!flag) {
plus.navigator.closeSplashscreen();
console.log('初始化IMSDK失败!');
uni.$u.toast("初始化IMSDK失败!");
return;
}
_this.setGlobalIMlistener();
// setTimeout(()=>{
// },1000);
let status;
do{
status = await IMSDK.asyncApi(IMSDK.IMMethods.GetLoginStatus,IMSDK.uuid());
//console.log(status);
}while(status == -1001);
if (status === 3) {
console.log('初始化,已经登录!');
_this.initStore();
return;
}
if (status === 1) {
IMSDK.asyncApi(IMSDK.IMMethods.Login, IMSDK.uuid(), {
userID: IMUserID,
token: IMToken,
})
.then(_this.initStore)
.catch((err) => {
console.log(err);
uni.removeStorage({
key: "IMToken",
});
uni.removeStorage({
key: "BusinessToken",
});
plus.navigator.closeSplashscreen();
});
if (!flag) {
plus.navigator.closeSplashscreen();
uni.$u.toast("初始化IMSDK失败!");
return;
}
const status = await IMSDK.asyncApi(IMSDK.IMMethods.GetLoginStatus,IMSDK.uuid());
if (status === 3) {
initStore();
return;
}
}
const IMToken = uni.getStorageSync("IMToken");
const IMUserID = uni.getStorageSync("IMUserID")+'';
if (IMToken && IMUserID) {
IMSDK.asyncApi(IMSDK.IMMethods.Login, IMSDK.uuid(), {
userID: IMUserID,
token: IMToken,
})
.then(initStore)
.catch((err) => {
console.log(err);
uni.removeStorage({
key: "IMToken",
});
uni.removeStorage({
key: "BusinessToken",
});
plus.navigator.closeSplashscreen();
});
} else {
plus.navigator.closeSplashscreen();
}
})
.catch((err) => {
console.log("get dir failed");
console.log(err);
plus.navigator.closeSplashscreen();
});
},
handleOfflineNewMessages(newServerMsg) {
console.log(newServerMsg);
console.log( getConversationContent(newServerMsg));
uni.createPushMessage({
title:"您的朋友发来新的消息",
content:getConversationContent(newServerMsg),
payload:{
type:"msg",
data:newServerMsg
},
//icon:'',
//sound:'',
//cover:'false',
//delay:0,
//when:0,//消息上显示的提示时间
//channelId:"",
//category:"",
success(res){
//console.log(res);
},
fail(res){
//console.log(res);
},
complete(res){
//console.log(res);
}
});
},
handleNewMessage(newServerMsg) {
if (this.inCurrentConversation(newServerMsg)) {
if (
@@ -368,8 +492,11 @@
setTimeout(() => uni.$emit(PageEvents.ScrollToBottom, true));
uni.$u.debounce(this.markConversationAsRead, 2000);
}
}else{
this.handleOfflineNewMessages(newServerMsg);
}
},
inCurrentConversation(newServerMsg) {
switch (newServerMsg.sessionType) {
case SessionType.Single:
@@ -386,6 +513,7 @@
return false;
}
},
markConversationAsRead() {
IMSDK.asyncApi(
IMSDK.IMMethods.MarkConversationMessageAsRead,
@@ -393,26 +521,93 @@
this.storeCurrentConversation.conversationID
);
},
initStore() {
const _this = this;
this.$store.dispatch("user/getSelfInfo");
this.$store.dispatch("conversation/getConversationList");
this.$store.dispatch("conversation/getUnReadCount");
this.$store.dispatch("contact/getBlacklist");
this.$store.dispatch("contact/getRecvFriendApplications");
this.$store.dispatch("contact/getSentFriendApplications");
this.$store.dispatch("contact/getRecvGroupApplications");
this.$store.dispatch("contact/getSentGroupApplications");
this.$store.dispatch("contact/getFriendList");
this.$store.dispatch("circle/getFriendCircleInfo");
if(true !== this.handleArguments()){
uni.switchTab({
url: "/pages/conversation/conversationList/index?isRedirect=true",
complete() {
_this.keppAlive();
_this.checkUpdate();
},
fail(e){
console.log(e);
}
});
}
},
// 验证是否升级
checkUpdate() {
let system = uni.getSystemInfoSync()
plus.runtime.getProperty(plus.runtime.appid, function(inf) {
checkUpgrade({version:system.appVersion,platform:system.platform,version_wgt:inf.versionCode}).then(res=>{
let skip_version = uni.getStorageSync('skip_version')
if(res && res.version!=skip_version){
uni.$emit('closeWebview')
this.setShow(this.current, false)
router('/pages/common/upgrade?model=' + JSON.stringify(res), '', 'fade-in')
}
})
})
const _this = this;
checkUpgrade();
},
},
keppAlive(){
// #ifdef APP-NVUE
uni.requestPermissions(['android.permission.RECEIVE_BOOT_COMPLETED'], (result) => {
if (result.granted) {
console.log('权限已获得');
} else {
console.log('权限被拒绝');
uni.showModal({
title: '权限申请',
content: '您需要授权后台运行权限才能正常使用该功能',
showCancel: false
});
}
});
// #endif
},
handleArguments(){
var args= plus.runtime.arguments;
if(args){
if(args.startsWith('shunliao://')){
console.log(args);
return ;
}
if(args.startsWith('{')){
const json = JSON.parse(args);
if(json.type == 'msg'){
if(this.inCurrentConversation(json.data)){
}else{
let conversation = this.storeConversationList.find((item) => item === json.data);
if(conversation){
plus.navigator.closeSplashscreen();
prepareConversationState(conversation);
return true;
}
}
}
return ;
}
// 处理args参数,如直达到某新页面等
console.log(args);
}
}
}
};
</script>
<style lang="scss">
@import "@/static/imfont/iconfont.css";
/*每个页面公共css */
text,view{
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
@import "@/uni_modules/uview-ui/index.scss";
@import "@/styles/login.scss";
@import "@/styles/global.scss";
+1
View File
@@ -1,4 +1,5 @@
```
安卓App Key:7105a186f961b5d418914c5d1b12f6af
n1e5a6s6m7
```
```
BIN
View File
Binary file not shown.
+65 -9
View File
@@ -4,8 +4,41 @@ import config from "@/common/config";
export const businessConfig = (params) =>
uni.$u?.http.post("/common/init", JSON.stringify(params));
// 验证是否升级
export const checkUpgrade = (params) =>
uni.$u?.http.post("/common/checkUpgrade", JSON.stringify(params));
export const checkUpgrade = (params) =>{
const _this = this;
return new Promise((resolve,reject)=>{
let system = uni.getSystemInfoSync()
plus.runtime.getProperty(plus.runtime.appid, function(inf) {
uni.$u?.http.post("/common/checkUpgrade", JSON.stringify({
version:system.appVersion,
platform:system.platform,
version_wgt:inf.versionCode,
})).then(res=>{
console.log(res);
if(!res || !res.version){
return reject(true);
}
let skip_version = uni.getStorageSync('skip_version')
//console.log(res.version,skip_version);
if(res && res.version!=skip_version){
uni.$emit('closeWebview')
uni.setStorageSync('upgrade_model',res)
uni.navigateTo({
url: '/pages/common/upgrade',
animationType:"fade-in",
complete(res1) {
//console.log(res1);
return resolve();
}
});
}
reject();
}).catch(e=>{
reject(e);
})
});
});
};
export const businessLogin = (params) =>
uni.$u?.http.post("/common/login", JSON.stringify(params));
export const businessSendSms = (params) =>
@@ -32,7 +65,7 @@ export const businessModify = (params) =>
// 用户信息
export const businessInfoUpdate = (params) =>
uni.$u?.http.post(
"/user/info",
"/user/profile",
JSON.stringify({...params}),
{
header: {
@@ -80,9 +113,10 @@ export const businessSearchUser = (keyword,searchtype) =>
},
}
);
export const getArticle = (id,type) => uni.$u?.http.post("/article/detail",JSON.stringify({id,type:(type? type : 'id')}));
export const getSpage = (name) => uni.$u?.http.get(`/article/singpage?name=${name}`);
export const getArticle = (id) => uni.$u?.http.get(`/article/detail?id=${id}`);
export const getFriendCircle = (page=1,limit=10) =>{
return uni.$u?.http.get("/friendcircle/list",JSON.stringify({limit:limit,page:page}));
return uni.$u?.http.get(`/friendcircle/list?limit=${limit}&page=${page}`);
}
export const getFriendCircleNewcount = () =>{
return uni.$u?.http.get("/friendcircle/newcount");
@@ -95,17 +129,39 @@ export const upload = (files,data,onProgress) =>{
onProgress = data;
data = {};
}
data.data = data.data ? data.data : {};
let headers = {};
if(data.token){
headers = data.headers;
delete data.headers;
}
headers = {
...headers,
client:uni.getSystemInfoSync().osName,
token:uni.getStorageSync("BusinessToken"),
operationID: (Math.random() * 36).toString(36).slice(2) + new Date().getTime().toString(),
// #ifdef APP-PLUS
ClientVersion:plus.runtime.versionCode,
// #endif
// #ifndef APP-PLUS
ClientVersion:350,
// #endif
}
console.log(typeof files);
let url = "/user/upload";
if(data.url){
url = data.url;
delete data.url;
}
url= config.getRegisterUrl()+url;
return new Promise((resolve,reject)=>{
var u = uni.uploadFile({
url: config.getRegisterUrl()+"/user/upload", // 仅为示例,非真实的接口地址
url: url,
filePath: files,
//files:files.length > 1 ? files : files[0],
name: "file",
formData:data,
header:{
token:uni.getStorageSync("BusinessToken"),
},
header:headers,
success({data,errMsg}){
console.log(data);
data = JSON.parse(data);
+6 -8
View File
@@ -3,13 +3,10 @@
// const API_URL = `http://${BASE_HOST}:10002`
// const WS_URL = `ws://${BASE_HOST}:10001`
const BASE_DOMAIN = 'www.axzc.xyz'
// const CHAT_URL = `https://${BASE_DOMAIN}/chat`
// const API_URL = `https://${BASE_DOMAIN}/api`
// const WS_URL = `wss://${BASE_DOMAIN}/msg_gateway`
const CHAT_URL = `http://${BASE_DOMAIN}/api`
const API_URL = `http://${BASE_DOMAIN}/imapi`
const WS_URL = `ws://${BASE_DOMAIN}/ws`
const BASE_DOMAIN = 'www.shun777.com'
const CHAT_URL = `https://${BASE_DOMAIN}/api`
const API_URL = `https://${BASE_DOMAIN}/imapi`
const WS_URL = `wss://${BASE_DOMAIN}/msg_gateway`
const version = '2.0.6'
@@ -18,7 +15,8 @@ const getApiUrl = () => uni.getStorageSync("IMApiUrl") || API_URL;
const getWsUrl = () => uni.getStorageSync("IMWsUrl") || WS_URL;
module.exports = {
cdnUrl:"http://"+BASE_DOMAIN,
//cdnUrl:"http://"+BASE_DOMAIN,
cdnUrl:"https://shunliao.oss-accelerate.aliyuncs.com",
version,
getRegisterUrl,
getApiUrl,
+77
View File
@@ -0,0 +1,77 @@
<template>
<view @longpress="longpress" >
<u-image
@click="click"
:width="width"
:height="height"
:mode="mode"
@error="errorHandle"
:src="cachesrc"></u-image>
</view>
</template>
<script>
import util from "@/util";
export default {
name: "CacheImage",
props: {
src: String,
type: {
type: String,
default: "images",
},
mode:{
type: String,
default: "aspectFill",
},
width: {
type: Number|String,
default: "100",
},
height: {
type: Number|String,
default: "",
},
shape:{
type: String,
default: "square",
}
},
data() {
return {
cachesrc:"",
};
},
created() {
this.init(this.src);
},
watch: {
src(newVal) {
this.init(newVal);
},
},
methods: {
init(nv){
const _this = this;
if (nv) {
util.cacheFile(util.cdn(nv),this.type).then(res=>{
_this.cachesrc = res;
//console.log(_this.cachesrc);
});
return ;
}
},
errorHandle() {
this.cachesrc="/static/images/default_image.png";
},
click() {
this.$emit("click");
},
longpress() {
this.$emit("longpress");
},
}
};
</script>
<style></style>
+68 -80
View File
@@ -1,92 +1,80 @@
<template>
<u-navbar :title="title" placeholder class="custom_nav_bar">
<template slot="left">
<slot name="left">
<view class="u-nav-slot">
<img
@click="leftClick"
class="back_icon"
width="12"
height="20"
src="static/images/common_left_arrow.png"
alt=""
srcset=""
/>
</view>
</slot>
</template>
<u-navbar :title="title" placeholder class="custom_nav_bar">
<template slot="left">
<slot name="left">
<view class="u-nav-slot">
<img @click="leftClick" class="back_icon" width="12" height="20"
src="static/images/common_left_arrow.png" alt="" srcset="" />
</view>
</slot>
</template>
<template slot="center">
<slot name="center"></slot>
</template>
<template slot="center">
<slot name="center"></slot>
</template>
<template slot="right">
<slot name="more">
<view @click="rightClick" v-if="more" class="u-nav-slot">
<u-icon
class="more_dot"
name="more-dot-fill"
size="23"
color="#0C1C33"
></u-icon>
</view>
</slot>
</template>
</u-navbar>
<template slot="right">
<slot name="more">
<view @click="rightClick" v-if="more" class="u-nav-slot">
<u-icon class="more_dot" name="more-dot-fill" size="23" color="#0C1C33"></u-icon>
</view>
</slot>
</template>
</u-navbar>
</template>
<script>
export default {
name: "",
components: {},
props: {
title: {
type: String,
},
more: {
type: Boolean,
default: false,
},
route: {
type: Boolean,
default: true,
},
},
data() {
return {};
},
methods: {
leftClick() {
if (this.route) {
uni.navigateBack();
}
this.$emit("leftClick");
},
rightClick() {
this.$emit("rightClick");
},
},
};
export default {
name: "",
components: {},
props: {
title: {
type: String,
},
more: {
type: Boolean,
default: false,
},
route: {
type: Boolean,
default: true,
},
},
data() {
return {};
},
methods: {
leftClick() {
if (this.route) {
uni.navigateBack();
}
this.$emit("leftClick");
},
rightClick() {
this.$emit("rightClick");
},
},
};
</script>
<style lang="scss" scoped>
.custom_nav_bar {
::v-deep .u-navbar__content__left {
padding: 0;
}
.custom_nav_bar {
::v-deep .u-navbar__content__left {
padding: 0;
}
::v-deep .u-navbar__content__right {
padding: 0;
}
::v-deep .u-navbar__content__right {
padding: 0;
}
.back_icon {
padding: 24rpx;
margin-left: 20rpx;
}
.back_icon {
padding: 24rpx;
margin-left: 20rpx;
}
.more_dot {
padding: 24rpx;
margin-right: 20rpx;
}
}
</style>
.more_dot {
padding: 24rpx;
margin-right: 20rpx;
}
}
</style>
+39 -31
View File
@@ -1,13 +1,15 @@
<template>
<u-avatar @longpress="longpress" @click="click" @onError="errorHandle" :src="getAvatarUrl" :text="avatarText"
bg-color="#cdcdcd" :defaultUrl="getDdefaultUrl" :shape="shape" :size="size" mode="aspectFill" font-size="14">
<u-avatar @longpress="longpress" @click="click" @onError="errorHandle" :src="cachesrc" :text="avatarText"
bg-color="#cdcdcd" :defaultUrl="getDefaultUrl" :shape="shape" :size="size" mode="aspectFill" font-size="14">
</u-avatar>
</template>
<script>
import defaultGroupIcon from "@/static/images/contact_my_group.png";
import defaultUserIcon from "@/static/images/user/avatar.png";
import defaultNotifyIcon from "@/static/images/default_notify_icon.png";
import util from "@/util";
import md5 from "md5";
export default {
name: "MyAvatar",
props: {
@@ -34,34 +36,48 @@
data() {
return {
avatarText: undefined,
cachesrc:"",
};
},
computed: {
getAvatarUrl() {
if (this.src) {
return util.cdn(this.src);
}
if (this.isGroup) {
return defaultGroupIcon;
}
if (this.isNotify) {
return defaultNotifyIcon;
}
this.avatarText = this.desc ? this.desc.slice(0, 1) : "未知";
return "";
},
getDdefaultUrl() {
return this.isGroup ? defaultGroupIcon : undefined;
getDefaultUrl() {
return this.isGroup ? defaultGroupIcon : defaultUserIcon;
},
},
watch:{
src(nv,ov){
this.init(nv);
},
desc() {
//this.redirectShow();
}
},
created() {
this.init(this.src);
},
methods: {
errorHandle() {
init(nv){
const _this = this;
if (nv) {
util.cacheFile(util.cdn(nv),'avatar').then(res=>{
_this.avatarText=""
_this.cachesrc = res;
//console.log(_this.cachesrc);
});
return ;
}
if (this.isGroup) {
_this.cachesrc = defaultGroupIcon;
return ;
}
if (this.isNotify) {
_this.cachesrc = defaultNotifyIcon;
return ;
}
this.avatarText = this.desc ? this.desc.slice(0, 1) : "未知";
},
redirectShow() {
if (this.avatarText) {
this.avatarText = undefined;
}
errorHandle() {
this.avatarText = this.desc ? this.desc.slice(0, 1) : "未知";
},
click() {
this.$emit("click");
@@ -69,15 +85,7 @@
longpress() {
this.$emit("longpress");
},
},
watch: {
src() {
this.redirectShow();
},
desc() {
this.redirectShow();
},
},
}
};
</script>
+1 -1
View File
@@ -15,7 +15,7 @@
const token = uni.getStorageSync('IMToken');
if (!token) {
uni.redirectTo({
url: "/pages/login/index"
url: "/pages/common/login/index"
})
}
},
+14 -5
View File
@@ -1,5 +1,5 @@
<template>
<view @click="clickItem" class="user_item">
<view @longtap.stop.prevent="longtap" @tap="clickItem" class="user_item">
<view v-if="checkVisible" class="check_wrap"
:class="{ check_wrap_active: checked, check_wrap_disabled: disabled }">
<u-icon v-show="checked" name="checkbox-mark" size="12" color="#fff" />
@@ -8,10 +8,11 @@
<my-avatar :src="item.faceURL" :desc="item.remark || item.nickname || item.showName"
:isGroup="item.groupName !== undefined || isGroupConversation" size="42" />
<view class="user_item_details">
<text class="user_name">{{item.remark || item.nickname || item.groupName || item.showName}}</text>
<text v-if="item.roleLevel === 100" class="user_role">群主</text>
<text v-if="item.roleLevel === 60" class="user_role admin_role">管理员</text>
<!-- <view class="bottom_line" /> -->
<view class="user_name">{{item.remark || item.nickname || item.groupName || item.showName}}</view>
<view v-if="item.roleLevel === 100" class="user_role">群主</view>
<view v-else-if="item.roleLevel === 60" class="user_role admin_role">管理员<u-icon v-if="item.muteEndTime>0" size="24" name="volume-off"></u-icon></view>
<view v-else class="user_role"><u-icon v-if="item.muteEndTime>0" size="24" name="volume-off"></u-icon></view>
<view class="bottom_line" />
</view>
<slot name="action"></slot>
@@ -55,6 +56,12 @@
this.$emit(this.checkVisible ? "updateCheck" : "itemClick", this.item);
}
},
longtap(){
if(this.checkVisible){
return ;
}
this.$emit("longtapEvent", this.item);
}
},
};
</script>
@@ -119,6 +126,8 @@
padding: 8rpx 24rpx;
border-radius: 24rpx;
margin-left: 24rpx;
display: flex;
align-items: center;
color: $u-tips-color;
}
+678
View File
@@ -0,0 +1,678 @@
<template>
<div class="cut-avatar-page" v-if="tempFilePath">
<div class="main-content" v-if="1==2">
<div class="preview-section">
<div v-if="resultPath" class="result-box">
<image :src="resultPath" mode="widthFix" class="result-image" @click="previewResult"></image>
<div class="action-row">
<button class="btn btn-outline" @click="reSelect">重新选择</button>
<button class="btn btn-primary" @click="uploadImage">确认上传</button>
</div>
<text class="tip-text">点击图片可预览大图</text>
</div>
<div v-else class="upload-box" @click="onSelect">
<div class="placeholder-icon">+</div>
<text class="upload-text">点击上传图片</text>
<text class="upload-sub-text">支持 JPG/PNG自动裁剪 1:1</text>
</div>
</div>
</div>
<div v-if="isShowEditor" class="editor-modal" :class="{ 'dark-mode': isDarkMode }">
<div class="cropper-wrapper">
<movable-area class="crop-area" :style="{ height: screenHeight + 'px' }">
<movable-view
class="crop-view"
direction="all"
:out-of-bounds="true"
:x="imgX"
:y="imgY"
:scale="scale+''"
:scale-min="0.5"
:scale-max="4"
:animation="false"
@change="onMoveChange"
@scale="onScaleChange"
:style="{ width: imgDisplayWidth + 'px', height: imgDisplayHeight + 'px' }"
>
<image :src="tempFilePath" class="target-image" :style="{ transform: 'rotate(' + rotation + 'deg)' }"></image>
</movable-view>
</movable-area>
<div class="mask-layer" :style="{ height: screenHeight + 'px' }">
<div class="mask-top" :style="{ height: (screenHeight - cropSize) / 2 + 'px' }"></div>
<div class="mask-middle" :style="{ height: cropSize + 'px' }">
<div class="mask-left" :style="{ width: (screenWidth - cropSize) / 2 + 'px' }"></div>
<div class="crop-box" :style="{ width: cropSize + 'px', height: cropSize + 'px' }">
<div class="corner c-tl"></div>
<div class="corner c-tr"></div>
<div class="corner c-bl"></div>
<div class="corner c-br"></div>
</div>
<div class="mask-right" :style="{ width: (screenWidth - cropSize) / 2 + 'px' }"></div>
</div>
<div class="mask-bottom" :style="{ height: (screenHeight - cropSize) / 2 + 'px' }"></div>
</div>
</div>
<div class="editor-toolbar">
<div class="tool-btn" @click="onCancel">取消</div>
<div class="tool-btn" @click="onReset"> <text class="icon-reset"></text> 重置 </div>
<div class="tool-btn" @click="onRotate"> <text class="icon-rotate"></text> 旋转 </div>
<div class="tool-btn btn-confirm" @click="onConfirm">确定</div>
</div>
<canvas canvas-id="cropCanvas" class="crop-canvas" :style="{ width: cropSize + 'px', height: cropSize + 'px' }"></canvas>
</div>
</div>
</template>
<script>
/**
* 核心逻辑说明:
* 1. 使用 movable-view 实现用户手势交互(拖拽、缩放)。
* 2. 使用 image 的 css transform 实现视觉上的旋转。
* 3. 最终生成时,通过计算偏移量,使用 CanvasContext 绘制图像。
* 4. 旋转后的 Canvas 绘图坐标系需要特殊处理 (translate + rotate)。
*/
export default {
data() {
return {
// --- 系统/环境参数 ---
screenWidth: 375,
screenHeight: 600,
isDarkMode: false,
// --- 业务数据 ---
resultPath: '', // 最终生成的图片路径
// --- 编辑器状态 ---
isShowEditor: false,
tempFilePath: '', // 选中的原始图片路径
// --- 图片原始信息 ---
realImgWidth: 0,
realImgHeight: 0,
// --- 交互状态数据 ---
cropSize: 280, // 裁剪框大小 (正方形)
imgDisplayWidth: 0, // 图片在屏幕上的初始显示宽度
imgDisplayHeight: 0, // 图片在屏幕上的初始显示高度
// movable-view 的属性
imgX: 0,
imgY: 0,
scale: 1,
rotation: 0, // 0, 90, 180, 270
// 记录移动过程中的临时数据 (用于计算)
currentX: 0,
currentY: 0,
currentScale: 1,
};
},
created() {
this.initSystemInfo();
},
methods: {
// -----------------------------------------------------------
// 初始化与系统适配
// -----------------------------------------------------------
initSystemInfo() {
const sys = uni.getSystemInfoSync();
this.screenWidth = sys.windowWidth;
this.screenHeight = sys.windowHeight;
// 适配暗黑模式
if (sys.theme === 'dark') {
this.isDarkMode = true;
}
// 动态计算裁剪框大小 (屏幕宽度的 80%,最大 400px 用于适配 Pad)
let size = this.screenWidth * 0.8;
if (size > 400) size = 400;
this.cropSize = Math.floor(size);
},
// -----------------------------------------------------------
// 交互逻辑
// -----------------------------------------------------------
onSelect() {
console.log('[Debug] 点击选择图片');
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const src = res.tempFilePaths[0];
this.enterEditor(src);
},
fail: (err) => {
console.error('[Error] 选择图片失败', err);
},
});
},
reSelect() {
this.onSelect();
},
previewResult() {
if (this.resultPath) {
uni.previewImage({
urls: [this.resultPath],
});
}
},
uploadImage() {
console.log('[Debug] 触发上传逻辑, 路径:', this.resultPath);
// 这里预留上传 API 调用
uni.showLoading({ title: '正在上传...' });
setTimeout(() => {
uni.hideLoading();
uni.showToast({ title: '模拟上传成功', icon: 'success' });
}, 1000);
},
// -----------------------------------------------------------
// 编辑器逻辑
// -----------------------------------------------------------
// 进入编辑模式,初始化图片位置
enterEditor(src) {
uni.showLoading({ title: '加载中...' });
uni.getImageInfo({
src: src,
success: (res) => {
this.tempFilePath = src;
this.realImgWidth = res.width;
this.realImgHeight = res.height;
// 重置状态
this.rotation = 0;
this.scale = 1;
this.currentScale = 1;
// 计算图片初始显示尺寸(使其适应屏幕宽度,类似 width: 100%
// 这里我们让图片的短边至少填满裁剪框,方便用户操作
const ratio = this.realImgWidth / this.realImgHeight;
if (ratio > 1) {
// 横图:高度定为裁剪框大小 + 一点冗余,宽度自适应
this.imgDisplayHeight = this.cropSize;
this.imgDisplayWidth = this.imgDisplayHeight * ratio;
} else {
// 竖图
this.imgDisplayWidth = this.cropSize;
this.imgDisplayHeight = this.imgDisplayWidth / ratio;
}
// 居中计算
// 屏幕中心
const centerX = this.screenWidth / 2;
const centerY = this.screenHeight / 2;
// 计算 movable-view 的初始 x, y 使其居中
// movable-view 的 x,y 是相对于 movable-area (全屏) 的左上角
this.imgX = centerX - this.imgDisplayWidth / 2;
this.imgY = centerY - this.imgDisplayHeight / 2;
// 同步 current 值
this.currentX = this.imgX;
this.currentY = this.imgY;
this.isShowEditor = true;
uni.hideLoading();
},
fail: () => {
uni.hideLoading();
uni.showToast({ title: '图片加载失败', icon: 'none' });
},
});
},
onMoveChange(e) {
// 记录移动位置,用于最终裁剪计算
// detail: {x, y, source}
this.currentX = e.detail.x;
this.currentY = e.detail.y;
},
onReset(){
this.rotation = 0;
this.currentScale = 1;
},
onScaleChange(e) {
// detail: {scale}
this.currentScale = e.detail.scale;
},
onRotate() {
console.log('[Debug] 点击旋转');
// 顺时针旋转 90 度
this.rotation = (this.rotation + 90) % 360;
// 旋转后,可能需要重置一下缩放或位置逻辑?
// 本方案中,为了简化体验,仅做视觉旋转,
// 最终 Canvas 裁剪时根据 rotation 参数处理坐标系。
},
onCancel() {
this.isShowEditor = false;
this.tempFilePath = '';
},
// -----------------------------------------------------------
// 核心裁剪生成逻辑 (Canvas)
// -----------------------------------------------------------
// -----------------------------------------------------------
onConfirm() {
console.log('[Debug] 开始生成图片');
uni.showLoading({ title: '处理中...' });
const ctx = uni.createCanvasContext('cropCanvas', this);
// 1. 基础参数
const destSize = this.cropSize; // 画布大小 (正方形)
const halfDest = destSize / 2;
// 清空画布
ctx.setFillStyle('#FFFFFF');
ctx.fillRect(0, 0, destSize, destSize);
// 2. 计算【屏幕坐标系】下的中心点偏移
// 这里的逻辑是:找出"图片中心"相对于"裁剪框中心"在屏幕上的距离
// A. 裁剪框中心在屏幕上的坐标
const cropCenterX = (this.screenWidth - this.cropSize) / 2 + this.cropSize / 2;
const cropCenterY = (this.screenHeight - this.cropSize) / 2 + this.cropSize / 2;
// B. 图片中心在屏幕上的坐标
// 注意:movable-view 的 x,y 是左上角坐标,且 movable-view 的缩放是以中心为原点的
// 实际上 uni-app 的 movable-view 在缩放/移动后,x/y 会自动更新为当前的左上角位置
// 所以图片中心 = 当前x + (显示宽度 * 缩放 / 2)
const currentRealWidth = this.imgDisplayWidth * this.currentScale;
const currentRealHeight = this.imgDisplayHeight * this.currentScale;
const imgCenterX = this.currentX + currentRealWidth / 2;
const imgCenterY = this.currentY + currentRealHeight / 2;
// C. 算出屏幕偏移向量 (Screen Diff)
// 含义:图片中心在裁剪框中心的 右侧 diffX 像素,下方 diffY 像素
const diffX = imgCenterX - cropCenterX;
const diffY = imgCenterY - cropCenterY;
// 3. 将屏幕偏移向量【映射】到旋转后的 Canvas 坐标系
// 我们需要算出:在旋转后的坐标系里,图片中心应该在哪里 (drawX, drawY)
let drawX = 0;
let drawY = 0;
// 规范化旋转角度 (0, 90, 180, 270)
const angle = this.rotation % 360;
// 坐标系映射逻辑:
// 屏幕 X+ (向右),屏幕 Y+ (向下)
// 假设 Canvas 原点已移至中心并旋转:
switch (angle) {
case 0:
// 0度:坐标系一致
drawX = diffX;
drawY = diffY;
break;
case 90:
// 90度 (顺时针)Canvas X轴指向屏幕下,Canvas Y轴指向屏幕左
// 屏幕的 X+ (向右) 对应 Canvas 的 Y- (向左的相反) -> drawY = -diffX
// 屏幕的 Y+ (向下) 对应 Canvas 的 X+ (向下) -> drawX = diffY
drawX = diffY;
drawY = -diffX;
break;
case 180:
// 180度:Canvas X轴向左,Canvas Y轴向上 (完全相反)
drawX = -diffX;
drawY = -diffY;
break;
case 270: // 相当于 -90度
// 270度:Canvas X轴向上,Canvas Y轴向右
// 屏幕 X+ (向右) 对应 Canvas Y+ -> drawY = diffX
// 屏幕 Y+ (向下) 对应 Canvas X- -> drawX = -diffY
drawX = -diffY;
drawY = diffX;
break;
}
// 4. 执行绘制
ctx.save();
// a. 将原点移到画布中心
ctx.translate(halfDest, halfDest);
// b. 旋转坐标系
ctx.rotate((angle * Math.PI) / 180);
// c. 绘制图片
// drawImage 的 x,y 是图片的左上角坐标
// 我们算出的 drawX, drawY 是图片中心相对于画布中心的坐标
// 所以要减去宽高的一半
ctx.drawImage(this.tempFilePath, drawX - currentRealWidth / 2, drawY - currentRealHeight / 2, currentRealWidth, currentRealHeight);
ctx.restore();
// 5. 导出图片
ctx.draw(false, () => {
setTimeout(() => {
uni.canvasToTempFilePath(
{
canvasId: 'cropCanvas',
width: destSize,
height: destSize,
fileType: 'jpg',
quality: 1,
success: (res) => {
this.resultPath = res.tempFilePath;
this.tempFilePath = "";
this.isShowEditor = false;
uni.hideLoading();
this.$emit('save', {path:this.resultPath});
},
fail: () => {
uni.hideLoading();
uni.showToast({ title: '生成失败', icon: 'none' });
},
},
this,
);
}, 200);
});
},
},
};
</script>
<style scoped>
/* ========================
CSS 变量定义
======================== */
.cut-avatar-page {
--primary-color: #ef3912;
--bg-color: #f8f9fa;
--text-color: #333333;
--modal-bg: #000000;
min-height: 100vh;
background-color: var(--bg-color);
display: flex;
flex-direction: column;
}
/* 适配暗黑模式 */
@media (prefers-color-scheme: dark) {
.page-container {
--bg-color: #1a1a1a;
--text-color: #ffffff;
}
}
/* ========================
主界面样式
======================== */
.nav-bar {
height: 44px;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--bg-color);
/* 简单的顶部阴影 */
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.05);
}
.nav-title {
font-size: 16px;
font-weight: bold;
color: var(--text-color);
}
.main-content {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}
.preview-section {
width: 100%;
max-width: 600px; /* Pad 适配限制 */
display: flex;
flex-direction: column;
align-items: center;
margin-top: 40px;
}
/* 上传占位框 */
.upload-box {
width: 100%;
height: 200px;
border: 2px dashed #cccccc;
border-radius: 12px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.02);
transition: all 0.3s;
}
.upload-box:active {
background-color: rgba(239, 57, 18, 0.05);
border-color: var(--primary-color);
}
.placeholder-icon {
font-size: 40px;
color: #999;
margin-bottom: 10px;
}
.upload-text {
font-size: 16px;
color: var(--text-color);
font-weight: bold;
}
.upload-sub-text {
font-size: 12px;
color: #999;
margin-top: 5px;
}
/* 结果展示区 */
.result-box {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.result-image {
width: 200px;
height: 200px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
background-color: #eee;
margin-bottom: 20px;
}
.action-row {
display: flex;
flex-direction: row;
justify-content: center;
gap: 15px;
width: 100%;
}
.btn {
font-size: 14px;
padding: 8px 24px;
border-radius: 20px;
border: none;
}
.btn-primary {
background-color: var(--primary-color);
color: #fff;
}
.btn-outline {
background-color: transparent;
border: 1px solid #ccc;
color: var(--text-color);
}
.tip-text {
font-size: 12px;
color: #999;
margin-top: 15px;
}
/* ========================
编辑器弹窗样式 (全屏)
======================== */
.editor-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 999;
background-color: #000;
display: flex;
flex-direction: column;
}
.cropper-wrapper {
flex: 1;
position: relative;
overflow: hidden;
}
.crop-area {
width: 100%;
/* height 由 JS 动态控制 */
}
.crop-view {
/* 初始不可见,加载图片后显示 */
display: flex;
align-items: center;
justify-content: center;
}
.target-image {
width: 100%;
height: 100%;
/* 优化图片渲染 */
will-change: transform;
}
/* 遮罩层布局 */
.mask-layer {
position: absolute;
top: 0;
left: 0;
width: 100%;
pointer-events: none; /* 让事件穿透到底下的 movable-area */
display: flex;
flex-direction: column;
z-index: 10;
}
.mask-top,
.mask-bottom,
.mask-left,
.mask-right {
background-color: rgba(0, 0, 0, 0.6);
}
.mask-middle {
display: flex;
flex-direction: row;
}
/* 裁剪框样式 */
.crop-box {
position: relative;
border: 1px solid rgba(255, 255, 255, 0.5);
box-sizing: border-box;
}
/* 四个角的装饰 */
.corner {
position: absolute;
width: 15px;
height: 15px;
border: 3px solid #fff;
}
.c-tl {
top: -2px;
left: -2px;
border-right: none;
border-bottom: none;
}
.c-tr {
top: -2px;
right: -2px;
border-left: none;
border-bottom: none;
}
.c-bl {
bottom: -2px;
left: -2px;
border-right: none;
border-top: none;
}
.c-br {
bottom: -2px;
right: -2px;
border-left: none;
border-top: none;
}
/* 底部工具栏 */
.editor-toolbar {
height: 80px; /* 留足安全距离 */
background-color: #1a1a1a;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30px;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
.tool-btn {
color: #fff;
font-size: 16px;
padding: 10px;
}
.btn-confirm {
color: var(--primary-color);
font-weight: bold;
}
.icon-rotate {
font-size: 18px;
margin-right: 4px;
}
/* Canvas 隐藏处理 */
.crop-canvas {
position: absolute;
left: -9999px;
top: -9999px;
pointer-events: none;
visibility: hidden; /* 这里用 hidden, 有些环境 display:none 会导致不渲染 */
opacity: 0;
}
</style>
-155
View File
@@ -1,155 +0,0 @@
<template>
<view class="map_container"></view>
</template>
<script>
export default {
data() {
return {
map: null,
centerMarker: null,
currentPoint: null,
currentAddress: '',
}
},
mounted() {
this.createMap();
this.initializeMap();
},
methods: {
createMap() {
const mapId = "map_" + Math.random();
const map = plus.maps.create(mapId, {
zoom: 13,
MapType: plus.maps.MapType.MAPTYPE_NORMAL,
traffic: true,
zoomControls: true,
top: "0",
left: "0",
width: '100%',
height: '100%',
position: "static",
});
plus.webview.currentWebview().append(map);
this.map = map;
},
initializeMap() {
const _this = this;
// 获取当前位置
uni.getLocation({
type: 'gcj02',
success(res) {
const point = new plus.maps.Point(res.longitude, res.latitude);
_this.map.setCenter(point);
_this.currentPoint = point;
_this.addCenterMarker(point);
_this.getAddressByPoint(point);
_this.setupMapMoveListener();
_this.map.show();
},
fail() {
// 如果获取位置失败,使用默认位置
const defaultPoint = new plus.maps.Point(116.4074, 39.9042);
_this.map.setCenter(defaultPoint);
_this.currentPoint = defaultPoint;
_this.addCenterMarker(defaultPoint);
_this.getAddressByPoint(defaultPoint);
_this.setupMapMoveListener();
_this.map.show();
}
});
},
addCenterMarker(point) {
// 移除旧的marker
if (this.centerMarker) {
this.map.removeOverlay(this.centerMarker);
}
// 添加中心点marker
this.centerMarker = new plus.maps.Marker(point);
this.centerMarker.setIcon("/static/images/chat/marker.png");
this.map.addOverlay(this.centerMarker);
},
setupMapMoveListener() {
const _this = this;
// 使用定时器轮询来检测地图位置变化
// plus.maps 的 H5 版本可能不支持事件监听,通过定时器实现
let lastCenter = this.map.getCenter ? this.map.getCenter() : null;
setInterval(() => {
_this.map.getCurrentCenter((state, point) => {
if (state !== 0 || !point) {
return;
}
// 简单的位置变化检测
if (!lastCenter ||
lastCenter.getLng() !== point.getLng() ||
lastCenter.getLat() !== point.getLat()) {
lastCenter = point;
_this.updateCenterMarker();
}
});
}, 500);
},
updateCenterMarker() {
const _this = this;
this.map.getCurrentCenter((state, point) => {
if (state !== 0) {
return;
}
_this.currentPoint = point;
_this.addCenterMarker(point);
_this.getAddressByPoint(point);
});
},
getAddressByPoint(point) {
const _this = this;
// 简化处理:直接显示"位置已选择",不调用外部 API
// 如果需要真实地址,可以在确认时调用服务端 API
_this.currentAddress = '位置已选择';
},
getCurrentCenter(callback) {
const _this = this;
this.map.getCurrentCenter((state, point) => {
if (state !== 0) {
callback(state, null);
return;
}
// 直接返回当前数据,地址会异步更新
callback(state, {
point: point,
lng: point.getLng(),
lat: point.getLat(),
address: _this.currentAddress || '位置已选择'
});
});
}
}
}
</script>
<style lang="scss" scoped>
.map_container {
width: 100%;
height: 100%;
}
</style>
<style lang="scss" scoped>
</style>
+3 -3
View File
@@ -4,7 +4,7 @@
:show="previewVideoFlag"
opacity="1"
:custom-style="customMaskStyle">
<view @tap.stop
<view @tap.stop="quitPlay"
class="playBox"
style="height:100vh;width:100vw;">
<video
@@ -18,7 +18,7 @@
</video>
<view class="quitBox" @click="quitPlay">
<u-icon name="close-circle" color="#FFF" size="32"></u-icon>
<cover-view class="icon"></cover-view>
<!-- <cover-view class="icon"></cover-view> -->
</view>
</view>
</u-overlay>
@@ -99,7 +99,7 @@
right: 5%;
top: 10%;
.icon{
color: #fff;
color: #f00;
font-size: 32px;
font-family: uicon-iconfont;
}
+2
View File
@@ -19,6 +19,8 @@ export const GroupMemberListTypes = {
Preview: "Preview",
Transfer: "Transfer",
Kickout: "Kickout",
setAdmin: "setAdmin",
Mute: "Mute",
};
export const ContactChooseTypes = {
+2
View File
@@ -2,6 +2,8 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<!-- <script src="/static/wangeditor/index.js"></script>
<link rel="stylesheet" href="/static/wangeditor/style.css" /> -->
<script>
var coverSupport =
"CSS" in window &&
Binary file not shown.
BIN
View File
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
com.shun777.app
密码123
+272
View File
@@ -0,0 +1,272 @@
/**
* 本模块封装了Android、iOS的应用权限判断、打开应用权限设置界面、以及位置系统服务是否开启
*/
var isIos
// #ifdef APP-PLUS
isIos = (plus.os.name == "iOS")
// #endif
// 判断推送权限是否开启
function judgeIosPermissionPush() {
var result = false;
var UIApplication = plus.ios.import("UIApplication");
var app = UIApplication.sharedApplication();
var enabledTypes = 0;
if (app.currentUserNotificationSettings) {
var settings = app.currentUserNotificationSettings();
enabledTypes = settings.plusGetAttribute("types");
console.log("enabledTypes1:" + enabledTypes);
if (enabledTypes == 0) {
console.log("推送权限没有开启");
} else {
result = true;
console.log("已经开启推送功能!")
}
plus.ios.deleteObject(settings);
} else {
enabledTypes = app.enabledRemoteNotificationTypes();
if (enabledTypes == 0) {
console.log("推送权限没有开启!");
} else {
result = true;
console.log("已经开启推送功能!")
}
console.log("enabledTypes2:" + enabledTypes);
}
plus.ios.deleteObject(app);
plus.ios.deleteObject(UIApplication);
return result;
}
// 判断定位权限是否开启
function judgeIosPermissionLocation() {
var result = false;
var cllocationManger = plus.ios.import("CLLocationManager");
var status = cllocationManger.authorizationStatus();
result = (status != 2)
console.log("定位权限开启:" + result);
// 以下代码判断了手机设备的定位是否关闭,推荐另行使用方法 checkSystemEnableLocation
/* var enable = cllocationManger.locationServicesEnabled();
var status = cllocationManger.authorizationStatus();
console.log("enable:" + enable);
console.log("status:" + status);
if (enable && status != 2) {
result = true;
console.log("手机定位服务已开启且已授予定位权限");
} else {
console.log("手机系统的定位没有打开或未给予定位权限");
} */
plus.ios.deleteObject(cllocationManger);
return result;
}
// 判断麦克风权限是否开启
function judgeIosPermissionRecord() {
var result = false;
var avaudiosession = plus.ios.import("AVAudioSession");
var avaudio = avaudiosession.sharedInstance();
var permissionStatus = avaudio.recordPermission();
console.log("permissionStatus:" + permissionStatus);
if (permissionStatus == 1684369017 || permissionStatus == 1970168948) {
console.log("麦克风权限没有开启");
} else {
result = true;
console.log("麦克风权限已经开启");
}
plus.ios.deleteObject(avaudiosession);
return result;
}
// 判断相机权限是否开启
function judgeIosPermissionCamera() {
var result = false;
var AVCaptureDevice = plus.ios.import("AVCaptureDevice");
var authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide');
console.log("authStatus:" + authStatus);
if (authStatus == 3) {
result = true;
console.log("相机权限已经开启");
} else {
console.log("相机权限没有开启");
}
plus.ios.deleteObject(AVCaptureDevice);
return result;
}
// 判断相册权限是否开启
function judgeIosPermissionPhotoLibrary() {
var result = false;
var PHPhotoLibrary = plus.ios.import("PHPhotoLibrary");
var authStatus = PHPhotoLibrary.authorizationStatus();
console.log("authStatus:" + authStatus);
if (authStatus == 3) {
result = true;
console.log("相册权限已经开启");
} else {
console.log("相册权限没有开启");
}
plus.ios.deleteObject(PHPhotoLibrary);
return result;
}
// 判断通讯录权限是否开启
function judgeIosPermissionContact() {
var result = false;
var CNContactStore = plus.ios.import("CNContactStore");
var cnAuthStatus = CNContactStore.authorizationStatusForEntityType(0);
if (cnAuthStatus == 3) {
result = true;
console.log("通讯录权限已经开启");
} else {
console.log("通讯录权限没有开启");
}
plus.ios.deleteObject(CNContactStore);
return result;
}
// 判断日历权限是否开启
function judgeIosPermissionCalendar() {
var result = false;
var EKEventStore = plus.ios.import("EKEventStore");
var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(0);
if (ekAuthStatus == 3) {
result = true;
console.log("日历权限已经开启");
} else {
console.log("日历权限没有开启");
}
plus.ios.deleteObject(EKEventStore);
return result;
}
// 判断备忘录权限是否开启
function judgeIosPermissionMemo() {
var result = false;
var EKEventStore = plus.ios.import("EKEventStore");
var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1);
if (ekAuthStatus == 3) {
result = true;
console.log("备忘录权限已经开启");
} else {
console.log("备忘录权限没有开启");
}
plus.ios.deleteObject(EKEventStore);
return result;
}
// Android权限查询
function requestAndroidPermission(permissionID) {
return new Promise((resolve, reject) => {
plus.android.requestPermissions(
[permissionID], // 理论上支持多个权限同时查询,但实际上本函数封装只处理了一个权限的情况。有需要的可自行扩展封装
function(resultObj) {
var result = 0;
for (var i = 0; i < resultObj.granted.length; i++) {
var grantedPermission = resultObj.granted[i];
console.log('已获取的权限:' + grantedPermission);
result = 1
}
for (var i = 0; i < resultObj.deniedPresent.length; i++) {
var deniedPresentPermission = resultObj.deniedPresent[i];
console.log('拒绝本次申请的权限:' + deniedPresentPermission);
result = 0
}
for (var i = 0; i < resultObj.deniedAlways.length; i++) {
var deniedAlwaysPermission = resultObj.deniedAlways[i];
console.log('永久拒绝申请的权限:' + deniedAlwaysPermission);
result = -1
}
resolve(result);
// 若所需权限被拒绝,则打开APP设置界面,可以在APP设置界面打开相应权限
// if (result != 1) {
// gotoAppPermissionSetting()
// }
},
function(error) {
console.log('申请权限错误:' + error.code + " = " + error.message);
resolve({
code: error.code,
message: error.message
});
}
);
});
}
// 使用一个方法,根据参数判断权限
function judgeIosPermission(permissionID) {
if (permissionID == "location") {
return judgeIosPermissionLocation()
} else if (permissionID == "camera") {
return judgeIosPermissionCamera()
} else if (permissionID == "photoLibrary") {
return judgeIosPermissionPhotoLibrary()
} else if (permissionID == "record") {
return judgeIosPermissionRecord()
} else if (permissionID == "push") {
return judgeIosPermissionPush()
} else if (permissionID == "contact") {
return judgeIosPermissionContact()
} else if (permissionID == "calendar") {
return judgeIosPermissionCalendar()
} else if (permissionID == "memo") {
return judgeIosPermissionMemo()
}
return false;
}
// 跳转到**应用**的权限页面
function gotoAppPermissionSetting() {
if (isIos) {
var UIApplication = plus.ios.import("UIApplication");
var application2 = UIApplication.sharedApplication();
var NSURL2 = plus.ios.import("NSURL");
// var setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES");
var setting2 = NSURL2.URLWithString("app-settings:");
application2.openURL(setting2);
plus.ios.deleteObject(setting2);
plus.ios.deleteObject(NSURL2);
plus.ios.deleteObject(application2);
} else {
// console.log(plus.device.vendor);
var Intent = plus.android.importClass("android.content.Intent");
var Settings = plus.android.importClass("android.provider.Settings");
var Uri = plus.android.importClass("android.net.Uri");
var mainActivity = plus.android.runtimeMainActivity();
var intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
intent.setData(uri);
mainActivity.startActivity(intent);
}
}
// 检查系统的设备服务是否开启
// var checkSystemEnableLocation = async function () {
function checkSystemEnableLocation() {
if (isIos) {
var result = false;
var cllocationManger = plus.ios.import("CLLocationManager");
var result = cllocationManger.locationServicesEnabled();
console.log("系统定位开启:" + result);
plus.ios.deleteObject(cllocationManger);
return result;
} else {
var context = plus.android.importClass("android.content.Context");
var locationManager = plus.android.importClass("android.location.LocationManager");
var main = plus.android.runtimeMainActivity();
var mainSvr = main.getSystemService(context.LOCATION_SERVICE);
var result = mainSvr.isProviderEnabled(locationManager.GPS_PROVIDER);
console.log("系统定位开启:" + result);
return result
}
}
module.exports = {
judgeIosPermission: judgeIosPermission,
requestAndroidPermission: requestAndroidPermission,
checkSystemEnableLocation: checkSystemEnableLocation,
gotoAppPermissionSetting: gotoAppPermissionSetting
}
-1
View File
@@ -58,6 +58,5 @@ app.REQUEST_TRACE = false;
// 引入请求封装
import request from "./util/request/index";
request(app)
//require("./util/request/index")(app);
app.$mount();
+43 -16
View File
@@ -1,17 +1,18 @@
{
"name" : "回声",
"appid" : "__UNI__CA458BA",
"description" : "",
"versionName" : "3.3.4",
"versionCode" : 334,
"name" : "瞬聊",
"appid" : "__UNI__E41111F",
"description" : "一款即时聊天软件",
"versionName" : "3.5.1",
"versionCode" : 351,
"transformPx" : false,
"app-plus" : {
"bounce" : "none",
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"orientation" : [ "portrait-primary" ],
"splashscreen" : {
"alwaysShowBeforeRender" : false,
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : false,
"delay" : 0
@@ -23,7 +24,8 @@
"Geolocation" : {},
"Fingerprint" : {},
"Contacts" : {},
"Barcode" : {}
"Barcode" : {},
"Push" : {}
},
"distribute" : {
"android" : {
@@ -48,12 +50,16 @@
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
"<uses-permission android:name=\"android.permission.RECEIVE_BOOT_COMPLETED\"/>",
"<uses-permission android:name=\"android.permission.READ_MEDIA_IMAGES\"/>",
"<uses-permission android:name=\"android.permission.READ_MEDIA_VIDEO\"/>",
"<uses-permission android:name=\"android.permission.READ_MEDIA_VISUAL_USER_SELECTED\"/>"
],
"abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ],
"minSdkVersion" : 21,
"targetSdkVersion" : 26,
"schemes" : "huisheng"
"targetSdkVersion" : 36,
"schemes" : "shunliao"
},
"ios" : {
"dSYMs" : false,
@@ -69,11 +75,11 @@
},
"capabilities" : {
"entitlements" : {
"com.apple.developer.associated-domains" : [ "huisheng", "applinks:url.huisheng.hk" ]
"com.apple.developer.associated-domains" : [ "shunliao", "applinks:www.shun777.com", "applinks:url.shun777.com" ]
}
},
"idfa" : false,
"urltypes" : "huisheng"
"urltypes" : "shunliao"
},
"sdkConfigs" : {
"ad" : {},
@@ -91,10 +97,25 @@
},
"share" : {},
"statics" : {},
"speech" : {}
"speech" : {},
"push" : {
"unipush" : {
"version" : "2",
"offline" : false,
"icons" : {
"small" : {
"ldpi" : "static/images/about_logo.png",
"mdpi" : "static/images/about_logo.png",
"hdpi" : "static/images/about_logo.png",
"xhdpi" : "static/images/about_logo.png",
"xxhdpi" : "static/images/about_logo.png"
}
}
}
}
},
"splashscreen" : {
"androidStyle" : "default",
"androidStyle" : "common",
"iosStyle" : "common",
"android" : {
"hdpi" : "unpackage/res/cover/480_762.9.png",
@@ -143,7 +164,7 @@
"platforms" : "Android,iOS",
"url" : "https://ext.dcloud.net.cn/plugin?id=6577",
"android_package_name" : "",
"ios_bundle_id" : "com.tuoyun.uni",
"ios_bundle_id" : "com.shun777.app",
"isCloud" : true,
"bought" : 1,
"pid" : "6577",
@@ -163,7 +184,10 @@
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
"usingComponents" : true,
"unipush" : {
"enable" : true
}
},
"mp-alipay" : {
"usingComponents" : true
@@ -199,6 +223,9 @@
"treeShaking" : {
"enable" : true
}
},
"unipush" : {
"enable" : true
}
}
}
+16 -11
View File
@@ -1,12 +1,17 @@
{
"dependencies": {
"@openim/client-sdk": "^0.0.11-ahpha.1",
"date-fns": "^2.30.0",
"dayjs": "^1.11.6",
"grapheme-splitter": "^1.0.4",
"image-tools": "^1.4.0",
"md5": "^2.3.0",
"openim-uniapp-polyfill": "^1.4.1",
"uuid": "^9.0.0"
}
}
"dependencies": {
"@openim/client-sdk": "^0.0.11-ahpha.1",
"crypto-js": "^4.2.0",
"date-fns": "^2.30.0",
"dayjs": "^1.11.6",
"grapheme-splitter": "^1.0.4",
"image-tools": "^1.4.0",
"md5": "^2.3.0",
"openim-uniapp-polyfill": "^1.4.4",
"uuid": "^9.0.0"
},
"devDependencies": {
"worker-loader": "^3.0.8",
"worker-plugin": "^5.0.1"
}
}
+214 -31
View File
@@ -2,32 +2,40 @@
"pages": [
//pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/login/index"
"path": "pages/common/login/index"
},
{
"path": "pages/login/registerOrForget/index"
"path": "pages/index/launch",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/login/setSelfInfo/index"
"path": "pages/index/guide",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/login/setPassword/index"
"path": "pages/common/registerOrForget/index"
},
{
"path": "pages/login/verifyCode/index"
"path": "pages/common/setSelfInfo/index"
},
{
"path": "pages/profile/index/index"
"path": "pages/common/setPassword/index"
},
{
"path": "pages/common/verifyCode/index"
},
{
"path": "pages/user/index/index"
},
{
"path": "pages/conversation/conversationList/index",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"disableScroll": true,
"app-plus": {
"bounce": "none"
}
"enablePullDownRefresh": true
}
},
{
@@ -135,6 +143,14 @@
"disableScroll": true
}
},
{
"path": "pages/common/contactChoose/chooseGroupMember",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"disableScroll": true
}
},
{
"path": "pages/common/createGroup/index",
"style": {
@@ -199,21 +215,21 @@
}
},
{
"path": "pages/profile/selfInfo/index",
"path": "pages/user/selfInfo/index",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
},
{
"path": "pages/profile/accountSetting/index",
"path": "pages/user/accountSetting/index",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
},
{
"path": "pages/profile/blockList/index",
"path": "pages/user/blockList/index",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
@@ -241,14 +257,14 @@
}
},
{
"path": "pages/profile/about/index",
"path": "pages/user/about/index",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
},
{
"path": "pages/workbench/index/index",
"path": "pages/find/index/index",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
@@ -257,7 +273,11 @@
{
"path": "pages/common/upgrade",
"style": {
"navigationBarTitleText": ""
"navigationStyle": "custom",
"backgroundColor": "transparent",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "系统更新",
"enablePullDownRefresh": false
}
},
{
@@ -274,31 +294,32 @@
}
},
{
"path": "pages/workbench/friend-circle/friend-circle",
"path": "pages/find/friend-circle/friend-circle",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": true
}
},
{
"path": "pages/find/friend-circle/releaseFriendCircle",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/workbench/friend-circle/releaseFriendCircle",
"path": "pages/find/friend-circle/chooseLocation",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/workbench/friend-circle/chooseLocation",
"path": "pages/find/friend-circle/chooseCircleBgImg",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/workbench/friend-circle/chooseCircleBgImg",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/workbench/friend-circle/builtinBgImg",
"path": "pages/find/friend-circle/builtinBgImg",
"style": {
"navigationBarTitleText": ""
}
@@ -346,7 +367,163 @@
}
},
{
"path": "pages/workbench/near/near",
"path": "pages/find/near/near",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/user/vip/vip",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/conversation/groupAlbum/groupAlbum",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/user/selfInfo/change_bio",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/find/shake/index",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/find/shake/linked_list",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/find/shake/set",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/find/shake/user_detail",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/find/shake/friend_home",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/find/shake/agree_apply_friend",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/find/shake/chatting/index",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/find/shake/chatting/zhuanfa_pop",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/find/music/index",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/find/music/song_list",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/user/service/index",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/user/service/bill/index",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/user/service/bill/detail",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/user/service/bill/question",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/user/service/bill/statistics",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/user/service/set",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/user/service/wallet",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/user/service/wallet_option",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/user/service/wallet_pay",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/user/service/wallet_sub_page/bind_bank",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/user/service/wallet_sub_page/lin_qian_tong",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/user/service/wallet_sub_page/qin_shu_ka",
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "pages/user/service/wallet_sub_page/fenfu-page",
"style": {
"navigationBarTitleText": ""
}
@@ -372,13 +549,13 @@
"text": "通讯录"
},
{
"pagePath": "pages/workbench/index/index",
"pagePath": "pages/find/index/index",
"iconPath": "./static/images/tabbar/workbench.png",
"selectedIconPath": "static/images/tabbar/workbench_active.png",
"text": "发现"
},
{
"pagePath": "pages/profile/index/index",
"pagePath": "pages/user/index/index",
"iconPath": "./static/images/tabbar/profile.png",
"selectedIconPath": "static/images/tabbar/profile_active.png",
"text": "我"
@@ -391,5 +568,11 @@
"app-plus": {
"bounce": "none"
}
}
},
"easycom": {
"autoscan": true,
"custom": {
"^c-(.*)": "@/components/$1/index.vue"
}
}
}
+54 -8
View File
@@ -1,20 +1,38 @@
<template>
<view class="n-ps-all-base">
<uv-parse v-if="content" :content="content"></uv-parse>
<view class="n-flex-row" v-if="showButton" :style="{marginTop:'100rpx'}">
<uv-button class="n-flex-1 n-ms-base" v-for="(item, idx) in buttonList" :key="idx" @click="switchButton(idx, item)" :text="item.title" :icon="'/static/image/' + item.value + '.png'" :plain="current==idx" :color="current==idx ? item.color:'#f8f8f8'" :customStyle="{height:'80rpx'}" :customTextStyle="{color:current==idx ? item.color:'#333333',marginLeft:'20rpx'}" shape="circle" color="#f8f8f8" throttleTime="1000"></uv-button>
<uni-nav-bar
left-icon="back"
@clickLeft="goto(1)"
fixed
statusBar
:title="title">
</uni-nav-bar>
<view class="u-content">
<u-parse
v-if="content"
:domain="config.website"
:previewImg="false"
:showImgMenu="false"
:content="content">
</u-parse>
</view>
<uv-empty :show="empty" icon="/static/image/empty.png" text="暂无数据~" width="200" marginTop="100"></uv-empty>
<view class="n-flex-row" v-if="showButton" :style="{marginTop:'100rpx'}">
<u-button class="n-flex-1 n-ms-base" v-for="(item, idx) in buttonList" :key="idx" @click="switchButton(idx, item)" :text="item.title" :icon="'/static/image/' + item.value + '.png'" :plain="current==idx" :color="current==idx ? item.color:'#f8f8f8'" :customStyle="{height:'80rpx'}" :customTextStyle="{color:current==idx ? item.color:'#333333',marginLeft:'20rpx'}" shape="circle" color="#f8f8f8" throttleTime="1000"></u-button>
</view>
<u-empty :show="empty" icon="/static/image/empty.png" text="暂无数据~" width="200" marginTop="100"></u-empty>
</view>
</template>
<script>
import {getArticle} from '@/api/login.js'
import {getSpage,getArticle} from '@/api/login.js'
import { mapGetters } from "vuex";
import util from "@/util"
export default {
data() {
return {
empty: false,
title:"",
content: '',
current: null,
questionId: 0,
@@ -28,9 +46,14 @@
onLoad(evt) {
if(evt.type=='config') this.setConfig(evt)
if(evt.type=='question') this.setQuestion(evt)
if(evt.type=='article') this.setArticle(evt)
if(evt.type=='spage') this.setSpage(evt)
this.title = evt.title;
uni.setNavigationBarTitle({title:evt.title})
},
computed:{
...mapGetters(["config"]),
},
methods: {
// 设置配置内容
setConfig(evt) {
@@ -47,15 +70,38 @@
this.questionId = evt.id
this.showButton = true
},
setArticle(evt) {
this.showButton = false;
getArticle(evt.id).then(res=>{
this.content = res.content
}).catch(e=>{
console.log(e);
})
},
setSpage(evt) {
this.showButton = false;
getSpage(evt.name).then(res=>{
this.content = res.content
}).catch(e=>{
console.log(e);
})
},
// 切换按钮
switchButton(idx, item) {
this.current = idx
getArticle({id:this.questionId,type:item.value})
}
},
goto:util.goto
}
}
</script>
<style>
<style lang="scss" scoped>
.u-content {
padding: 24rpx;
font-size: 32rpx;
color: $u-content-color;
line-height: 1.6;
}
</style>
+55 -37
View File
@@ -1,21 +1,27 @@
<template>
<view class="contact_choose_container">
<custom-nav-bar title="联系人">
<view slot="more" style="margin-right: 10rpx;">
<u-button type="primary" @click="confirm" :disabled="!isConfirmEnable">确定</u-button>
</view>
</custom-nav-bar>
<uni-nav-bar
left-icon="back"
@clickLeft="uni.$u.route({type:'back'})"
statusBar
title="联系人">
<template v-slot:right>
<u-button type="primary" size="mini" @click="confirm" :disabled="!isConfirmEnable">确定</u-button>
</template>
</uni-nav-bar>
<view class="search_bar_wrap">
<u-search shape="square" placeholder="搜索" :showAction="false" v-model="keyword" />
</view>
<view class="tab_container">
<template v-if="activeTab === 0">
<setting-item @click="tabChange(tabs[0].idx)" :title="tabs[0].title" :border="false" />
<div @click="tabChange(tabs[0].idx)" style="display: flex; align-items: center;justify-content: space-between;padding: 20rpx 40rpx;">
{{ tabs[0].title }}
<u-icon name="arrow-right" />
</div>
<view class="tab_pane">
<template v-for="cell in conversationList">
<template v-if="cell.groupID">
<template v-if="cell.groupID && type!='Invite'">
<user-item
v-if="allowGroup"
@itemClick="updateCheckedUserOrGroup"
@@ -25,7 +31,7 @@
:checkVisible="muitple"
:item="cell" :key="cell.groupID" />
</template>
<template v-if="cell.userID">
<template v-if="cell.userID && !cell.userID.startsWith('system')">
<user-item
@itemClick="updateCheckedUserOrGroup"
@updateCheck="updateCheckedUserOrGroup"
@@ -54,23 +60,19 @@
</template>
<script>
import {mapGetters} from "vuex";
import { mapGetters } from "vuex";
import {ContactChooseTypes} from "@/constant";
import {formatChooseData,toastWithCallback} from "@/util/common";
import IMSDK from "openim-uniapp-polyfill";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import IMSDK from "openim-uniapp-polyfill"
import UserItem from "@/components/UserItem/index.vue";
import ChooseIndexList from "@/components/ChooseIndexList/index.vue";
import ChooseIndexFooter from "@/components/ChooseIndexFooter/index.vue";
import SettingItem from "@/components/SettingItem/index.vue";
export default {
components: {
CustomNavBar,
UserItem,
ChooseIndexList,
ChooseIndexFooter,
SettingItem,
ChooseIndexFooter
},
data() {
return {
@@ -94,18 +96,23 @@
};
},
computed: {
...mapGetters([
"storeFriendList",
"storeCurrentConversation",
"storeCurrentUserID",
"storeConversationList",
]),
...mapGetters(["storeCurrentUserID","storeFriendList","storeConversationList",'storeCurrentConversation']),
getChooseData() {
const _this = this;
let list = [...this.storeFriendList];
list = list.filter((item)=>{
return !item.userID.startsWith('system') && !item.userID.startsWith('official_team') && item.userID !== this.storeCurrentUserID
});
if(!this.allowGroup){
list = list.filter((item)=>{
return !item.groupID
});
}
if (this.keyword) {
return {
indexList: ["#"],
dataList: [
this.storeFriendList.filter(
list.filter(
(friend) =>
friend.nickname.includes(this.keyword) ||
friend.remark.includes(this.keyword)
@@ -113,14 +120,22 @@
],
};
}
return formatChooseData(this.storeFriendList);
return formatChooseData(list);
},
conversationList(){
const _this = this;
let list = [...this.storeConversationList];
list = list.filter((item)=>{
return !item.userID.startsWith('system') && !item.userID.startsWith('official_team') && item.userID !== this.storeCurrentUserID
});
if(!this.allowGroup){
list = list.filter((item)=>{
return !item.groupID
});
}
if(this.keyword){
list = list.filter((item)=>{77
list = list.filter((item)=>{
return item.showName.indexOf(_this.kw)>-1 || item.userID.indexOf(_this.kw)>-1 || item.groupID.indexOf(_this.kw)>-1
})
}
@@ -165,18 +180,16 @@
}
},
onLoad(options) {
const {groupID,type,checkedUserIDList,muitple} = options;
const {groupID,type,checkedUserIDList,muitple,allowType} = options;
this.type = type;
if(allowType){
this.allowGroup = allowType === 'All';
}
this.groupID = groupID;
if(muitple){
this.muitple = muitple;
}
//this.muitple = true;
this.checkedUserIDList = checkedUserIDList ? JSON.parse(checkedUserIDList) : [];
//console.log(this.checkedUserIDList);
//console.log(this.groupID);
//console.log(this.muitple);
//console.log(this.type);
if (this.type === ContactChooseTypes.Invite) {
this.allowGroup = false;
this.checkDisabledUser();
@@ -235,6 +248,7 @@
}
},
confirm() {
const _this = this;
//console.log(this.checkedUserIDList,this.checkedGroupIDList);
//return false;
// if (this.activeTab) {
@@ -262,16 +276,20 @@
return;
}
if (this.type === ContactChooseTypes.Invite) {
console.log(_this.getCheckedUserInfo,_this.groupID);
IMSDK.asyncApi(IMSDK.IMMethods.InviteUserToGroup, IMSDK.uuid(), {
groupID: this.groupID,
groupID: _this.groupID,
reason: "",
userIDList: this.getCheckedUserInfo.map((user) => user.userID),
userIDList: _this.getCheckedUserInfo.map((user) => user.userID),
})
.then(() => {
toastWithCallback("操作成功", () => uni.navigateBack());
this.comfirmLoading = false;
_this.comfirmLoading = false;
})
.catch(() => toastWithCallback("操作失败"));
.catch((e) => {
console.log(e);
toastWithCallback("操作失败")
});
return;
}
@@ -294,7 +312,7 @@
}
.contact_choose_container {
height: 100vh;
min-height: 100vh;
display: flex;
flex-direction: column;
@@ -306,7 +324,6 @@
.tab_container {
@include colBox(false);
flex: 1;
overflow: hidden;
.setting_item {
padding: 32rpx 36rpx;
@@ -343,6 +360,7 @@
flex-direction: column;
flex: 1;
overflow: hidden;
overflow-y: scroll;
.member_list {
flex: 1;
@@ -0,0 +1,260 @@
<template>
<view class="contact_choose_container">
<uni-nav-bar
left-icon="back"
@clickLeft="uni.$u.route({type:'back'})"
statusBar
title="群成员">
<template v-slot:right>
<u-button type="primary" size="mini" @click="confirm" :disabled="!isConfirmEnable">确定</u-button>
</template>
</uni-nav-bar>
<view class="search_bar_wrap">
<u-search shape="square" placeholder="搜索" :showAction="false" v-model="keyword" />
</view>
<view class="tab_container">
<view class="tab_pane">
<choose-index-list
@updateCheck="updateCheckedUser"
:indexList="getChooseData.indexList"
:itemArr="getChooseData.dataList"
:checkedIDList="checkedUserIDList"
:disabledIDList="disabledUserIDList"
:showCheck="muitple" />
</view>
</view>
</view>
</template>
<script>
import {formatChooseData,toastWithCallback} from "@/util/common";
import IMSDK from "openim-uniapp-polyfill"
import UserItem from "@/components/UserItem/index.vue";
import ChooseIndexList from "@/components/ChooseIndexList/index.vue";
export default {
components: {
UserItem,
ChooseIndexList
},
data() {
return {
keyword: "",
groupID: "",
groupMemberList:[],
hideUserIDList: [],
checkedUserIDList: [],
disabledUserIDList: [],
comfirmLoading: false,
muitple:true
};
},
computed: {
getChooseData() {
const list = [...this.groupMemberList];
if (this.keyword) {
return {
indexList: ["#"],
dataList: [
list.filter(
(friend) =>{
if(friend.nickname && friend.nickname.indexOf(this.keyword) !==-1){
return true;
}
if(friend.remark && friend.remark.includes(this.keyword)){
return true;
}
return false;
}
),
],
};
}
return formatChooseData(list);
},
getCheckedUserInfo() {
const tmpUserIDList = [...this.checkedUserIDList];
const checkedFriends = this.groupMemberList.filter((friend) => {
const idx = tmpUserIDList.findIndex(
(userID) => userID === friend.userID
);
if (idx > -1) {
tmpUserIDList.splice(idx, 1);
}
return idx > -1;
});
return [...checkedFriends];
},
isConfirmEnable(){
if(this.checkedUserIDList.length){
return true;
}
return false;
}
},
async onLoad(options) {
const {groupID,checkedUserIDList,hideUserIDList,muitple} = options;
this.groupID = groupID;
if(muitple){
this.muitple = muitple;
}
this.checkedUserIDList = checkedUserIDList ? JSON.parse(checkedUserIDList) : [];
this.hideUserIDList = hideUserIDList ? JSON.parse(hideUserIDList) : [];
const list = await this.getGroupMemberList();
this.groupMemberList = list.filter(
(friend) =>{
return false == this.hideUserIDList.includes(friend.userID);
}
);
if (this.checkedUserIDList.length > 0) {
this.checkDisabledUser();
}
},
methods: {
async getGroupMemberList() {
const _this = this;
return new Promise((resolve,reject)=>{
if (!_this.groupID) {
return reject('groupid is null');
}
IMSDK.asyncApi(IMSDK.IMMethods.GetGroupMemberList, IMSDK.uuid(), {
groupID: _this.groupID,
filter: 0,
offset: 0,
count: 6,
}).then(({ data }) => {
return resolve(data)
}).catch(e=>{
return reject(e);
})
});
},
checkDisabledUser() {
const friendIDList = this.groupMemberList.map((friend) => friend.userID);
IMSDK.asyncApi("getUsersInGroup", IMSDK.uuid(), {
groupID: this.groupID,
userIDList: friendIDList,
}).then(({data}) => {
this.disabledUserIDList = data;
});
},
tabChange(idx) {
this.keyword = "";
this.activeTab = idx;
},
updateCheckedUser({userID}) {
if(!this.muitple){
this.checkedUserIDList = [userID];
this.confirm();
}else{
if (this.checkedUserIDList.includes(userID)) {
const idx = this.checkedUserIDList.findIndex((item) => item === userID);
const tmpArr = [...this.checkedUserIDList];
tmpArr.splice(idx, 1);
this.checkedUserIDList = [...tmpArr];
} else {
this.checkedUserIDList = [...this.checkedUserIDList, userID];
}
}
},
confirm() {
this.comfirmLoading = true;
try{
const eventChannel = this.getOpenerEventChannel();
eventChannel.emit('onSelectedConfirm', this.getCheckedUserInfo);
}catch(e){
console.log(e);
}
this.comfirmLoading = false;
uni.navigateBack({
delta: 1,
});
this.comfirmLoading = false;
},
},
onBackPress() {
return false;
},
};
</script>
<style lang="scss" scoped>
::v-deep.u-popup {
flex: none;
}
.contact_choose_container {
height: 100vh;
display: flex;
flex-direction: column;
.search_bar_wrap {
height: 34px;
padding: 12px 22px;
}
.tab_container {
@include colBox(false);
flex: 1;
overflow: hidden;
.setting_item {
padding: 32rpx 36rpx;
}
.title {
height: 60rpx;
display: flex;
justify-content: flex-start;
align-items: center;
// padding: 16rpx 8rpx;
background: #f8f9fa;
color: #8e9ab0;
font-size: 24rpx;
}
.tabs_bar {
@include vCenterBox();
justify-content: space-evenly;
.tab_item {
@include colBox(false);
align-items: center;
image {
width: 50px;
height: 50px;
}
}
}
.tab_pane {
display: flex;
flex-direction: column;
flex: 1;
overflow: hidden;
.member_list {
flex: 1;
height: 80% !important;
::v-deepuni-scroll-view {
max-height: 100% !important;
}
}
.user_list {
height: 100% !important;
}
.member_anchor {
background-color: #f8f8f8 !important;
border: none !important;
}
}
}
}
</style>
+54 -20
View File
@@ -1,6 +1,11 @@
<template>
<view class="contact_choose_container">
<custom-nav-bar title="联系人" />
<uni-nav-bar
left-icon="back"
@clickLeft="uni.$u.route({type:'back'})"
statusBar
title="联系人">
</uni-nav-bar>
<view class="search_bar_wrap">
<u-search shape="square" placeholder="搜索" :showAction="false" v-model="keyword" />
@@ -8,14 +13,17 @@
<view class="tab_container">
<template v-if="activeTab === 0">
<setting-item @click="tabChange(tabs[0].idx)" :title="tabs[0].title" :border="false" />
<div @click="tabChange(tabs[0].idx)" style="display: flex; align-items: center;justify-content: space-between;padding: 20rpx 40rpx;">
{{ tabs[0].title }}
<u-icon name="arrow-right" />
</div>
<view class="tab_pane">
<user-item
@updateCheck="updateCheckedUser"
:checked="checkedUserIDList.includes(cell.userID)"
:disabled="disabledUserIDList.includes(cell.userID)"
:checkVisible="true"
v-for="cell in storeConversationList" :item="cell" :key="cell.userID" />
v-for="cell in conversationList" :item="cell" :key="cell.userID" />
</view>
</template>
@@ -33,23 +41,19 @@
</template>
<script>
import {mapGetters} from "vuex";
import { mapGetters } from "vuex";
import {ContactChooseTypes} from "@/constant";
import {formatChooseData,toastWithCallback} from "@/util/common";
import IMSDK from "openim-uniapp-polyfill";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import IMSDK from "openim-uniapp-polyfill"
import UserItem from "@/components/UserItem/index.vue";
import ChooseIndexList from "@/components/ChooseIndexList/index.vue";
import ChooseIndexFooter from "@/components/ChooseIndexFooter/index.vue";
import SettingItem from "@/components/SettingItem/index.vue";
export default {
components: {
CustomNavBar,
UserItem,
ChooseIndexList,
ChooseIndexFooter,
SettingItem,
ChooseIndexFooter
},
data() {
return {
@@ -70,18 +74,42 @@
};
},
computed: {
...mapGetters([
"storeFriendList",
"storeCurrentConversation",
"storeCurrentUserID",
"storeConversationList",
]),
...mapGetters(["storeFriendList","storeConversationList",'storeCurrentConversation',"storeCurrentUserID"]),
conversationList(){
const _this = this;
let list = [...this.storeConversationList];
list = list.filter((item)=>{
return !item.userID.startsWith('system') && !item.userID.startsWith('official_team') && item.userID !== this.storeCurrentUserID
});
if(!this.allowGroup){
list = list.filter((item)=>{
return !item.groupID
});
}
if(this.keyword){
list = list.filter((item)=>{
return item.showName.indexOf(_this.kw)>-1 || item.userID.indexOf(_this.kw)>-1 || item.groupID.indexOf(_this.kw)>-1
})
}
return list;
},
getChooseData() {
const _this = this;
let list = [...this.storeFriendList];
list = list.filter((item)=>{
return !item.userID.startsWith('system') && !item.userID.startsWith('official_team') && item.userID !== this.storeCurrentUserID
});
if(!this.allowGroup){
list = list.filter((item)=>{
return !item.groupID
});
}
if (this.keyword) {
return {
indexList: ["#"],
dataList: [
this.storeFriendList.filter(
list.filter(
(friend) =>
friend.nickname.includes(this.keyword) ||
friend.remark.includes(this.keyword)
@@ -89,7 +117,7 @@
],
};
}
return formatChooseData(this.storeFriendList);
return formatChooseData(list);
},
getCheckedInfo() {
const tmpUserIDList = [...this.checkedUserIDList];
@@ -106,10 +134,16 @@
},
},
onLoad(options) {
const {groupID,type,checkedUserIDList,limit} = options;
//console.log(this.storeFriendList);
//cardInfo
const {groupID,type,checkedUserIDList,muitple,allowType,limit} = options;
this.type = type;
if(allowType){
this.allowGroup = allowType === 'All';
}
this.groupID = groupID;
if(muitple){
this.muitple = muitple;
}
this.checkedUserIDList = checkedUserIDList ? JSON.parse(checkedUserIDList) : [];
if (this.type === ContactChooseTypes.Invite) {
this.checkDisabledUser();
+2 -6
View File
@@ -48,11 +48,7 @@
<script>
import { ContactChooseTypes } from "@/constant";
import IMSDK, {
GroupType,
IMMethods,
SessionType,
} from "openim-uniapp-polyfill";
import IMSDK, {GroupType,IMMethods,SessionType,} from "openim-uniapp-polyfill";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import MyAvatar from "@/components/MyAvatar/index.vue";
import { navigateToDesignatedConversation } from "@/util/imCommon";
@@ -88,7 +84,7 @@ export default {
(member) => member.userID,
);
uni.navigateTo({
url: `/pages/common/contactChoose/index?type=${ContactChooseTypes.GetList}&checkedUserIDList=${JSON.stringify(checkedIDList)}`,
url: `/pages/common/contactChoose/index?type=${ContactChooseTypes.GetList}&checkedUserIDList=${JSON.stringify(checkedIDList)}&allowType=User`,
});
},
complateCreate() {
+81 -82
View File
@@ -1,94 +1,93 @@
<template>
<view class="details_container">
<custom-nav-bar title="个人资料" />
<view class="details_container">
<custom-nav-bar title="个人资料" />
<view class="info_list">
<user-info-row-item class="info_item" lable="头像" arrow>
<my-avatar
:src="sourceInfo.faceURL"
:desc="sourceInfo.nickname"
size="26"
/>
</user-info-row-item>
<user-info-row-item class="info_item" lable="昵称" arrow>
<text class="right_content">{{ sourceInfo.nickname }}</text>
</user-info-row-item>
<user-info-row-item class="info_item" lable="性别" arrow>
<text class="right_content">{{ getGender }}</text>
</user-info-row-item>
<user-info-row-item class="info_item" lable="生日" arrow>
<text class="right_content">{{ getBirthStr }}</text>
</user-info-row-item>
</view>
<view class="info_list">
<user-info-row-item class="info_item" lable="头像" arrow>
<my-avatar :src="sourceInfo.faceURL" :desc="sourceInfo.nickname" size="26" />
</user-info-row-item>
<user-info-row-item class="info_item" lable="昵称" arrow>
<text class="right_content">{{ sourceInfo.nickname }}</text>
</user-info-row-item>
<user-info-row-item class="info_item" lable="性别" arrow>
<text class="right_content">{{ getGender }}</text>
</user-info-row-item>
<user-info-row-item class="info_item" lable="生日" arrow>
<text class="right_content">{{ getBirthStr }}</text>
</user-info-row-item>
</view>
<view class="info_list">
<user-info-row-item class="info_item" lable="手机号码" arrow>
<text class="right_content">{{ sourceInfo.phoneNumber || "-" }}</text>
</user-info-row-item>
<user-info-row-item class="info_item" lable="邮箱" arrow>
<text class="right_content">{{ sourceInfo.email || "-" }}</text>
</user-info-row-item>
</view>
</view>
<view class="info_list">
<user-info-row-item class="info_item" lable="手机号码" arrow>
<text class="right_content">{{ sourceInfo.phoneNumber || "-" }}</text>
</user-info-row-item>
<user-info-row-item class="info_item" lable="邮箱" arrow>
<text class="right_content">{{ sourceInfo.email || "-" }}</text>
</user-info-row-item>
</view>
</view>
</template>
<script>
import dayjs from "dayjs";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import MyAvatar from "@/components/MyAvatar/index.vue";
import UserInfoRowItem from "../userCard/components/UserInfoRowItem.vue";
export default {
components: {
CustomNavBar,
MyAvatar,
UserInfoRowItem,
},
data() {
return {
sourceInfo: {},
};
},
computed: {
getGender() {
if (this.sourceInfo.gender === 1) {
return "男";
}
if (this.sourceInfo.gender === 2) {
return "女";
}
return "保密";
},
getBirthStr() {
const birth = this.sourceInfo.birth ?? 0;
return dayjs(birth).format("YYYY-MM-DD");
},
},
onLoad(options) {
const { sourceInfo } = options;
this.sourceInfo = JSON.parse(sourceInfo);
},
};
import dayjs from "dayjs";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import MyAvatar from "@/components/MyAvatar/index.vue";
import UserInfoRowItem from "../userCard/components/UserInfoRowItem.vue";
import util from "@/util/index.js"
export default {
components: {
CustomNavBar,
MyAvatar,
UserInfoRowItem,
},
data() {
return {
sourceInfo: {},
};
},
computed: {
getGender() {
if (this.sourceInfo.gender === 1) {
return "男";
}
if (this.sourceInfo.gender === 2) {
return "女";
}
return "保密";
},
getBirthStr() {
const birth = this.sourceInfo.birth ?? 0;
return dayjs(birth).format("YYYY-MM-DD");
},
},
onLoad(options) {
const {
sourceInfo
} = options;
this.sourceInfo = util.aesdecode(sourceInfo);
},
};
</script>
<style lang="scss" scoped>
.details_container {
@include colBox(false);
height: 100vh;
background-color: #f6f6f6;
.details_container {
@include colBox(false);
height: 100vh;
background-color: #f6f6f6;
.info_list {
border-radius: 6px;
overflow: hidden;
margin: 24rpx;
.info_list {
border-radius: 6px;
overflow: hidden;
margin: 24rpx;
.info_item {
background-color: #fff;
// border-bottom: 1px solid rgba(153, 153, 153, 0.3);
.info_item {
background-color: #fff;
// border-bottom: 1px solid rgba(153, 153, 153, 0.3);
.right_content {
color: #999;
}
}
}
}
</style>
.right_content {
color: #999;
}
}
}
}
</style>
+250 -276
View File
@@ -1,309 +1,283 @@
<template>
<view class="group_card_container">
<custom-nav-bar title="" />
<u-toast ref="uToast"></u-toast>
<view class="group_card_container">
<custom-nav-bar title="" />
<u-toast ref="uToast"></u-toast>
<view class="main">
<view class="base_info">
<my-avatar :src="sourceGroupInfo.faceURL" :isGroup="true" size="48" />
<view>
<view class="group_name">
<text>{{ sourceGroupInfo.groupName }}</text>
<text v-if="!!sourceGroupInfo.memberCount"
>{{ sourceGroupInfo.memberCount }}</text
>
</view>
<view class="create_time">
<u-icon name="clock" color="#999" size="14"></u-icon>
<text>{{ getCreateTime }}</text>
</view>
</view>
</view>
<view class="main">
<view class="base_info">
<my-avatar :src="sourceGroupInfo.faceURL" :isGroup="true" size="48" />
<view>
<view class="group_name">
<text>{{ sourceGroupInfo.groupName }}</text>
<text v-if="!!sourceGroupInfo.memberCount">{{ sourceGroupInfo.memberCount }}</text>
</view>
<view class="create_time">
<u-icon name="clock" color="#999" size="14"></u-icon>
<text>{{ getCreateTime }}</text>
</view>
</view>
</view>
<view
v-if="!!sourceGroupInfo.memberCount"
@click="toMemberList"
class="member_row info_row"
>
<view class="member_desc">
<text>群成员</text>
<text class="member_count">{{
<view v-if="!!sourceGroupInfo.memberCount" @click="toMemberList" class="member_row info_row">
<view class="member_desc">
<text>群成员</text>
<text class="member_count">{{
`${sourceGroupInfo.memberCount}`
}}</text>
<u-icon name="arrow-right" color="#999" size="18"></u-icon>
</view>
<view class="member_list">
<my-avatar
v-for="member in getRenderMemberList"
:key="member.userID"
class="member_item"
size="42"
:src="member.faceURL"
:desc="member.nickname"
></my-avatar>
<u-avatar
bgColor="#5496EB"
icon="more-dot-fill"
shape="square"
size="42"
></u-avatar>
</view>
</view>
<u-icon name="arrow-right" color="#999" size="18"></u-icon>
</view>
<view class="member_list">
<my-avatar v-for="member in getRenderMemberList" :key="member.userID" class="member_item" size="42"
:src="member.faceURL" :desc="member.nickname"></my-avatar>
<u-avatar bgColor="#5496EB" icon="more-dot-fill" shape="square" size="42"></u-avatar>
</view>
</view>
<view class="info_row">
<user-info-row-item lable="群ID号" :content="sourceGroupInfo.groupID" />
</view>
</view>
<view class="info_row">
<user-info-row-item lable="群ID号" :content="sourceGroupInfo.groupID" />
</view>
</view>
<view class="action_row">
<u-button type="primary" v-if="!isJoinedGroup" @click="joinGroup"
>申请加入该群</u-button
>
<u-button type="primary" v-else @click="chatingInGroup">发消息</u-button>
</view>
</view>
<view class="action_row">
<u-button type="primary" v-if="!isJoinedGroup" @click="joinGroup">申请加入该群</u-button>
<u-button type="primary" v-else @click="chatingInGroup">发消息</u-button>
</view>
</view>
</template>
<script>
import { GroupMemberListTypes } from "@/constant";
import { navigateToDesignatedConversation } from "@/util/imCommon";
import IMSDK, {
GroupVerificationType,
SessionType,
} from "openim-uniapp-polyfill";
import dayjs from "dayjs";
import MyAvatar from "@/components/MyAvatar/index.vue";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import UserInfoRowItem from "../userCard/components/UserInfoRowItem.vue";
import {GroupMemberListTypes} from "@/constant";
import {navigateToDesignatedConversation} from "@/util/imCommon";
import IMSDK, {GroupVerificationType,SessionType,} from "openim-uniapp-polyfill";
import dayjs from "dayjs";
import util from "@/util/index.js"
import MyAvatar from "@/components/MyAvatar/index.vue";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import UserInfoRowItem from "../userCard/components/UserInfoRowItem.vue";
import userIcon from "static/images/contact_my_friend.png";
import userIcon from "static/images/contact_my_friend.png";
export default {
components: {
CustomNavBar,
MyAvatar,
UserInfoRowItem,
},
data() {
return {
sourceID: "",
isScan: false,
sourceGroupInfo: {},
groupMemberList: [],
};
},
computed: {
isJoinedGroup() {
return (
this.$store.getters.storeGroupList.findIndex(
(group) => group.groupID === this.sourceID,
) !== -1
);
},
getCreateTime() {
return dayjs(this.sourceGroupInfo.createTime).format("YYYY-MM-DD");
},
getRenderMemberList() {
if (this.isJoinedGroup) {
this.groupMemberList;
return this.groupMemberList;
}
const memberCount = this.sourceGroupInfo.memberCount ?? 0;
return new Array(memberCount >= 6 ? 6 : memberCount)
.fill(1)
.map((item, idx) => ({
userID: idx,
src: userIcon,
}));
},
},
onLoad(options) {
const { sourceID, sourceInfo, isScan } = options;
this.isScan = !!isScan;
if (sourceID) {
this.sourceID = sourceID;
this.getSourceGroupInfo();
} else {
const info = JSON.parse(sourceInfo);
this.sourceID = info.groupID;
this.sourceGroupInfo = {
...info,
};
}
this.getGroupMemberList();
},
methods: {
toMemberList() {
if (this.isJoinedGroup) {
this.$store.dispatch("conversation/getCurrentGroup", this.sourceID);
this.$store.dispatch(
"conversation/getCurrentMemberInGroup",
this.sourceID,
);
uni.navigateTo({
url: `/pages/conversation/groupMemberList/index?type=${GroupMemberListTypes.Preview}&groupID=${this.sourceID}`,
});
}
},
joinGroup() {
uni.$u.route("/pages/common/sendAddRequest/index", {
isGroup: true,
sourceID: this.sourceID,
isScan: this.isScan,
notNeedVerification:
this.sourceGroupInfo.needVerification ===
GroupVerificationType.AllNot,
sessionType: SessionType.WorkingGroup,
});
},
chatingInGroup() {
navigateToDesignatedConversation(
this.sourceID,
SessionType.WorkingGroup,
).catch(() => this.showToast("获取会话信息失败"));
},
async getSourceGroupInfo() {
let info = null;
if (this.isJoinedGroup) {
info = this.$store.getters.storeGroupList.find(
(group) => group.groupID === this.sourceID,
);
} else {
try {
const { data } = await IMSDK.asyncApi(
IMSDK.IMMethods.GetSpecifiedGroupsInfo,
IMSDK.uuid(),
[this.sourceID],
);
info = data[0] ?? {};
} catch (e) {
info = {};
}
}
this.sourceGroupInfo = {
...info,
};
},
getGroupMemberList() {
if (this.isJoinedGroup) {
IMSDK.asyncApi(IMSDK.IMMethods.GetGroupMemberList, IMSDK.uuid(), {
groupID: this.sourceID,
filter: 0,
offset: 0,
count: 6,
}).then(({ data }) => {
this.groupMemberList = [...data];
});
}
},
showToast(message) {
this.$refs.uToast.show({
message,
});
},
},
};
export default {
components: {CustomNavBar,MyAvatar,UserInfoRowItem,},
data() {
return {
sourceID: "",
isScan: false,
sourceGroupInfo: {},
groupMemberList: [],
};
},
computed: {
isJoinedGroup() {
return (this.$store.getters.storeGroupList.findIndex((group) => group.groupID === this.sourceID,) !== -1);
},
getCreateTime() {
return dayjs(this.sourceGroupInfo.createTime).format("YYYY-MM-DD");
},
getRenderMemberList() {
if (this.isJoinedGroup) {
this.groupMemberList;
return this.groupMemberList;
}
const memberCount = this.sourceGroupInfo.memberCount ?? 0;
return new Array(memberCount >= 6 ? 6 : memberCount)
.fill(1)
.map((item, idx) => ({
userID: idx,
src: userIcon,
}));
},
},
onLoad(options) {
const {sourceID,sourceInfo,isScan} = options;
this.isScan = !!isScan;
if (sourceID) {
this.sourceID = sourceID;
this.getSourceGroupInfo();
} else {
const info = util.aesdecode(sourceInfo);
this.sourceID = info.groupID;
this.sourceGroupInfo = {
...info,
};
}
this.getGroupMemberList();
},
methods: {
toMemberList() {
if (this.isJoinedGroup) {
this.$store.dispatch("conversation/getCurrentGroup", this.sourceID);
this.$store.dispatch(
"conversation/getCurrentMemberInGroup",
this.sourceID,
);
uni.navigateTo({
url: `/pages/conversation/groupMemberList/index?type=${GroupMemberListTypes.Preview}&groupID=${this.sourceID}`,
});
}
},
joinGroup() {
uni.$u.route("/pages/common/sendAddRequest/index", {
isGroup: true,
sourceID: this.sourceID,
isScan: this.isScan,
notNeedVerification: this.sourceGroupInfo.needVerification ===
GroupVerificationType.AllNot,
sessionType: SessionType.WorkingGroup,
});
},
chatingInGroup() {
navigateToDesignatedConversation(
this.sourceID,
SessionType.WorkingGroup,
).catch(() => this.showToast("获取会话信息失败"));
},
async getSourceGroupInfo() {
let info = null;
if (this.isJoinedGroup) {
info = this.$store.getters.storeGroupList.find(
(group) => group.groupID === this.sourceID,
);
} else {
try {
const {
data
} = await IMSDK.asyncApi(
IMSDK.IMMethods.GetSpecifiedGroupsInfo,
IMSDK.uuid(),
[this.sourceID],
);
info = data[0] ?? {};
} catch (e) {
info = {};
}
}
this.sourceGroupInfo = {
...info,
};
},
getGroupMemberList() {
if (this.isJoinedGroup) {
IMSDK.asyncApi(IMSDK.IMMethods.GetGroupMemberList, IMSDK.uuid(), {
groupID: this.sourceID,
filter: 0,
offset: 0,
count: 6,
}).then(({
data
}) => {
this.groupMemberList = [...data];
});
}
},
showToast(message) {
this.$refs.uToast.show({
message,
});
},
},
};
</script>
<style lang="scss" scoped>
.group_card_container {
@include colBox(false);
height: 100vh;
background-color: #f6f6f6;
.group_card_container {
@include colBox(false);
height: 100vh;
background-color: #f6f6f6;
.main {
display: flex;
flex-direction: column;
flex: 1;
}
.main {
display: flex;
flex-direction: column;
flex: 1;
}
.base_info {
@include vCenterBox();
background-color: #fff;
padding: 44rpx;
margin-bottom: 18rpx;
.base_info {
@include vCenterBox();
background-color: #fff;
padding: 44rpx;
margin-bottom: 18rpx;
.u-avatar {
margin-right: 24rpx;
}
.u-avatar {
margin-right: 24rpx;
}
.group_name {
display: flex;
margin-bottom: 12rpx;
}
.group_name {
display: flex;
margin-bottom: 12rpx;
}
.create_time {
@include vCenterBox();
justify-content: center;
color: #adadad;
font-size: 26rpx;
.create_time {
@include vCenterBox();
justify-content: center;
color: #adadad;
font-size: 26rpx;
.u-icon {
margin-right: 12rpx;
}
}
}
.u-icon {
margin-right: 12rpx;
}
}
}
.member_row {
padding: 24rpx 44rpx;
.member_row {
padding: 24rpx 44rpx;
.member_desc {
margin-bottom: 24rpx;
position: relative;
.member_desc {
margin-bottom: 24rpx;
position: relative;
.member_count {
font-size: 28rpx;
color: #adadad;
margin-left: 24rpx;
}
.member_count {
font-size: 28rpx;
color: #adadad;
margin-left: 24rpx;
}
.u-icon {
position: absolute;
right: 0;
top: 0;
}
}
.u-icon {
position: absolute;
right: 0;
top: 0;
}
}
.member_list {
display: flex;
.member_list {
display: flex;
.member_item {
margin-right: 12rpx;
.member_item {
margin-right: 12rpx;
&:nth-child(7) {
margin-right: 0;
}
}
}
}
&:nth-child(7) {
margin-right: 0;
}
}
}
}
.info_row {
background-color: #fff;
margin-bottom: 24rpx;
.info_row {
background-color: #fff;
margin-bottom: 24rpx;
::v-deep .content {
color: #adadad;
}
}
::v-deep .content {
color: #adadad;
}
}
.action_row {
background-color: #fff;
padding: 44rpx 44rpx;
}
.action_row {
background-color: #fff;
padding: 44rpx 44rpx;
}
.online_state {
@include vCenterBox();
margin-left: 24rpx;
font-size: 24rpx;
color: #999;
.online_state {
@include vCenterBox();
margin-left: 24rpx;
font-size: 24rpx;
color: #999;
.dot {
background-color: #10cc64;
width: 12rpx;
height: 12rpx;
border-radius: 50%;
margin-right: 12rpx;
}
}
}
</style>
.dot {
background-color: #10cc64;
width: 12rpx;
height: 12rpx;
border-radius: 50%;
margin-right: 12rpx;
}
}
}
</style>
@@ -2,7 +2,7 @@
<view class="page_container">
<view class="login">
<view class="logo">
<img src="static/images/logo.png" alt="" />
<img :src="cdn(config.app_logo)" alt="" />
<view class="title">欢迎使用{{ config.name }}</view>
</view>
<u-tabs v-if="1 == 2" :list="list" :current="active" @click="click"></u-tabs>
@@ -32,8 +32,7 @@
</u-form-item>
<u-form-item v-if="active <= 1 && !isPwdLogin" label="" prop="verificationCode">
<u-input v-model="loginInfo.verificationCode" border="surround" placeholder="请输入验证码">
<view class="code_btn" slot="suffix" @click="getCode">
{{ count !== 0 ? `${count} s` : "获取验证码" }}
<view class="code_btn" slot="suffix" @click="getCode">{{ count !== 0 ? `${count} s` : "获取验证码" }}
</view>
</u-input>
</u-form-item>
@@ -67,6 +66,9 @@ import AreaPicker from "@/components/AreaPicker";
import { checkLoginError } from "@/util/common";
import { SmsUserFor } from "@/constant";
import IMSDK from "openim-uniapp-polyfill";
import util from "@/util/index.js"
import config from "@/common/config";
import {getDbDir,toastWithCallback} from "@/util/common.js";
let timer;
@@ -119,6 +121,7 @@ export default {
this.init();
},
methods: {
...util,
click({ index }) {
this.active = index;
},
@@ -143,59 +146,46 @@ export default {
this.loginInfo.email = "commiu@outlook.com";
this.loginInfo.password = "qwe123";
}
//plus.navigator.closeSplashscreen();
},
updateEye() {
this.eying = !this.eying;
},
toRegisterOrForget(isRegister) {
uni.$u.route("/pages/login/registerOrForget/index", {
uni.$u.route("/pages/common/registerOrForget/index", {
isRegister,
});
},
async startLogin() {
// this.$refs.loginForm.validate().then(async (valid) => {
this.loading = true;
this.saveLoginInfo();
let data = {};
try {
data = await businessLogin({
mobile: this.loginInfo.phoneNumber,
email: this.loginInfo.email,
region: `+${this.loginInfo.region}`,
password: this.isPwdLogin ? md5(this.loginInfo.password) : "",
platform: uni.$u.os(),
type: this.active === 0 ? 'mobile' : 'email',
code: this.loginInfo.verificationCode,
});
const { imToken, userID } = data;
this.loading = true;
this.saveLoginInfo();
let data = {};
try {
data = await businessLogin({
mobile: this.loginInfo.phoneNumber,
email: this.loginInfo.email,
region: `+${this.loginInfo.region}`,
password: this.isPwdLogin ? md5(this.loginInfo.password) : "",
platform: uni.$u.os(),
type: this.active === 0 ? 'mobile' : 'email',
code: this.loginInfo.verificationCode,
});
const { imToken, userID } = data;
this.saveLoginProfile(data);
this.$store.commit("user/SET_AUTH_DATA", data);
this.loginInfo.password = "";
// #ifdef APP
await IMSDK.asyncApi(IMSDK.IMMethods.Login, uuidv4(), {
userID,
token: imToken,
});
// await IMSDK.asyncApi(IMSDK.IMMethods.Login, uuidv4(), {
// userID,
// token: imToken,
// });
plus.runtime.restart();
// #endif
this.saveLoginProfile(data);
this.$store.commit("user/SET_AUTH_DATA", data);
this.$store.dispatch("user/getSelfInfo");
this.$store.dispatch("conversation/getConversationList");
this.$store.dispatch("conversation/getUnReadCount");
// this.$store.dispatch("contact/getFriendList");
// this.$store.dispatch("contact/getGrouplist");
this.$store.dispatch("contact/getBlacklist");
this.$store.dispatch("contact/getRecvFriendApplications");
this.$store.dispatch("contact/getSentFriendApplications");
this.$store.dispatch("contact/getRecvGroupApplications");
this.$store.dispatch("contact/getSentGroupApplications");
uni.switchTab({
url: "/pages/conversation/conversationList/index",
});
this.loginInfo.password = "";
} catch (err) {
console.error(err);
uni.$u.toast(checkLoginError(err));
}
this.loading = false;
// });
} catch (err) {
console.error(err);
uni.$u.toast(checkLoginError(err));
}
this.loading = false;
},
saveLoginProfile(data) {
const { imToken, token, userID } = data;
@@ -227,12 +217,13 @@ export default {
return;
}
const options = {
phoneNumber: this.loginInfo.phoneNumber,
region: `+${this.loginInfo.region}`,
usedFor: SmsUserFor.Login,
operationID: Date.now() + "",
};
const options = {
mobile: this.loginInfo.phoneNumber,
email: this.loginInfo.email,
region: `+${this.loginInfo.region}`,
event: "login",
type:"mobile"
};
businessSendSms(options)
.then(() => {
uni.$u.toast("验证码已发送!");
@@ -279,9 +270,10 @@ export default {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
align-items: center;
img {
border-radius: 32rpx;
width: 160rpx;
height: 160rpx;
}
+60 -24
View File
@@ -1,33 +1,39 @@
<template>
<view class="map_page">
<u-navbar left-icon="arrow-left" @leftClick="back" placeholder bgColor="transparent">
<template slot="right">
<u-button type="primary" class="confirm_btn" size="mini" @click="confirm">确定</u-button>
</template>
</u-navbar>
<view style="display: flex; flex-direction: column; height: 100%;">
<!-- 使用web-view嵌入天地图 -->
<ly-map class="ly-map"
v-if="lng && lat"
@onUserEvent="onUserEvent"
ref="map"
:type="type"
:lonlat=[lng,lat]
:map-key="apikey" />
<view class="search_container" v-if="1==2">
<u-search placeholder="日照香炉生紫烟" v-model="keyword"></u-search>
<u-cell-group :customStyle="{backgroundColor:'#FFF'}">
<u-cell title="摇一摇" icon="/static/images/workbench/05.png" :size="cellSize"></u-cell>
<u-cell title="看一看" icon="/static/images/workbench/06.png" :size="cellSize"></u-cell>
<u-cell title="听一听" icon="/static/images/workbench/06.png" :size="cellSize"></u-cell>
<u-cell title="附近" icon="/static/images/workbench/08.png" :size="cellSize"></u-cell>
<u-cell title="购物" icon="/static/images/workbench/09.png" :size="cellSize"></u-cell>
<u-cell title="摇一摇" icon="/static/images/find/05.png" :size="cellSize"></u-cell>
<u-cell title="看一看" icon="/static/images/find/06.png" :size="cellSize"></u-cell>
<u-cell title="听一听" icon="/static/images/find/06.png" :size="cellSize"></u-cell>
<u-cell title="附近" icon="/static/images/find/08.png" :size="cellSize"></u-cell>
<u-cell title="购物" icon="/static/images/find/09.png" :size="cellSize"></u-cell>
</u-cell-group>
</view>
</view>
<view class="map_info" v-if="type == 'viewlocation'">
<view class="left">
<u-text wordWrap="anywhere" size="16" :text="address"></u-text>
</view>
<u-button @click="gotoMap" class="right">
<uni-icons size="36" color="#07c160" type="paperplane-filled"></uni-icons>
</u-button>
</view>
</view>
</template>
<script>
import util from "@/util/index.js"
export default {
data() {
return {
@@ -53,6 +59,7 @@
if(opt.address){
this.address = opt.address;
}
console.log(this.type)
this.init();
},
methods: {
@@ -72,10 +79,12 @@
})
}
},
gotoMap(){
util.toMapAPP(this.lng,this.lat,this.address);
},
onUserEvent(e) {
//console.log(e)
if(this.type=='chooselocation'){
if(e.type == "move"){
if(e.type == "move"){
if(this.type=='chooselocation'){
this.lng = e.lng;
this.lat = e.lat;
this.$refs.map.setMarkers([
@@ -84,10 +93,22 @@
lat: Number(e.lat)
}
]);
return ;
}
if(this.type=='viewlocation'){
return ;
}
return ;
}
if(this.type=='viewlocation'){
if(e.type=='back'){
uni.navigateBack();
return ;
}
if(e.type=='confirm'){
this.confirm();
return ;
}
//console.log(e)
},
/**
* 确定位置按钮点击
@@ -107,14 +128,14 @@
uni.request({
url:url,
success(res){
//console.log(res.data);
console.log(res.data);
const result = res.data.result;
_this.address = result.formatted_address;
console.log( {
lng: result.location.lon,
lat: result.location.lat,
address: result.formatted_address
});
// console.log( {
// lng: result.location.lon,
// lat: result.location.lat,
// address: result.formatted_address
// });
//return 1;
// 通过事件通道返回数据给父页面
const eventChannel = _this.getOpenerEventChannel();
@@ -122,7 +143,9 @@
eventChannel.emit('onConfirm', {
lng: result.location.lon,
lat: result.location.lat,
address: result.formatted_address
name: result.addressComponent.town,
address: result.formatted_address,
addressComponent:result.addressComponent
});
uni.navigateBack();
},
@@ -153,8 +176,21 @@
flex: 1;
width: 100%;
}
}
.confirm_btn{
padding: 30rpx 10rpx;
.map_info{
height: 100rpx;
padding: 50rpx 30rpx;
display: flex;
align-items: center;
justify-content: space-between;
.left{
flex:1;
}
.right{
width: 100rpx;
.u-button{
background-color: #ccc;
}
}
}
}
</style>
+2 -1
View File
@@ -17,6 +17,7 @@
<script>
import IMSDK from "openim-uniapp-polyfill";
import util from "@/util/index.js"
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import {
@@ -56,7 +57,7 @@
isSelfNickname,
sourceInfo
} = options;
this.sourceInfo = JSON.parse(sourceInfo);
this.sourceInfo = util.aesdecode(sourceInfo);
console.log(sourceInfo);
console.log(this.sourceInfo);
this.isRemark = !!isRemark;
@@ -34,15 +34,10 @@
<script>
import AreaPicker from "@/components/AreaPicker";
import {
businessSendSms
} from "@/api/login";
import {
SmsUserFor
} from "@/constant";
import {
checkLoginError
} from "@/util/common";
import {businessSendSms} from "@/api/login";
import {SmsUserFor} from "@/constant";
import {checkLoginError} from "@/util/common";
import util from "@/util/index.js"
export default {
components: {
AreaPicker,
@@ -98,8 +93,8 @@
uni.$u.toast("验证码已发送!");
setTimeout(
() =>
uni.$u.route("/pages/login/verifyCode/index", {
userInfo: JSON.stringify(this.userInfo),
uni.$u.route("/pages/common/verifyCode/index", {
userInfo: util.aesencode(this.userInfo),
isRegister: this.isRegister,
}),
1000,
@@ -112,7 +107,7 @@
});
},
back() {
uni.$u.route("/pages/login/index");
uni.$u.route("/pages/common/login/index");
},
showPicker() {
this.$refs.AreaPicker.init();
+17 -31
View File
@@ -1,24 +1,9 @@
<template>
<view class="scan_page" :style="{height:windowHeight+'px'}">
<uni-nav-bar
left-icon="back"
@clickLeft="back"
fixed
backgroundColor="#FFF"
statusBar
>
<uni-nav-bar left-icon="back" @clickLeft="back" fixed backgroundColor="#FFF" statusBar>
</uni-nav-bar>
<barcode id='1'
class="barcode"
ref="barcode"
:autostart="true"
background="rgb(0,0,0)"
frameColor="#07c160"
scanbarColor="#07c160"
:style="{height:windowHeight+'px'}"
:filters="fil"
@marked="success"
@error="fail">
<barcode id='1' class="barcode" ref="barcode" :autostart="true" background="rgb(0,0,0)" frameColor="#07c160"
scanbarColor="#07c160" :style="{height:windowHeight+'px'}" :filters="fil" @marked="success" @error="fail">
</barcode>
<view class="overlay">
<button class="btn" @click="toStart">开始扫码识别</button>
@@ -33,8 +18,8 @@
export default {
data() {
return {
fil: [0, 2, 1,3,4,12],
windowHeight:500,
fil: [0, 2, 1, 3, 4, 12],
windowHeight: 500,
}
},
@@ -52,10 +37,10 @@
},
methods: {
success(e) {
console.log("success1:" + JSON.stringify(e));
//console.log("success1:" + JSO1N.stringify(e));
},
fail(e) {
console.log("fail1:" + JSON.stringify(e));
//console.log("fail1:" + JSON1.stringify(e));
},
toStart: function() {
this.$refs.barcode.start({
@@ -63,7 +48,7 @@
filename: '_doc/barcode/'
});
},
tocancel:function(){
tocancel: function() {
this.$refs.barcode.cancel();
},
toFlash: function() {
@@ -73,12 +58,11 @@
toscan: function() {
console.log("scan:");
const barcodeModule = uni.requireNativePlugin('barcodeScan');
barcodeModule.scan("/static/barcode1.png"
,(e)=>{
console.log("scan_error:"+JSON.stringify(e));
barcodeModule.scan("/static/barcode1.png", (e) => {
console.log("scan_error:", e);
});
},
back(){
back() {
uni.navigateBack();
}
}
@@ -86,14 +70,15 @@
</script>
<style lang="scss" scoped>
.scan_page{
.scan_page {
width: 750rpx;
position: relative;
.barcode {
width: 750rpx;
background-color: #808080;
}
.btn {
top: 20rpx;
width: 730rpx;
@@ -102,7 +87,8 @@
background-color: #458B00;
border-radius: 10rpx;
}
.overlay{
.overlay {
background: rgba(0, 0, 0, 0.1);
position: absolute;
bottom: 100rpx;
@@ -111,4 +97,4 @@
height: 200rpx;
}
}
</style>
</style>
+5 -6
View File
@@ -32,9 +32,8 @@
import searchGroup from "static/images/contact_add_join_group_fill.png";
import searchUser from "static/images/contact_add_search_user_fill.png";
import {
businessSearchUserInfo
} from "@/api/login";
import {businessSearchUserInfo} from "@/api/login";
import util from "@/util/index.js"
export default {
components: {
@@ -86,10 +85,10 @@
);
info = data[0];
}
console.log(info)
if (info) {
const s = util.aesencode(info);
uni.navigateTo({
url: `/pages/common/groupCard/index?sourceInfo=${JSON.stringify(info,)}`,
url: `/pages/common/groupCard/index?sourceInfo=${s}`,
});
} else {
this.empty = true;
@@ -117,7 +116,7 @@
console.log(info)
if (info) {
uni.navigateTo({
url: `/pages/common/userCard/index?sourceInfo=${JSON.stringify(info,)}`,
url: `/pages/common/userCard/index?sourceID=${info.userID}`,
});
} else {
this.empty = true;
@@ -2,10 +2,10 @@
<view class="set_password_container content_with_back">
<view class="title">重置密码</view>
<u-form class="loginForm commonPage-form" labelPosition="top" :model="formData" :rules="rules" :labelStyle="{
fontSize: '14px',
marginTop: '20rpx',
minWidth: '200rpx',
}" ref="loginForm">
fontSize: '14px',
marginTop: '20rpx',
minWidth: '200rpx',
}" ref="loginForm">
<u-form-item label="密码" prop="password">
<u-input v-model="formData.password" border="surround" placeholder="请输入密码" :password="!passwordEying">
<u-icon @click="updateEye('passwordEying')" slot="suffix"
@@ -30,9 +30,8 @@
</template>
<script>
import {
businessReset
} from "@/api/login";
import {businessReset} from "@/api/login";
import util from "@/util/index.js"
export default {
data() {
return {
@@ -61,7 +60,7 @@
validator: (rule, value, callback) => {
return value.length >= 6;
},
message: "密码太",
message: "密码太过于简单",
trigger: ["change", "blur"],
},
],
@@ -89,8 +88,8 @@
isRegister,
codeValue
} = options;
this.userInfo = JSON.parse(userInfo);
this.isRegister = JSON.parse(isRegister);
this.userInfo = util.aesdecode(userInfo);
this.isRegister = util.parse(isRegister);
this.codeValue = codeValue;
},
onBackPress() {
@@ -112,7 +111,7 @@
businessReset(options)
.then(() => {
uni.$u.toast("密码重置成功,请前往登录!");
setTimeout(() => uni.$u.route("/pages/login/index"), 1000);
setTimeout(() => uni.$u.route("/pages/common/login/index"), 1000);
})
.catch((err) => {
console.log('err', err)
@@ -2,10 +2,10 @@
<view class="set_info_container content_with_back">
<view class="title">设置信息</view>
<u-form class="loginForm commonPage-form" labelPosition="top" :model="userInfo" :rules="rules" :labelStyle="{
fontSize: '14px',
marginTop: '20rpx',
minWidth: '200rpx',
}" ref="loginForm">
fontSize: '14px',
marginTop: '20rpx',
minWidth: '200rpx',
}" ref="loginForm">
<u-form-item label="昵称" prop="nickname">
<u-input v-model="userInfo.nickname" border="surround" placeholder="请输入您的昵称" clearable>
</u-input>
@@ -37,8 +37,9 @@
import md5 from "md5";
import MyAvatar from "@/components/MyAvatar/index.vue";
import { mapGetters } from "vuex";
import { businessRegister } from "@/api/login";
import { checkLoginError } from "@/util/common";
import {businessRegister} from "@/api/login";
import {checkLoginError } from "@/util/common";
import util from "@/util/index.js"
export default {
components: {
MyAvatar,
@@ -61,10 +62,11 @@
nickname: [{
type: "string",
required: true,
message: "请填写真实姓名",
message: "请填写您的昵称",
trigger: ["blur", "change"],
}, ],
password: [{
password: [
{
type: "string",
required: true,
message: "请输入密码",
@@ -75,20 +77,20 @@
validator: (rule, value, callback) => {
return value.length >= 6;
},
message: "密码太",
message: "密码太过于简单",
trigger: ["change", "blur"],
},
],
confirmPassword: [{
type: "string",
required: true,
message: "请输入确认密码",
message: "请再次输入密码",
trigger: ["blur", "change"],
pattern: /^(?=.*\d)(?=.*[a-zA-Z]).{6,}$/,
},
{
validator: (rule, value, callback) => {
return value === this.formData.password;
return value === this.userInfo.password;
},
message: "两次密码不一致",
trigger: ["change", "blur"],
@@ -102,15 +104,17 @@
},
onLoad(options) {
const {userInfo,codeValue} = options;
this.userInfo = {
...this.userInfo,
...JSON.parse(userInfo),
};
if(userInfo){
this.userInfo = {
...this.userInfo,
...util.aesdecode(userInfo),
};
}
this.codeValue = codeValue;
if(process.env.NODE_ENV == 'development'){
//this.userInfo.email = "commiu@outlook.com";
this.userInfo.nickname = "";
this.userInfo.password = "qwe123";
this.userInfo.password = "qwe1231";
this.userInfo.confirmPassword = "qwe123";
}
},
@@ -128,36 +132,46 @@
}
});
},
async doRegister() {
this.loading = true;
const options = {
code: this.codeValue,
platform: uni.$u.os(),
autoLogin: true,
...this.userInfo,
region: `+${this.userInfo.region}`,
password: md5(this.userInfo.password),
mobile: this.userInfo.mobile
};
try {
await businessRegister(options);
this.saveLoginInfo();
uni.$u.toast('注册成功')
uni.$u.route("/pages/login/index")
} catch (err) {
console.log(err);
if(err.msg=="验证码过期" || err.msg=="验证码错误"){
uni.$u.route("/pages/login/verifyCode/index", {
userInfo: JSON.stringify(this.userInfo),
isRegister: true,
resend: 1,
})
return ;
doRegister() {
const _this = this;
this.$refs.loginForm.validate().then(async (res) => {
_this.loading = true;
console.log(res);
const options = {
code: _this.codeValue,
platform: uni.$u.os(),
autoLogin: true,
..._this.userInfo,
region: `+${_this.userInfo.region}`,
password: md5(_this.userInfo.password),
mobile: _this.userInfo.mobile
};
try {
await businessRegister(options);
_this.saveLoginInfo();
uni.$u.toast('注册成功')
uni.$u.route("/pages/common/login/index")
} catch (err) {
console.log(err);
if(err.msg=="验证码过期" || err.msg=="验证码错误"){
const s = util.aesencode(_this.userInfo);
uni.$u.route("/pages/common/verifyCode/index", {
userInfo: s,
isRegister: true,
resend: 1,
})
return ;
}
// uni.$u.toast('')
} finally {
_this.loading = false;
}
// uni.$u.toast('')
} finally {
this.loading = false;
}
uni.$u.toast('校验通过')
}).catch(errors => {
console.log(errors);
uni.$u.toast('校验失败')
});
return ;
},
saveLoginInfo() {
uni.setStorage({
@@ -174,7 +188,6 @@
</script>
<style lang="scss" scoped>
.set_info_container {
margin-top: var(--status-bar-height);
background: linear-gradient(180deg,
rgba(0, 137, 255, 0.1) 0%,
rgba(255, 255, 255, 0) 100%);
+100 -30
View File
@@ -1,24 +1,32 @@
<template>
<view class="m-shade n-flex-1 n-align-center n-justify-center">
<view :style="{width:'580rpx'}">
<image src="/static/image/upgrade.png" mode="widthFix"></image>
<view class="n-ps-all-ll n-ms-top-ll n-position-absolute">
<text class="n-size-mm n-weight-7 n-color-inverse">发现新版本</text>
<text class="n-size-base n-ms-top-ss n-color-inverse">V{{model.version}}</text>
</view>
<view class="n-ps-all-l n-radius-lb-base" :style="{backgroundColor:'#f3f3f3',borderRadius:'0 0 16rpx 16rpx'}">
<view :style="{height:'300rpx'}">
<scroll-view class="n-flex-1" :show-scrollbar="false" scroll-y>
<rich-text class="n-size-s" :nodes="model.content" :style="{lineHeight:'26rpx'}"></rich-text>
</scroll-view>
<view class="upgrade_page">
<uni-nav-bar left-icon="back"
@clickLeft="back"
title="系统更新"
backgroundColor="#FFF"
fixed
statusBar>
</uni-nav-bar>
<view style="flex:1;display: flex;align-items: center;justify-content: center;">
<view :style="{width:'580rpx',position:'relative'}">
<image src="/static/images/upgrade.png" :style="{width:'580rpx'}" mode="widthFix"></image>
<view class="version_info">
<text class="title">发现新版本</text>
<text class="code">V{{model.version}}</text>
</view>
<view class="n-height-base n-justify-center">
<view v-if="progress">
<uv-line-progress :percentage="value" activeColor="#fc3463"></uv-line-progress>
</view>
<view class="n-flex-row" v-else>
<uv-button class="n-flex-1 n-ms-right-ll" v-if="model.force==0" @click="cancel" :customStyle="{backgroundColor:'#f3f3f3'}" text="暂不更新" color="#fc3463" shape="circle" throttleTime="1000" plain></uv-button>
<uv-button class="n-flex-1" @click="upgrade" text="立即更新" color="#fc3463" shape="circle" throttleTime="1000"></uv-button>
<view class="detail">
<scroll-view :show-scrollbar="false" scroll-y>
<rich-text :nodes="model.content"></rich-text>
</scroll-view>
<view class="footer">
<view v-if="progress">
<u-line-progress :percentage="value" activeColor="#fc3463"></u-line-progress>
</view>
<view class="buttons" v-else>
<u-button v-if="model.force==0" @click="cancel" :customStyle="{backgroundColor:'#f3f3f3'}" text="暂不更新" color="#fc3463" shape="circle" throttleTime="1000" plain></u-button>
<u-button @click="upgrade" text="立即更新" color="#fc3463" shape="circle" throttleTime="1000"></u-button>
</view>
</view>
</view>
</view>
@@ -27,7 +35,7 @@
</template>
<script>
import util from "@/util/index.js"
export default {
data() {
return {
@@ -42,9 +50,23 @@
}
},
onLoad(evt) {
if(evt.model){
this.model = {...this.model, ...JSON.parse(evt.model)}
// if(evt.model){
// this.model = {...this.model, ...JSON.parse(evt.model)}
// }
var model = uni.getStorageSync('upgrade_model');
console.log(model);
if(model){
this.model = {...this.model, ...model};
}
// this.model = {
// "id": 1,
// "type": 1,
// "force": 1,
// "source": "https://shunliao.oss-accelerate.aliyuncs.com/files/150016c51d8672fde3d1cc6945a95089_695d97b6c0162.wgt",
// "version": "3.3.6",
// "content": "修复了一些bug",
// "source_text": null
// };
},
onBackPress() {
if(this.model.force==1){
@@ -52,6 +74,11 @@
}
},
methods: {
back(){
if(this.model.force!=1){
uni.navigateBack()
}
},
// 取消更新
cancel() {
uni.navigateBack()
@@ -80,17 +107,14 @@
// 防止强制更新无法关闭界面
this.model.force = 0
if(this.model.type==1){
uni.showToast({
title:'更新成功,软件重启'
})
util.showToast('更新成功,软件重启')
setTimeout(()=>{ plus.runtime.restart() }, 1500)
}
})
},
fail: error=>{
uni.showToast({
title:'下载失败,请检查您的网络情况'
})
util.showToast('下载失败,请检查您的网络情况')
this.model.force = 0;
}
})
// 监听下载进度
@@ -103,8 +127,54 @@
}
</script>
<style>
.m-shade{
<style lang="scss" scoped>
.upgrade_page{
display: flex;
justify-content: center;
background-color: rgba(0, 0, 0, 0.1);
height: 100vh;
width: 100vw;
.version_info{
position: absolute;
top:120rpx;
display: flex;
align-items: center;
justify-content: flex-start;
margin-left: 20rpx;
width: 100%;
.title{
color: #fff;
}
.code{
color: #fff;
}
}
.detail{
margin-top: -10rpx;
background-color:#f3f3f3;
border-radius:0 0 16rpx 16rpx;
padding: 10rpx 20rpx 30rpx;
display: flex;
flex-direction: column;
scroll-view{
flex: 1;
min-height: 300rpx;
rich-text{
font-size:32rpx;
line-height: 1.5;
}
}
.footer{
display: flex;
flex-direction: column;
justify-content: center;
gap: 20rpx;
.buttons{
width: 100%;
display: flex;
justify-content: space-around;
}
}
}
}
</style>
@@ -1,51 +1,55 @@
<template>
<view @click="click" class="row_item" :class="{ arrow_right: arrow }">
<view class="title">
<text>{{ lable }}</text>
</view>
<view class="content">
<text>{{ content }}</text>
</view>
<slot>
<u-icon v-if="arrow" name="arrow-right" color="#999" size="20"></u-icon>
</slot>
</view>
<view @click="click" class="row_item" :class="{ arrow_right: arrow }">
<view class="title">
<text>{{ lable }}</text>
</view>
<view class="content">
<text>{{ content }}</text>
</view>
<slot>
<u-icon v-if="arrow" name="arrow-right" color="#999" size="20"></u-icon>
</slot>
</view>
</template>
<script>
export default {
name: "",
components: {},
props: {
lable: String,
content: String,
arrow: {
type: Boolean,
default: false,
},
},
data() {
return {};
},
methods: {
click() {
this.$emit("click");
},
},
};
export default {
name: "",
components: {},
props: {
lable: String,
content: String,
arrow: {
type: Boolean,
default: false,
},
},
data() {
return {};
},
methods: {
click() {
this.$emit("click");
},
},
};
</script>
<style lang="scss" scoped>
.row_item {
@include vCenterBox();
padding: 24rpx 44rpx;
}
.row_item {
@include vCenterBox();
padding: 24rpx 44rpx;
.content{
display: flex;
align-items: center;
}
}
.title {
margin-right: 24rpx;
}
.title {
margin-right: 24rpx;
}
.arrow_right {
justify-content: space-between;
}
</style>
.arrow_right {
justify-content: space-between;
}
</style>
+32 -59
View File
@@ -1,7 +1,8 @@
<template>
<view class="user_card_container">
<u-loading-page :loading="isLoading" loading-text="loading..."></u-loading-page>
<custom-nav-bar title="" />
<custom-nav-bar title="" @rightClick="toMoreInfo" more v-if="isFriend" />
<custom-nav-bar title="" v-else />
<view v-if="!isLoading" style="flex: 1;display: flex;flex-direction: column;">
<view class="base_info">
@@ -9,26 +10,23 @@
size="46" />
<view class="user_name">
<text class="text">{{ getShowName }}</text>
<text class="id" @click="copy(sourceUserInfo.userID)">{{sourceUserInfo.userID}}</text>
</view>
<view class="add_btn" @click="toAddFriend" v-if="trySendRequest">
<u-button type="primary" icon="man-add" text="添加"></u-button>
<text class="id" @click="copy(sourceUserInfo.userID || sourceUserInfo.id)">{{sourceUserInfo.userID || sourceUserInfo.id}}</text>
</view>
</view>
<view class="info_row">
<user-info-row-item lable="性别" :content="getGender" />
<user-info-row-item lable="生日" :content="getBirth" />
<user-info-row-item lable="个性签名" :content="sourceUserInfo.bio" />
</view>
<view v-if="isFriend" class="info_row">
<user-info-row-item @click="toMoreInfo" lable="个人资料" arrow />
</view>
<uni-list class="info_row">
<uni-list-item title="性别" :rightText="getGender"></uni-list-item>
<uni-list-item title="生日" :rightText="getBirth"></uni-list-item>
<uni-list-item title="个性签名">
<u--text slot="footer" color="#999" :text="sourceUserInfo.bio" :lines="2" wordWrap="anywhere"></u--text>
</uni-list-item>
</uni-list>
<uni-list class="info_row" v-if="isFriend">
<uni-list-item v-if="isFriend" title="性别" @click="toMoreInfo" clickable showArrow></uni-list-item>
<uni-list-item v-if="1==2" title="朋友圈" @click="gotoCircle" clickable showArrow></uni-list-item>
</uni-list>
<view class="action_row" v-if="!isSelf">
<view @click="toDesignatedConversation" class="action_item">
<img src="static/images/user_card_message.png" alt="" />
<text>发消息</text>
</view>
<u-button type="primary" icon="chat" text="发消息" @click="toDesignatedConversation" v-if="isFriend"></u-button>
<u-button type="primary" icon="man-add" text="添加" @click="toAddFriend" v-else></u-button>
</view>
</view>
</view>
@@ -43,6 +41,7 @@
import UserInfoRowItem from "./components/UserInfoRowItem.vue";
import {businessSearchUserInfo} from "@/api/login";
import dayjs from "dayjs";
import util from "@/util/index.js"
export default {
components: {
@@ -106,10 +105,9 @@
if (sourceID) {
this.sourceID = sourceID;
} else {
const info = JSON.parse(sourceInfo);
const info = util.aesdecode(sourceInfo);
this.sourceID = info.userID;
}
//console.log(this.storeSelfInfo);
this.getSourceUserInfo();
},
methods: {
@@ -132,6 +130,7 @@
info = {
...friendInfo
};
//console.log(info);
} else {
const {
data
@@ -143,21 +142,14 @@
info = {
...(data[0] ?? {})
};
//console.log(info);
}
this.isLoading = true
try {
const res = await businessSearchUserInfo(this.sourceID);
const res = await businessSearchUserInfo(this.sourceID);console.log(res);
if (res.total > 0) {
const {
data
} = await IMSDK.asyncApi(
IMSDK.IMMethods.GetUsersInfo,
IMSDK.uuid(),
[this.sourceID+''],
);
const imData = data[0]?.friendInfo ?? data[0]?.publicInfo ?? {};
info = {
...imData,
...info,
...res.data[0],
};
}
@@ -165,6 +157,7 @@
info = {};
}
this.isLoading = false
//console.log(info);
this.sourceUserInfo = {
...info,
};
@@ -177,6 +170,13 @@
notNeedVerification: false,
});
},
gotoCircle(){
console.log('gotoCircle');
return ;
uni.navigateTo({
url:"/pages/find/friend-circle/friend-circle?userId="+this.sourceID
})
},
toDesignatedConversation() {
navigateToDesignatedConversation(
this.sourceID,
@@ -185,8 +185,9 @@
).catch(() => uni.$u.toast("获取会话信息失败"));
},
toMoreInfo() {
const s = util.aesencode(this.sourceUserInfo);
uni.navigateTo({
url: `/pages/common/userCardMore/index?sourceInfo=${JSON.stringify(this.sourceUserInfo,)}`,
url: `/pages/common/userCardMore/index?sourceInfo=${s}`,
});
}
},
@@ -207,16 +208,6 @@
padding: 44rpx;
margin-bottom: 18rpx;
.add_btn {
width: 140rpx;
height: 60rpx;
margin-left: auto;
.u-button {
width: 140rpx;
height: 60rpx;
}
}
.u-avatar {
margin-right: 24rpx;
@@ -226,7 +217,6 @@
display: flex;
flex-direction: column;
justify-content: space-between;
margin-bottom: 12rpx;
height: 46px;
.text {
@@ -271,23 +261,6 @@
margin: 44rpx;
flex: 1;
.action_item {
width: 100%;
@include colBox(true);
flex-direction: row;
align-items: center;
justify-content: center;
padding: 22rpx 0;
background: $u-primary;
color: #fff;
border-radius: 12rpx;
img {
margin-right: 16rpx;
width: 40rpx;
height: 40rpx;
}
}
}
.id {
+15 -8
View File
@@ -26,9 +26,8 @@
import IMSDK from "openim-uniapp-polyfill";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import UserInfoRowItem from "../userCard/components/UserInfoRowItem.vue";
import {
ContactChooseTypes
} from "@/constant";
import {ContactChooseTypes} from "@/constant";
import util from "@/util/index.js"
export default {
components: {
CustomNavBar,
@@ -59,7 +58,7 @@
},
onLoad(options) {
const {sourceInfo} = options;
this.sourceInfo = JSON.parse(sourceInfo);
this.sourceInfo = util.aesdecode(sourceInfo);
},
methods: {
change(isBlack) {
@@ -76,23 +75,31 @@
},
confirmRemove() {
IMSDK.asyncApi(IMSDK.IMMethods.DeleteFriend,IMSDK.uuid(),this.sourceInfo.userID,)
.then(() => this.showToast("操作成功"))
.then(() => {
this.showToast("操作成功");
uni.navigateBack({
delta: 2,
})
})
.catch(() => this.showToast("操作失败"))
.finally(() => (this.showConfirm = false));
},
toMore() {
const s = util.aesencode(this.sourceInfo);
uni.navigateTo({
url: `/pages/common/detailsFileds/index?sourceInfo=${JSON.stringify(this.sourceInfo,)}`,
url: `/pages/common/detailsFileds/index?sourceInfo=${s}`,
});
},
toMark() {
const s = util.aesencode(this.sourceInfo);
uni.navigateTo({
url: `/pages/common/markOrIDPage/index?isRemark=true&sourceInfo=${JSON.stringify(this.sourceInfo,)}`,
url: `/pages/common/markOrIDPage/index?isRemark=true&sourceInfo=${s}`,
});
},
toShare() {
const s = util.aesencode(this.sourceInfo);
uni.navigateTo({
url: `/pages/common/contactChoose/index?type=${ContactChooseTypes.ShareCard}&cardInfo=${JSON.stringify(this.sourceInfo)}`,
url: `/pages/common/contactChoose/index?type=${ContactChooseTypes.ShareCard}&cardInfo=${s}`,
});
},
showToast(message) {
+160 -67
View File
@@ -14,22 +14,25 @@
</uni-nav-bar>
<view style="flex:1;display: flex;flex-direction: column;align-items: center;justify-content: center;">
<view class="info_card">
<my-avatar :src="userInfo.faceURL" :desc="userInfo.nickname" size="64" />
<my-avatar :src="source.faceURL" :desc="source.showName" size="64" />
<view class="id_row">
<text class="nickname">{{ userInfo.nickname }}</text>
<view class="id_row_copy" @click="copy">
<text class="id">{{ userInfo.userID }}</text>
<text class="nickname">{{ source.showName }}</text>
<view class="id_row_copy" @click="copy(source.code)">
<text class="id">{{ source.code }}</text>
<image style="width: 32rpx; height: 32rpx" src="@/static/images/id_copy.png" mode="" />
</view>
</view>
</view>
<canvas canvas-id="qrcode_canvas" id="qrcode_canvas" style="width: 600rpx; height: 600rpx"></canvas>
<view id="qrcode_canvas_container" style="width: 600rpx; height: 600rpx">
<canvas canvas-id="qrcode_canvas" id="qrcode_canvas" style="width: 600rpx; height: 600rpx"></canvas>
</view>
<u-gap></u-gap>
<view style="color: #b4b4b4;">扫一扫上面的二维码图案加我为朋友</view>
</view>
<view style="width: 80%;display: flex;align-items: center;justify-content: center;height: 20%;">
<u-button type="default" plain :hairline="false" iconColor="#9aa2b2" @click="scan">扫一扫</u-button>
<u-button type="default" plain :hairline="false" iconColor="#9aa2b2">换个样式</u-button>
<u-button type="default" plain :hairline="false" iconColor="#9aa2b2">保存图片</u-button>
<u-button @click="scan" type="default" plain :hairline="false" color="#506388" iconColor="#9aa2b2">扫一扫</u-button>
<u-button @click="createQrcode" type="default" plain :hairline="false" color="#506388" iconColor="#9aa2b2">换个样式</u-button>
<u-button @click="save" type="default" plain :hairline="false" color="#506388" iconColor="#9aa2b2">保存图片</u-button>
</view>
</view>
</template>
@@ -39,6 +42,7 @@
import QRCode from "@/components/qrcode.js";
import UserBase from '@/components/User.vue';
import util from "@/util";
import { mapGetters } from "vuex";
export default {
mixins:[UserBase],
components: {
@@ -46,37 +50,128 @@
},
data() {
return {
qrcodeUrl:"",
qrcode_src:"",
source:{
type:"user",
showName:"",
faceURL:"",
code:"",
},
qrcodeSize:"360",
qrcodeStyle:[]
}
},
onLoad() {
console.log(this.userInfo)
this.createQrcode();
computed:{
...mapGetters(["storeFriendList","storeGroupList","config"]),
},
watch:{
qrcodeSize(nv,ov){
if(nv && this.qrcodeUrl){
this.createQrcode();
}
}
},
onLoad(opt) {
if(opt.sourceInfo){
this.source = util.aesdecode(opt.sourceInfo);
}else{
this.source = {
type:"user",
showName:"",
faceURL:"",
code:"",
};
}
if(this.source.type == "user"){
this.qrcodeUrl = `${this.config.website}/u/${this.source.code}`;
}else{
this.qrcodeUrl = `${this.config.website}/g/${this.source.code}`;
}
this.qrcodeStyle.push({
background: "#fff", // 背景色
foreground: '#000000', // 前景色
pdground: '#000000', // 定位角点颜色
correctLevel: 3, // 容错级别
image: this.config.app_logo, // 二维码图标
imageSize: 40, // 二维码图标大小
});
this.qrcodeStyle.push({
background: "#fff", // 背景色
foreground: '#000000', // 前景色
pdground: '#000000', // 定位角点颜色
correctLevel: 3, // 容错级别
image: this.source.faceURL, // 二维码图标
imageSize: 40, // 二维码图标大小
});
},
mounted() {
const _this = this;
uni.createSelectorQuery().in(this).select("#qrcode_canvas_container")
.boundingClientRect((data) => {
_this.qrcodeSize = data.width
})
.exec();
},
methods: {
...util,
save(){
const _this = this;
uni.saveImageToPhotosAlbum({
filePath:_this.qrcode_src,
success() {
util.success("保存成功");
},
fail(e) {
console.log(e);
util.error("保存失败");
}
})
},
createQrcode() {
const _this = this;
const style = this.qrcodeStyle[Math.floor(Math.random() * this.qrcodeStyle.length)];
style.imageSize = parseInt(this.qrcodeSize * 0.2);
return new Promise((resolve, reject) => {
new QRCode({
context: _this, // 上下文环境
canvasId: 'qrcode_canvas', // canvas-id
usingComponents: true, // 是否是自定义组件
showLoading: false, // 是否显示loading
loadingText: "", // loading文字
text: "/pages/common/invite_register?code=", // 生成内容
size: 320, // 二维码大小
background: "#fff", // 背景色
foreground: '#000000', // 前景色
pdground: '#000000', // 定位角点颜
correctLevel: 3, // 容错级别
image: "", // 二维码图标
imageSize: 40, // 二维码图标大小
cbResult: function(res) { // 生成二维码的回调
//_this.qrcode_src = (res)
//resolve(res);
},
});
var createFn = (icon)=>{
console.log(icon)
new QRCode({
context: _this, // 上下文环境
canvasId: 'qrcode_canvas', // canvas-id
usingComponents: true, // 是否是自定义组件
showLoading: false, // 是否显示loading
loadingText: "", // loading文字
text: `${_this.qrcodeUrl}`, // 生成内容
size: _this.qrcodeSize, // 二维码大小
background: style.background, // 背景
foreground: style.foreground, // 前景色
pdground: style.pdground, // 定位角点颜色
correctLevel: 3, // 容错级别
image: icon || "", // 二维码图标
imageSize: style.imageSize || 40, // 二维码图标大小
cbResult: function(res) { // 生成二维码的回调
_this.qrcode_src = (res)
//resolve(res);
},
});
}
if(!style.image){
createFn();
return ;
}
if(style.image.startsWith("/static/images")){
createFn(style.image);
return ;
}
if(!style.image.startsWith("http")){
style.image = util.cdn(style.image);
}
util.cacheFile(style.image,'avatar').then(fn=>{
createFn(fn);
});
return ;
});
},
}
@@ -84,48 +179,46 @@
</script>
<style scoped lang="scss">
.info_card {
width: 600rpx;
height: 196rpx;
border-radius: 6px;
background: #fff;
margin: 0 auto 0 auto;
padding: 0 36rpx;
color: #0c1c33;
.info_card {
width: 600rpx;
border-radius: 6px;
background: #fff;
margin: 0 auto 0 auto;
padding: 40rpx 36rpx;
color: #0c1c33;
display: flex;
align-items: center;
.id_row {
display: flex;
align-items: center;
margin-left: 16rpx;
flex-direction: column;
align-items: flex-start;
justify-content: space-around;
height: 100%;
flex: 1;
font-size: 28rpx;
.id_row {
&_copy {
@include vCenterBox();
display: flex;
//height: 46px;
margin-left: 16rpx;
flex-direction: column;
align-items: flex-start;
justify-content: space-between;
flex: 1;
font-size: 28rpx;
&_copy {
@include vCenterBox();
}
.nickname {
@include nomalEllipsis();
max-width: 400rpx;
font-weight: 500;
font-size: 34rpx;
}
.id {
color: #8e9ab0;
}
}
img {
width: 18px;
height: 18px;
.nickname {
@include nomalEllipsis();
max-width: 400rpx;
font-weight: 500;
font-size: 34rpx;
}
.id {
color: #8e9ab0;
}
}
img {
width: 18px;
height: 18px;
}
}
</style>
@@ -22,10 +22,11 @@
</view>
</template>
<script>
import user from "../../../store/modules/user";
import { businessSendSms, businessVerifyCode } from "@/api/login";
import { SmsUserFor } from "@/constant";
import { checkLoginError } from "@/util/common";
import user from "@/store/modules/user";
import {businessSendSms,businessVerifyCode} from "@/api/login";
import {SmsUserFor} from "@/constant";
import {checkLoginError } from "@/util/common";
import util from "@/util/index.js"
let timer;
export default {
data() {
@@ -49,7 +50,7 @@
resend
} = options;
console.log(userInfo,isRegister)
this.userInfo = JSON.parse(userInfo);
this.userInfo = util.aesdecode(userInfo);
this.isRegister = JSON.parse(isRegister);
if(resend == 1){
this.count = 0;
@@ -61,7 +62,7 @@
onReady() {},
methods: {
back() {
uni.$u.route("/pages/login/registerOrForget/index", {
uni.$u.route("/pages/common/registerOrForget/index", {
isRegister: this.isRegister,
});
},
@@ -76,15 +77,16 @@
};
businessVerifyCode(options)
.then(() => {
const s = util.aesencode(this.userInfo);
if (this.isRegister) {
uni.$u.route("/pages/login/setSelfInfo/index", {
userInfo: JSON.stringify(this.userInfo),
uni.$u.route("/pages/common/setSelfInfo/index", {
userInfo: s,
isRegister: this.isRegister,
codeValue: this.codeValue,
});
} else {
uni.$u.route("/pages/login/setPassword/index", {
userInfo: JSON.stringify(this.userInfo),
uni.$u.route("/pages/common/setPassword/index", {
userInfo: s,
isRegister: !this.isRegister,
codeValue: this.codeValue,
});
+218 -229
View File
@@ -1,256 +1,245 @@
<template>
<view class="page_container">
<custom-nav-bar :title="isGroupApplication ? '群通知' : '好友请求'" />
<view class="page_container">
<custom-nav-bar :title="isGroupApplication ? '群通知' : '好友请求'" />
<view class="application_item">
<view class="base_info_row">
<view class="base_info_left" @click="toSourceDetails">
<my-avatar :src="getSourceFaceURL" :desc="getSourceName" />
<view class="base_info_details">
<text class="nickname">{{ getSourceName }}</text>
</view>
</view>
<view class="application_item">
<view class="base_info_row">
<view class="base_info_left" @click="toSourceDetails">
<my-avatar :src="getSourceFaceURL" :desc="getSourceName" />
<view class="base_info_details">
<text class="nickname">{{ getSourceName }}</text>
</view>
</view>
<u-icon name="arrow-right" size="18" color="#999"></u-icon>
</view>
<u-icon name="arrow-right" size="18" color="#999"></u-icon>
</view>
<view class="request_message">
<view v-if="isGroupApplication" class="title">
<text>申请加入 </text>
<text class="group_name">{{ currentApplication.groupName }}</text>
</view>
<text v-else>{{ `${getSourceName}` }}</text>
<text>{{ currentApplication.reqMsg }}</text>
</view>
<view class="request_message">
<view v-if="isGroupApplication" class="title">
<text>申请加入 </text>
<text class="group_name">{{ currentApplication.groupName }}</text>
</view>
<text v-else>{{ `${getSourceName}` }}</text>
<text>{{ currentApplication.reqMsg }}</text>
</view>
</view>
</view>
<view class="action_row">
<u-button
:loading="loadingState.accept"
@click="acceptAplication"
type="primary"
:plain="true"
:text="`通过${isGroupApplication ? '入群' : '好友'}申请`"
></u-button>
</view>
<view class="action_row">
<u-button :loading="loadingState.accept" @click="acceptAplication" type="primary" :plain="true"
:text="`通过${isGroupApplication ? '入群' : '好友'}申请`"></u-button>
</view>
<view class="action_row">
<u-button
:loading="loadingState.refuse"
@click="refuseAplication"
type="primary"
:plain="true"
:text="`拒绝${isGroupApplication ? '入群' : '好友'}申请`"
></u-button>
</view>
</view>
<view class="action_row">
<u-button :loading="loadingState.refuse" @click="refuseAplication" type="primary" :plain="true"
:text="`拒绝${isGroupApplication ? '入群' : '好友'}申请`"></u-button>
</view>
</view>
</template>
<script>
import { mapGetters } from "vuex";
import IMSDK, { GroupJoinSource } from "openim-uniapp-polyfill";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import MyAvatar from "@/components/MyAvatar/index.vue";
export default {
components: {
CustomNavBar,
MyAvatar,
},
data() {
return {
currentApplication: {},
isOnline: false,
loadingState: {
accept: false,
refuse: false,
},
};
},
computed: {
...mapGetters(["storeSelfInfo"]),
isGroupApplication() {
return this.currentApplication.groupID !== undefined;
},
getSourceID() {
return (
this.currentApplication.fromUserID ?? this.currentApplication.userID
);
},
getSourceName() {
return (
this.currentApplication.fromNickname ?? this.currentApplication.nickname
);
},
getSourceFaceURL() {
return (
this.currentApplication.fromFaceURL ?? this.currentApplication.faceURL
);
},
},
onLoad(options) {
const { application } = options;
this.currentApplication = JSON.parse(application);
},
methods: {
toSourceDetails() {
uni.navigateTo({
url: `/pages/common/userCard/index?sourceID=${this.getSourceID}`,
});
},
acceptAplication() {
this.loadingState.accept = true;
let func;
if (this.isGroupApplication) {
func = IMSDK.asyncApi(
IMSDK.IMMethods.AcceptGroupApplication,
IMSDK.uuid(),
{
groupID: this.currentApplication.groupID,
fromUserID: this.currentApplication.userID,
handleMsg: "",
},
);
} else {
console.log(this.currentApplication);
func = IMSDK.asyncApi(
IMSDK.IMMethods.AcceptFriendApplication,
IMSDK.uuid(),
{
toUserID: this.currentApplication.fromUserID,
handleMsg: "",
},
);
}
func
.then(() => {
uni.$u.toast("操作成功");
setTimeout(() => uni.navigateBack(), 500);
})
.catch((e) => {
console.log(e);
uni.$u.toast("操作失败");
})
.finally(() => (this.loadingState.accept = false));
},
refuseAplication() {
this.loadingState.refuse = true;
let func;
if (this.isGroupApplication) {
func = IMSDK.asyncApi(
IMSDK.IMMethods.RefuseGroupApplication,
IMSDK.uuid(),
{
groupID: this.currentApplication.groupID,
fromUserID: this.currentApplication.userID,
handleMsg: "",
},
);
} else {
func = IMSDK.asyncApi(
IMSDK.IMMethods.RefuseFriendApplication,
IMSDK.uuid(),
{
toUserID: this.currentApplication.fromUserID,
handleMsg: "",
},
);
}
func
.then(() => {
uni.$u.toast("操作成功");
setTimeout(() => uni.navigateBack(), 250);
})
.catch(() => uni.$u.toast("操作失败"))
.finally(() => (this.loadingState.refuse = false));
},
},
};
import {mapGetters} from "vuex";
import IMSDK, {GroupJoinSource} from "openim-uniapp-polyfill";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import MyAvatar from "@/components/MyAvatar/index.vue";
import util from "@/util/index.js"
export default {
components: {
CustomNavBar,
MyAvatar,
},
data() {
return {
currentApplication: {},
isOnline: false,
loadingState: {
accept: false,
refuse: false,
},
};
},
computed: {
...mapGetters(["storeSelfInfo"]),
isGroupApplication() {
return this.currentApplication.groupID !== undefined;
},
getSourceID() {
return (
this.currentApplication.fromUserID ?? this.currentApplication.userID
);
},
getSourceName() {
return (
this.currentApplication.fromNickname ?? this.currentApplication.nickname
);
},
getSourceFaceURL() {
return (
this.currentApplication.fromFaceURL ?? this.currentApplication.faceURL
);
},
},
onLoad(options) {
const {
application
} = options;
this.currentApplication = util.aesdecode(application);
},
methods: {
toSourceDetails() {
uni.navigateTo({
url: `/pages/common/userCard/index?sourceID=${this.getSourceID}`,
});
},
acceptAplication() {
this.loadingState.accept = true;
let func;
if (this.isGroupApplication) {
func = IMSDK.asyncApi(
IMSDK.IMMethods.AcceptGroupApplication,
IMSDK.uuid(), {
groupID: this.currentApplication.groupID,
fromUserID: this.currentApplication.userID,
handleMsg: "",
},
);
} else {
console.log(this.currentApplication);
func = IMSDK.asyncApi(
IMSDK.IMMethods.AcceptFriendApplication,
IMSDK.uuid(), {
toUserID: this.currentApplication.fromUserID,
handleMsg: "",
},
);
}
func
.then(() => {
uni.$u.toast("操作成功");
setTimeout(() => uni.navigateBack(), 500);
})
.catch((e) => {
console.log(e);
uni.$u.toast("操作失败");
})
.finally(() => (this.loadingState.accept = false));
},
refuseAplication() {
this.loadingState.refuse = true;
let func;
if (this.isGroupApplication) {
func = IMSDK.asyncApi(
IMSDK.IMMethods.RefuseGroupApplication,
IMSDK.uuid(), {
groupID: this.currentApplication.groupID,
fromUserID: this.currentApplication.userID,
handleMsg: "",
},
);
} else {
func = IMSDK.asyncApi(
IMSDK.IMMethods.RefuseFriendApplication,
IMSDK.uuid(), {
toUserID: this.currentApplication.fromUserID,
handleMsg: "",
},
);
}
func
.then(() => {
uni.$u.toast("操作成功");
setTimeout(() => uni.navigateBack(), 250);
})
.catch(() => uni.$u.toast("操作失败"))
.finally(() => (this.loadingState.refuse = false));
},
},
};
</script>
<style lang="scss" scoped>
.page_container {
background-color: #f8f8f8;
.page_container {
background-color: #f8f8f8;
.application_item {
padding: 72rpx 44rpx 24rpx;
background-color: #fff;
.application_item {
padding: 72rpx 44rpx 24rpx;
background-color: #fff;
.base_info_row {
@include btwBox();
.base_info_row {
@include btwBox();
.base_info_left {
@include vCenterBox();
}
.base_info_left {
@include vCenterBox();
}
.base_info_details {
margin-left: 24rpx;
.base_info_details {
margin-left: 24rpx;
.nickname {
@include nomalEllipsis();
max-width: 600rpx;
}
.nickname {
@include nomalEllipsis();
max-width: 600rpx;
}
.online_state {
@include vCenterBox();
flex-direction: row;
font-size: 24rpx;
color: #999;
margin-top: 6rpx;
.online_state {
@include vCenterBox();
flex-direction: row;
font-size: 24rpx;
color: #999;
margin-top: 6rpx;
.dot {
background-color: #10cc64;
width: 12rpx;
height: 12rpx;
border-radius: 50%;
margin-right: 12rpx;
}
}
}
}
.dot {
background-color: #10cc64;
width: 12rpx;
height: 12rpx;
border-radius: 50%;
margin-right: 12rpx;
}
}
}
}
.request_message {
background-color: #eee;
margin-top: 48rpx;
padding: 24rpx 36rpx;
border-radius: 12rpx;
font-size: 28rpx;
color: #666;
min-height: 240rpx;
.request_message {
background-color: #eee;
margin-top: 48rpx;
padding: 24rpx 36rpx;
border-radius: 12rpx;
font-size: 28rpx;
color: #666;
min-height: 240rpx;
.title {
margin-bottom: 12rpx;
color: $uni-text-color;
.title {
margin-bottom: 12rpx;
color: $uni-text-color;
.group_name {
@nomalEllipsis();
max-width: 400rpx;
color: $uni-color-primary;
margin-left: 12rpx;
}
}
}
.group_name {
@nomalEllipsis();
max-width: 400rpx;
color: $uni-color-primary;
margin-left: 12rpx;
}
}
}
.join_source {
margin-top: 20rpx;
font-size: 24rpx;
color: #666;
text-align: right;
}
}
.join_source {
margin-top: 20rpx;
font-size: 24rpx;
color: #666;
text-align: right;
}
}
.action_row {
margin-top: 24rpx;
.action_row {
margin-top: 24rpx;
.u-button {
border: none;
}
.u-button {
border: none;
}
&:last-child {
.u-button {
color: #999 !important;
}
}
}
}
</style>
&:last-child {
.u-button {
color: #999 !important;
}
}
}
}
</style>
@@ -28,13 +28,10 @@
</template>
<script>
import {
navigateToDesignatedConversation
} from "@/util/imCommon";
import IMSDK, {
SessionType
} from "openim-uniapp-polyfill";
import {navigateToDesignatedConversation} from "@/util/imCommon";
import IMSDK, {SessionType} from "openim-uniapp-polyfill";
import MyAvatar from "@/components/MyAvatar/index.vue";
import util from "@/util/index.js"
export default {
name: "ApplicationItem",
components: {
@@ -97,17 +94,12 @@
methods: {
clickItem() {
if (this.showAccept) {
const s = util.aesencode(this.application);
uni.navigateTo({
url: `/pages/contact/applicationDetails/index?application=${JSON.stringify(
this.application,
)}`,
url: `/pages/contact/applicationDetails/index?application=${s}`,
});
} else {
let sourceID =
this.application.groupID ??
(this.isRecv ?
this.application.fromUserID :
this.application.toUserID);
let sourceID = this.application.groupID ?? (this.isRecv ? this.application.fromUserID : this.application.toUserID);
let cardType = this.isGroupApplication ? "groupCard" : "userCard";
const url = `/pages/common/${cardType}/index?sourceID=${sourceID}`;
uni.navigateTo({
+170 -158
View File
@@ -1,173 +1,185 @@
<template>
<view class="application_list_container">
<custom-nav-bar :title="isGroupApplication ? '新的群聊' : '新的好友'" />
<view
class="pane_row"
:style="{ transform: `translateX(${isRecv ? '0' : '-100%'})` }"
>
<view class="pane_content">
<u-list v-if="getRecvRenderData.length > 0" class="application_list">
<u-list-item
v-for="application in getRecvRenderData"
:key="
application[!isGroupApplication ? 'fromUserID' : 'userID'] +
application.groupID
"
>
<application-item :isRecv="true" :application="application" />
</u-list-item>
</u-list>
<u-list
v-else-if="getSendRenderData.length > 0"
class="application_list"
>
<u-list-item
v-for="application in getSendRenderData"
:key="application[!isGroupApplication ? 'toUserID' : 'groupID']"
>
<application-item :application="application" />
</u-list-item>
</u-list>
<view v-else class="empty">
<image src="@/static/images/block_empty.png"></image>
<text class="empty_text">暂无数据</text>
</view>
</view>
</view>
</view>
<view class="application_list_container">
<custom-nav-bar :title="isGroupApplication ? '新的群聊' : '新的好友'" />
<view class="pane_row" :style="{ transform: `translateX(${isRecv ? '0' : '-100%'})` }">
<view class="pane_content">
<u-list v-if="getRecvRenderData.length > 0" class="application_list">
<u-list-item v-for="(application,index) in getRecvRenderData" :key="index">
<application-item :isRecv="true" :application="application" />
</u-list-item>
</u-list>
<u-list v-else-if="getSendRenderData.length > 0" class="application_list">
<u-list-item v-for="(application,index1) in getSendRenderData"
:key="index1">
<application-item :application="application" />
</u-list-item>
</u-list>
<view v-else class="empty">
<image src="@/static/images/block_empty.png"></image>
<text class="empty_text">暂无数据</text>
</view>
</view>
</view>
</view>
</template>
<script>
import { mapGetters } from "vuex";
import { ContactMenuTypes } from "@/constant";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import ApplicationItem from "./ApplicationItem.vue";
export default {
components: {
CustomNavBar,
ApplicationItem,
},
data() {
return {
keyword: "",
isRecv: true,
isGroupApplication: false,
};
},
computed: {
...mapGetters([
"storeRecvFriendApplications",
"storeSentFriendApplications",
"storeRecvGroupApplications",
"storeSentGroupApplications",
]),
getRecvRenderData() {
const tmpList = this.isGroupApplication
? this.storeRecvGroupApplications
: this.storeRecvFriendApplications;
tmpList.sort((a, b) => (a.handleResult === 0 ? -1 : 1));
return tmpList.slice(0, 4);
},
getSendRenderData() {
const tmpList = this.isGroupApplication
? this.storeSentGroupApplications
: this.storeSentFriendApplications;
tmpList.sort((a, b) => (a.handleResult === 0 ? -1 : 1));
return tmpList.slice(0, 4);
},
tabList() {
return [
{
name: this.isGroupApplication ? "入群申请" : "好友请求",
},
{
name: "我的请求",
},
];
},
},
onLoad(params) {
const { applicationType } = params;
this.isGroupApplication = applicationType === ContactMenuTypes.NewGroup;
},
methods: {
clickTab({ index }) {
this.isRecv = index === 0;
},
previewAll() {
uni.navigateTo({
url: `/pages/contact/applicationListDetails/index?isGroupApplication=${this.isGroupApplication}&isRecv=${this.isRecv}`,
});
},
toSearch() {
uni.navigateTo({
url: `/pages/common/searchUserOrGroup/index?isSearchGroup=${this.isGroupApplication}`,
});
},
},
};
import {
mapGetters
} from "vuex";
import {
ContactMenuTypes
} from "@/constant";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import ApplicationItem from "./ApplicationItem.vue";
export default {
components: {
CustomNavBar,
ApplicationItem,
},
data() {
return {
keyword: "",
isRecv: true,
isGroupApplication: false,
};
},
computed: {
...mapGetters([
"storeRecvFriendApplications",
"storeSentFriendApplications",
"storeRecvGroupApplications",
"storeSentGroupApplications",
]),
getRecvRenderData() {
const tmpList = this.isGroupApplication ?
this.storeRecvGroupApplications :
this.storeRecvFriendApplications;
tmpList.sort((a, b) => (a.handleResult === 0 ? -1 : 1));
return tmpList.slice(0, 4);
},
getSendRenderData() {
const tmpList = this.isGroupApplication ?
this.storeSentGroupApplications :
this.storeSentFriendApplications;
tmpList.sort((a, b) => (a.handleResult === 0 ? -1 : 1));
return tmpList.slice(0, 4);
},
tabList() {
return [{
name: this.isGroupApplication ? "入群申请" : "好友请求",
},
{
name: "我的请求",
},
];
},
},
// watch:{
// getRecvRenderData:{
// handler(v){
// console.log(v)
// },
// immediate: true, // 立即执行一次
// deep: true // 开启深度监听
// },
// getSendRenderData:{
// handler(v){
// console.log(v)
// },
// immediate: true, // 立即执行一次
// deep: true // 开启深度监听
// }
// },
onLoad(params) {
const {
applicationType
} = params;
this.isGroupApplication = applicationType === ContactMenuTypes.NewGroup;
},
methods: {
clickTab({
index
}) {
this.isRecv = index === 0;
},
previewAll() {
uni.navigateTo({
url: `/pages/contact/applicationListDetails/index?isGroupApplication=${this.isGroupApplication}&isRecv=${this.isRecv}`,
});
},
toSearch() {
uni.navigateTo({
url: `/pages/common/searchUserOrGroup/index?isSearchGroup=${this.isGroupApplication}`,
});
},
},
};
</script>
<style lang="scss" scoped>
.empty {
@include centerBox();
flex-direction: column;
margin-top: 25vh !important;
.empty {
@include centerBox();
flex-direction: column;
margin-top: 25vh !important;
&_text {
margin-top: 26rpx;
color: #8e9ab0;
}
image {
width: 237rpx;
height: 244rpx;
}
}
.application_list_container {
@include colBox(false);
height: 100vh;
background-color: #f8f9fa;
overflow-x: hidden;
&_text {
margin-top: 26rpx;
color: #8e9ab0;
}
.search_bar_wrap {
height: 34px;
padding: 12px 22px;
background-color: #fff;
}
image {
width: 237rpx;
height: 244rpx;
}
}
.u-tabs {
background-color: #fff;
}
.application_list_container {
@include colBox(false);
height: 100vh;
background-color: #f8f9fa;
overflow-x: hidden;
.pane_row {
display: flex;
flex: 1;
transition: all 0.3s ease 0s !important;
background-color: #fff;
margin-top: 20rpx;
.search_bar_wrap {
height: 34px;
padding: 12px 22px;
background-color: #fff;
}
.pane_content {
@include colBox(false);
height: 100%;
flex: 0 0 100%;
.u-tabs {
background-color: #fff;
}
.pane_title {
font-size: 28rpx;
color: #999;
padding: 12rpx 44rpx;
background-color: #f8f8f8;
}
.pane_row {
display: flex;
//flex: 1;
transition: all 0.3s ease 0s !important;
background-color: #fff;
margin-top: 20rpx;
.application_list {
flex: 1;
height: 100% !important;
}
}
}
.pane_content {
@include colBox(false);
width: 100%;
//height: 100%;
//flex: 0 0 100%;
.view_all {
background-color: #fff;
padding: 44rpx 44rpx;
}
}
</style>
.pane_title {
font-size: 28rpx;
color: #999;
padding: 12rpx 44rpx;
background-color: #f8f8f8;
}
.application_list {
flex: 1;
height: 100% !important;
}
}
}
.view_all {
background-color: #fff;
padding: 44rpx 44rpx;
}
}
</style>
+72 -72
View File
@@ -1,81 +1,81 @@
<template>
<view class="application_list_container">
<custom-nav-bar :title="getTitle" />
<view class="application_list_container">
<custom-nav-bar :title="getTitle" />
<u-list class="application_list">
<u-list-item
v-for="application in getRenderData"
:key="getKey(application)"
>
<application-item :isRecv="isRecv" :application="application" />
</u-list-item>
</u-list>
</view>
<u-list class="application_list">
<u-list-item v-for="application in getRenderData" :key="getKey(application)">
<application-item :isRecv="isRecv" :application="application" />
</u-list-item>
</u-list>
</view>
</template>
<script>
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import ApplicationItem from "../applicationList/ApplicationItem.vue";
export default {
components: {
CustomNavBar,
ApplicationItem,
},
data() {
return {
isGroupApplication: false,
isRecv: false,
};
},
computed: {
getRenderData() {
let getterKey = this.isRecv
? "storeRecvFriendApplications"
: "storeSentFriendApplications";
if (this.isGroupApplication) {
getterKey = this.isRecv
? "storeRecvGroupApplications"
: "storeSentGroupApplications";
}
return [...this.$store.getters[getterKey]].sort((a, b) =>
a.handleResult === 0 ? -1 : 1,
);
},
getKey() {
return (application) => {
if (this.isGroupApplication) {
return this.isRecv
? application.userID + application.groupID
: application.groupID;
}
return application[this.isRecv ? "fromUserID" : "toUserID"];
};
},
getTitle() {
if (!this.isRecv) {
return "我的申请";
}
return this.isGroupApplication ? "群通知" : "好友请求";
},
},
onLoad(options) {
const { isGroupApplication, isRecv } = options;
this.isGroupApplication = JSON.parse(isGroupApplication);
this.isRecv = JSON.parse(isRecv);
},
methods: {},
};
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import ApplicationItem from "../applicationList/ApplicationItem.vue";
export default {
components: {
CustomNavBar,
ApplicationItem,
},
data() {
return {
isGroupApplication: false,
isRecv: false,
};
},
computed: {
getRenderData() {
let getterKey = this.isRecv ?
"storeRecvFriendApplications" :
"storeSentFriendApplications";
if (this.isGroupApplication) {
getterKey = this.isRecv ?
"storeRecvGroupApplications" :
"storeSentGroupApplications";
}
return [...this.$store.getters[getterKey]].sort((a, b) =>
a.handleResult === 0 ? -1 : 1,
);
},
getKey() {
return (application) => {
if (this.isGroupApplication) {
return this.isRecv ?
application.userID + application.groupID :
application.groupID;
}
return application[this.isRecv ? "fromUserID" : "toUserID"];
};
},
getTitle() {
if (!this.isRecv) {
return "我的申请";
}
return this.isGroupApplication ? "群通知" : "好友请求";
},
},
onLoad(options) {
const {
isGroupApplication,
isRecv
} = options;
this.isGroupApplication = JSON.parse(isGroupApplication);
this.isRecv = JSON.parse(isRecv);
},
methods: {},
};
</script>
<style lang="scss" scoped>
.application_list_container {
@include colBox(false);
height: 100vh;
background-color: #f8f8f8;
.application_list_container {
@include colBox(false);
height: 100vh;
background-color: #f8f8f8;
.application_list {
margin-top: 24rpx;
flex: 1;
}
}
</style>
.application_list {
margin-top: 24rpx;
flex: 1;
}
}
</style>
+7 -7
View File
@@ -26,15 +26,15 @@
{
idx: 0,
title: "创建群聊",
desc: "创建群聊,全面使用OpenIM",
desc: "创建群聊",
icon: contact_add_create_group_img,
},
{
idx: 1,
title: "添加群聊",
desc: "向管理员或团队成员询问ID",
icon: rcontact_add_join_group_img,
},
// {
// idx: 1,
// title: "添加群聊",
// desc: "向管理员或团队成员询问ID",
// icon: contact_add_join_group_img,
// },
],
friendActionMenus: [
{
+26 -7
View File
@@ -14,9 +14,27 @@
<SearchbarPlace @click="toSearch">搜索</SearchbarPlace>
</view>
<uni-list class="contact_menus">
<uni-list-item title="新的好友" :showBadge="storeUnHandleFriendApplicationNum>0" :badgeText="storeUnHandleFriendApplicationNum+''" badgeType="error" thumbSize="lg" to="/pages/contact/applicationList/index?applicationType=NewFriend" thumb="/static/images/contact_new_friend.png"></uni-list-item>
<uni-list-item title="新的群组" thumbSize="lg" to="/pages/contact/applicationList/index?applicationType=NewGroup" thumb="/static/images/contact_new_group.png"></uni-list-item>
<uni-list-item title="群聊" thumbSize="lg" to="/pages/contact/groupList/index" thumb="/static/images/contact_my_group.png"></uni-list-item>
<uni-list-item title="新的好友"
:showBadge="storeUnHandleFriendApplicationNum>0"
:badgeText="storeUnHandleFriendApplicationNum+''"
badgeType="error"
thumbSize="lg"
to="/pages/contact/applicationList/index?applicationType=NewFriend"
thumb="/static/images/contact_new_friend.png"></uni-list-item>
<uni-list-item
title="新的群组"
:showBadge="storeUnHandleGroupApplicationNum>0"
:badgeText="storeUnHandleGroupApplicationNum+''"
badgeType="error"
thumbSize="lg"
to="/pages/contact/applicationList/index?applicationType=NewGroup"
thumb="/static/images/contact_new_group.png"></uni-list-item>
<uni-list-item
v-if="1==2"
title="群聊"
thumbSize="lg"
to="/pages/contact/groupList/index"
thumb="/static/images/contact_my_group.png"></uni-list-item>
</uni-list>
<choose-index-list v-if="getIndexData.dataList.length > 0" @itemClick="userClick" :height="`${listHeight}px`"
@@ -46,7 +64,11 @@
};
},
computed: {
...mapGetters(["storeFriendList",'storeUnHandleFriendApplicationNum']),
...mapGetters([
"storeFriendList",
'storeUnHandleFriendApplicationNum',
'storeUnHandleGroupApplicationNum'
]),
getIndexData() {
return formatChooseData(this.storeFriendList);
},
@@ -54,9 +76,6 @@
mounted() {
this.getListHeight();
},
async onShow() {
//await this.getFriendList();
},
methods: {
...mapActions('contact',['getFriendList']),
toSearch(){
+120 -142
View File
@@ -1,157 +1,135 @@
<template>
<view class="group_list_container">
<custom-nav-bar title="我的群组">
<view class="group_list_container">
<custom-nav-bar title="我的群组"></custom-nav-bar>
<view class="search_bar_wrap">
<u-search class="search_bar" shape="square" placeholder="搜索" disabled :showAction="false" />
</view>
</custom-nav-bar>
<view class="search_bar_wrap">
<u-search
class="search_bar"
shape="square"
placeholder="搜索"
disabled
:showAction="false"
/>
</view>
<u-tabs :scrollable="false" :list="tabList" @click="clickTab"></u-tabs>
<u-tabs :scrollable="false" :list="tabList" @click="clickTab"></u-tabs>
<view class="pane_row" :style="{ transform: `translateX(${isMyCreate ? '0' : '-100%'})` }">
<view class="pane_content">
<u-list v-if="getMyCreateGroupList.length > 0" class="group_list" :height="`${getListHeight}px`">
<u-list-item v-for="group in getMyCreateGroupList" :key="group.groupID">
<group-item :groupInfo="group" />
</u-list-item>
</u-list>
<u-empty v-else mode="list" />
</view>
<view
class="pane_row"
:style="{ transform: `translateX(${isMyCreate ? '0' : '-100%'})` }"
>
<view class="pane_content">
<u-list
v-if="getMyCreateGroupList.length > 0"
class="group_list"
:height="`${getListHeight}px`"
>
<u-list-item
v-for="group in getMyCreateGroupList"
:key="group.groupID"
>
<group-item :groupInfo="group" />
</u-list-item>
</u-list>
<u-empty v-else mode="list" />
</view>
<view class="pane_content">
<u-list
v-if="getMyJoinedGroupList.length > 0"
class="group_list"
:height="`${getListHeight}px`"
>
<u-list-item
v-for="group in getMyJoinedGroupList"
:key="group.groupID"
>
<group-item :groupInfo="group" />
</u-list-item>
</u-list>
<u-empty v-else mode="list" />
</view>
</view>
</view>
<view class="pane_content">
<u-list v-if="getMyJoinedGroupList.length > 0" class="group_list" :height="`${getListHeight}px`">
<u-list-item v-for="group in getMyJoinedGroupList" :key="group.groupID">
<group-item :groupInfo="group" />
</u-list-item>
</u-list>
<u-empty v-else mode="list" />
</view>
</view>
</view>
</template>
<script>
import { mapGetters } from "vuex";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import GroupItem from "./GroupItem.vue";
export default {
components: {
CustomNavBar,
GroupItem,
},
data() {
return {
keyword: "",
tabList: [
{
name: "我创建的",
},
{
name: "我加入的",
},
],
isMyCreate: true,
};
},
computed: {
...mapGetters(["storeGroupList", "storeCurrentUserID"]),
getMyCreateGroupList() {
return this.storeGroupList.filter(
(group) => group.ownerUserID === this.storeCurrentUserID,
);
},
getListHeight() {
const statusBar = uni.getWindowInfo().statusBarHeight;
const searchBar = 58;
const tabAndNavBar = 44 * 2;
const titleBar = 32;
return (
uni.getWindowInfo().safeArea.height -
statusBar -
searchBar -
tabAndNavBar -
titleBar
);
},
getMyJoinedGroupList() {
// console.log(this.storeGroupList.filter(group => group.ownerUserID !== this.storeCurrentUserID));
return this.storeGroupList.filter(
(group) => group.ownerUserID !== this.storeCurrentUserID,
);
},
},
mounted() {},
methods: {
clickTab({ index }) {
this.isMyCreate = index === 0;
},
toCreateGroup() {
uni.navigateTo({
url: `/pages/common/createGroup/index`,
});
}
},
};
import {
mapGetters
} from "vuex";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import GroupItem from "./GroupItem.vue";
export default {
components: {
CustomNavBar,
GroupItem,
},
data() {
return {
keyword: "",
tabList: [{
name: "我创建的",
},
{
name: "我加入的",
},
],
isMyCreate: true,
};
},
computed: {
...mapGetters(["storeGroupList", "storeCurrentUserID"]),
getMyCreateGroupList() {
return this.storeGroupList.filter(
(group) => group.ownerUserID === this.storeCurrentUserID,
);
},
getListHeight() {
const statusBar = uni.getWindowInfo().statusBarHeight;
const searchBar = 58;
const tabAndNavBar = 44 * 2;
const titleBar = 32;
return (
uni.getWindowInfo().safeArea.height -
statusBar -
searchBar -
tabAndNavBar -
titleBar
);
},
getMyJoinedGroupList() {
//console.log(this.storeGroupList);
return this.storeGroupList.filter(
(group) => group.ownerUserID !== this.storeCurrentUserID,
);
},
},
mounted() {},
methods: {
clickTab({
index
}) {
this.isMyCreate = index === 0;
},
toCreateGroup() {
uni.navigateTo({
url: `/pages/common/createGroup/index`,
});
}
},
};
</script>
<style lang="scss" scoped>
.group_list_container {
@include colBox(false);
height: 100vh;
overflow: hidden;
.group_list_container {
@include colBox(false);
height: 100vh;
overflow: hidden;
.nav_right_action {
padding-right: 44rpx;
}
.nav_right_action {
padding-right: 44rpx;
}
.search_bar_wrap {
height: 34px;
padding: 12px 22px;
}
.search_bar_wrap {
height: 34px;
padding: 12px 22px;
}
.pane_row {
display: flex;
flex: 1;
transition: all 0.3s ease 0s !important;
border-top: 2rpx solid #e8eaef;
// overflow-x: hidden;
.pane_row {
display: flex;
flex: 1;
transition: all 0.3s ease 0s !important;
border-top: 2rpx solid #e8eaef;
// overflow-x: hidden;
.pane_content {
@include colBox(false);
height: 100%;
flex: 0 0 100%;
.pane_content {
@include colBox(false);
height: 100%;
flex: 0 0 100%;
.pane_title {
font-size: 14px;
color: #999;
padding: 6px 22px;
background-color: #f8f8f8;
}
}
}
}
</style>
.pane_title {
font-size: 14px;
color: #999;
padding: 6px 22px;
background-color: #f8f8f8;
}
}
}
}
</style>
@@ -1,193 +1,284 @@
<template>
<view class="editor_wrap">
<editor
:placeholder="placeholder"
id="editor2"
@ready="editorReady"
@focus="editorFocus"
@blur="editorBlur"
@input="editorInput" />
</view>
<div id="editor-container" :call="option" :change:call="editorModule.call"><!-- 编辑器 --></div>
</template>
<script>
import { forIn } from "lodash";
import {html2Text} from "@/util/common";
export default {
props: {
placeholder: {
type: String,
default: "",
},
},
data() {
return {
editorCtx: null,
lastStr: "",
isInsertingEmoji: false, // 标记是否正在插入表情
hasFocus: false, // 记录编辑器是否有焦点
};
timer:null,
option:null,
events:[],
}
},
methods: {
editorReady() {
uni
.createSelectorQuery()
.select("#editor2")
.context((res) => {
//this.$emit("ready", res);
this.editorCtx = res.context;
})
.exec();
insertText(text,successFn,errorFn){
this.addEvent('insertText',text);
},
editorFocus() {
this.hasFocus = true;
// 如果正在插入表情,不触发 focus 事件,并立即隐藏键盘
if (this.isInsertingEmoji) {
// #ifdef APP-PLUS || H5
uni.hideKeyboard();
// #endif
return;
}
this.$emit("focus");
insertImgEmoji(src,successFn,errorFn){
this.addEvent('insertImgEmoji',src);
},
editorBlur() {
this.hasFocus = false;
this.$emit("blur");
insertMention(username,userid){
this.addEvent('insertMention',{
username,
userid
});
},
clear(){
this.editorCtx.clear()
this.addEvent('clear');
},
insertText(text,successFn,errFn){
// 标记正在插入表情,阻止 focus 事件触发
this.isInsertingEmoji = true;
// 先隐藏键盘,避免插入时键盘弹出
// #ifdef APP-PLUS || H5
uni.hideKeyboard();
// #endif
// 如果编辑器当前有焦点,先让它失焦(通过点击外部区域)
// 但这种方式可能不太可靠,所以我们主要依赖 isInsertingEmoji 标志
// 使用 insertText 插入文本(这是最可靠的方法)
// 虽然会触发焦点,但我们已经通过 isInsertingEmoji 标志阻止了 focus 事件
this.editorCtx.insertText({
text: text,
success: (res) => {
successFn && successFn.call(this, [res]);
console.log("插入文字成功");
// 插入后立即隐藏键盘,防止键盘弹出
// #ifdef APP-PLUS || H5
// 使用多个延迟确保键盘被隐藏
setTimeout(() => {
uni.hideKeyboard();
}, 10);
setTimeout(() => {
uni.hideKeyboard();
}, 50);
setTimeout(() => {
uni.hideKeyboard();
}, 100);
// #endif
// 延迟重置标志,确保 focus 事件被完全忽略
setTimeout(() => {
this.isInsertingEmoji = false;
}, 300);
},
fail: (err) => {
errFn && errFn.call(this, [err]);
console.log("插入文字失败", err);
this.isInsertingEmoji = false;
}
});
blur(){
this.addEvent('blur');
},
delete(){
this.editorCtx.getContents({
success({html,text,delta}){
console.log(html,text,delta);
}
})
return ;
//setContents(OBJECT)
let emojiStr = this.editorCtx.getContents();
let emojiArr = [];
emojiStr = emojiStr.replace(/\[([^(\]|\[)]*)\]/g, function(item, index) {
emojiArr.unshift(item);
});
let sendStr ="";
if (emojiArr.length > 0) {
if (this.sendStr.endsWith(emojiArr[0])) {
this.sendStr = this.sendStr.replace(emojiArr[0], "");
focus(){
this.addEvent('focus');
},
setHtml(html){
this.addEvent('setHtml',html);
},
getText(){
this.addEvent('getText');
},
getHtml(){
console.log(this);
return 1;
this.addEvent('getHtml');
},
getSelectionPosition(){
this.addEvent('getSelectionPosition');
},
getParentNode(){
this.addEvent('getParentNode');
},
// 调用
call() {
if (this.timer) return;
// 消费事件队列(生产者/消费者机制)
this.timer = setInterval(() => {
if (this.events.length) {
this.option = this.events.shift();
console.log(this.option);
} else {
this.sendStr = this.sendStr.slice(0, this.sendStr.length - 1);
clearInterval(this.timer);
this.timer = null;
}
} else {
this.sendStr = this.sendStr.slice(0, this.sendStr.length - 1);
}, 10);
},
// 添加事件队列
addEvent(name, data) {
// #ifdef APP-PLUS
// tips:由于采用监听option改变来调用方法,
// 如果连续变化两次option,渲染层只会则监听到最后一次
// 导致调用丢失,所以采用事件队列形式解决,稍微延时10ms
// 等待渲染进程监听到option变化,在进行更改option
// 从性能上,几乎无感可以放心使用
const option = {
id: this.genId(),
name: `_${name}`,
data
};
this.events.push(option);
this.call();
// #endif
// #ifndef APP-PLUS
this[`_${name}`] && this[`_${name}`](data);
// #endif
},
genId() {
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (let i = 0; i < 30; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
this.editorCtx.setContents({
html:sendStr
})
console.log('delete')
},
editorInput(e) {
let str = e.detail.html;
const oldArr = (this.lastStr ?? '').split("");
let contentStr = str;
oldArr.forEach((str) => {
contentStr = contentStr.replace(str, "");
});
contentStr = html2Text(contentStr);
this.$emit("input", e);
this.lastStr = e.detail.html;
return Date.now() + result;
},
// 开始拖拽地图
UserEvent(data) {
//console.log(data);
this.$emit('onUserEvent',data);
}
}
}
</script>
<script module="editorModule" lang="renderjs">
export default {
data() {
return {
_editorIns:null
}
},
};
mounted() {
this._initEditor();
},
methods: {
_initEditor() {
if (typeof window.wangEditor === 'function') {
this._initialize();
} else {
const script = document.createElement('script');
script.onload = this._initialize;
script.src = "static/wangeditor/index.js";
document.head.appendChild(script);
const link = document.createElement('link');
link.href = "static/wangeditor/style.css";
link.rel = "stylesheet";
document.head.appendChild(link);
}
},
// 创建地图
_initialize() {
const _this = this;
// 创建地图实例
const {createEditor} = window.wangEditor
const editor = createEditor({
selector: '#editor-container',
html: '',
config: {
placeholder: '',
maxLength:100,
hoverbarKeys:{
divider: {menuKeys: [],},
link: {menuKeys: [],},
image: {menuKeys: [],},
pre: {menuKeys: [],},
table: {menuKeys: [],},
text: {menuKeys: [],},
video: {menuKeys: [],},
},
onCreated(){
_this.$ownerInstance.callMethod('UserEvent',{
type:'ready'
});
},
//onDestroyed(){},
onFocus(){
_this.$ownerInstance.callMethod('UserEvent',{
type:'focus'
});
},
onBlur(){
_this.$ownerInstance.callMethod('UserEvent',{
type:'blur'
});
},
//onDestroyed(){},
onChange(editor) {
const html = editor.getHtml()
const text = editor.getText()
//console.log('editor content', html)
// 也可以同步到 <textarea>
_this.$ownerInstance.callMethod('UserEvent',{
type:'onChange',
html,
text,
});
},
}
});
editor.on('atevent',()=>{
_this.$ownerInstance.callMethod('UserEvent',{
type:'atevent'
});
})
this._editorIns = editor;
console.log(editor.insertText);
this._editorIns.insertText("text");
},
_insertText(text){
console.log('_insertText',text);
this._editorIns.insertText(text);
},
_insertNode(node){
this._editorIns.insertNode(node);
},
_insertImgEmoji(src){
this._insertNode({
type: 'image',
style:{width:'30px',height:'30px'},
src: src,
children: [{text: ''}],
});
},
_insertMention(data){
this._insertNode({
type: 'mention',
username: data.username,
userid: data.userid,
children: [{
text: ''
}],
});
},
_clear(){
this._editorIns.clear();
},
_delete(){
this._editorIns.deleteBackward();
},
_blur(){
this._editorIns.blur();
},
_focus(){
this._editorIns.focus();
},
_setHtml(html){
this._editorIns.setHtml(html);
},
_getText(){
return this._editorIns.getText();
},
_getHtml(){
return this._editorIns.getHtml();
},
_getSelectionPosition(){
return this._editorIns.getSelectionPosition();
},
_getParentNode(){
return this._editorIns.getParentNode();
},
// 通过监听call来调用渲染层方法
call(newValue, oldValue, ownerInstance, instance) {
if(!newValue){
return false;
}
if (this[newValue.name] && typeof this[newValue.name] === "function") {
this[newValue.name](newValue.data);
}
},
}
}
</script>
<style lang="scss" scoped>
.editor_wrap {
position: relative;
}
#editor2 {
background-color: #fff;
min-height: 30px;
max-height: 120px;
height: auto;
word-break: break-all;
}
::v-deep.ql-editor {
img {
vertical-align: sub !important;
}
p {
padding: 4px;
}
}
.canvas_container {
position: fixed;
bottom: -99px;
z-index: -100;
&_name {
max-width: 480rpx;
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#atCanvas {
height: 20px;
}
.convas_container_name {
font-size: 16px !important;
.custom_editor {
::v-deep.w-e-text-container{
background: transparent;
[data-slate-editor]{
padding: 0;
}
p,
span{
height: 60rpx;
line-height: 60rpx;
}
p{
white-space: pre-wrap; /* 保留空格 */
margin: 0;
}
img{
}
span{
}
span[data-w-e-type="mention"]{
background-color: #ccc;
margin-right: 10px;
}
}
}
</style>
@@ -150,7 +150,7 @@
};
if (this.voiceCanSend) {
//console.log("=====上传语音文件,并发送语音信息====");
this.$emit('RecodeEvent',{type:"sendVoiceMessage",audio:voiceFile})
this.$emit('RecodEvent',{type:"sendVoiceMessage",audio:voiceFile})
return;
} else {
console.log("=====已经取消发送语音信息====")
@@ -59,21 +59,22 @@
this.textValue = e.detail.value;
// 更新光标位置
this.cursorPos = e.detail.cursor || this.textValue.length;
this.$emit("input", {
detail: {
value: this.textValue,
text: this.textValue,
html: this.textValue // 简单编辑器,HTML 就是文本
}
this.$emit("onUserEvent", {
"type":"input",
text: this.textValue,
html: this.textValue // 简单编辑器,HTML 就是文本
});
},
onFocus(e) {
this.$emit("focus", e);
this.$emit("onUserEvent",{
type:"focus",
e
});
},
onBlur(e) {
// 保存光标位置
this.cursorPos = e.detail.cursor || this.textValue.length;
this.$emit("blur", e);
this.$emit("onUserEvent",{type:"blur", e});
},
// 插入文本(表情或普通文本)
insertText(text, successFn, errFn) {
@@ -93,12 +94,10 @@
this.cursorPos = newCursorPos;
// 触发 input 事件
this.$emit("input", {
detail: {
value: newText,
text: newText,
html: newText
}
this.$emit("onUserEvent", {
type:"input",
html: newText,
text: newText,
});
// 使用 $nextTick 确保 DOM 更新后再设置光标
@@ -139,12 +138,10 @@
clear() {
this.textValue = "";
this.cursorPos = 0;
this.$emit("input", {
detail: {
value: "",
text: "",
html: ""
}
this.$emit("onUserEvent", {
type:"input",
text: "",
html: ""
});
},
// 删除(退格)
@@ -180,12 +177,10 @@
this.cursorPos = currentPos - deletedLength;
// 触发 input 事件
this.$emit("input", {
detail: {
value: this.textValue,
text: this.textValue,
html: this.textValue
}
this.$emit("onUserEvent", {
type:"input",
text: this.textValue,
html: this.textValue
});
// 使用 $nextTick 确保 DOM 更新后再设置光标
@@ -220,12 +215,12 @@
.simple_editor_textarea {
width: 100%;
min-height: 30px;
min-height: 60rpx;
max-height: 120px;
line-height: 60rpx;
background-color: #fff;
font-size: 14px;
line-height: 1.5;
padding: 4px;
font-size: 30rpx;
padding: 0 6rpx;
word-break: break-all;
box-sizing: border-box;
}
@@ -1,19 +1,28 @@
<template>
<view>
<view v-if="isMute">
<view class="forbidden_footer">
<view class="mute_tip" v-if="storeCurrentGroup.status === 3">全群禁言中</view>
<view class="mute_tip" v-else>您被禁言至{{date(storeCurrentMemberInGroup.muteEndTime)}}</view>
</view>
</view>
<view v-else-if="!isSingle && !storeCurrentMemberInGroup.userID">
<view class="forbidden_footer">
<view class="mute_tip">您不是群成员</view>
</view>
</view>
<view v-else>
<view class="chat_footer">
<!-- 语音信息 -->
<image class="action_btn" v-show="inputType == 'keyboard'" @click.prevent="swtichInputType('record')" mode="heightFix" src="@/static/images/chating_footer_audio.png" alt="" srcset="" />
<image class="action_btn" v-show="inputType == 'record'" @click.prevent="swtichInputType('keyboard')" mode="heightFix" src="@/static/images/chating_footer_audio_recording.png" alt="" srcset="" />
<view class="input_content">
<Recoder v-if="inputType == 'record'" @RecodeEvent="onRecodeEvent"></Recoder>
<Recoder v-if="inputType == 'record'" @RecodEvent="onRecodEvent"></Recoder>
<SimpleEditor
v-if="inputType == 'keyboard'"
class="custom_editor"
ref="customEditor"
:value="inputHtml"
@focus="editorFocus"
@blur="editorBlur"
@input="editorInput" />
@onUserEvent="onEditorEvent"
:value="inputHtml"/>
</view>
<view class="footer_action_area" v-show="inputType == 'keyboard'">
@@ -45,18 +54,22 @@
</view>
<!-- #endif -->
</view>
</template>
<script>
import {mapGetters,mapActions} from "vuex";
import {getPurePath,html2Text,getVideoCover,getVideoInfo} from "@/util/common";
import {offlinePushInfo} from "@/util/imCommon";
import {offlinePushInfo,date} from "@/util/imCommon";
import {ChatingFooterActionTypes,UpdateMessageTypes,} from "@/constant";
import IMSDK, {IMMethods,MessageStatus,MessageType,} from "openim-uniapp-polyfill";
import IMSDK, {IMMethods,MessageStatus,MessageType,SessionType} from "openim-uniapp-polyfill";
import CustomEditor from "./CustomEditor";
import SimpleEditor from "./SimpleEditor";
import ChatingActionBar from "./ChatingActionBar";
import Recoder from "./Recoder";
import {upload} from "@/api/login.js"
import IM from "@/util/im.js";
import permision from "@/js_sdk/wa-permission/permission.js"
const needClearTypes = [MessageType.TextMessage];
const rtcChoose = [
@@ -74,7 +87,7 @@
export default {
components: {
SimpleEditor,
CustomEditor,SimpleEditor,
ChatingActionBar,
Recoder,
},
@@ -87,7 +100,7 @@
voiceIconText : "正在录音...",
inputType:"keyboard",
isEmoji:false,
inputHtml: '<span>@cansnow</span><span>@baidu</span><span>@jingds</span>',
inputHtml: '',
actionBarVisible: false,
isInputFocus: false,
actionSheetMenu: [],
@@ -102,10 +115,32 @@
"storeCurrentConversation",
"storeCurrentGroup",
"storeBlackList",
"storeCurrentUserID",
"storeCurrentMemberInGroup"
]),
isSingle() {
return (
this.storeCurrentConversation.conversationType === SessionType.Single
);
},
hasContent() {
return html2Text(this.inputHtml) !== "";
},
isAdminOrOwner(){
return this.storeCurrentMemberInGroup && this.storeCurrentMemberInGroup?.roleLevel>20;
},
isMute(){
if(this.isSingle){
return false;
}
if(this.storeCurrentGroup && this.storeCurrentGroup.status === 3 && !this.isAdminOrOwner){
return true;
}
if (this.storeCurrentMemberInGroup && this.storeCurrentMemberInGroup?.muteEndTime>0){
return true;
}
return false;
}
},
watch: {
footerOutsideFlag(newVal) {
@@ -113,6 +148,8 @@
},
},
mounted() {
//console.log(this.storeCurrentGroup)
//console.log(this.storeCurrentMemberInGroup)
this.setKeyboardListener();
},
beforeDestroy() {
@@ -120,6 +157,7 @@
},
methods: {
...mapActions("message", ["pushNewMessage", "updateOneMessage"]),
date,
async createTextMessage() {
let message = "";
const text = html2Text(this.inputHtml);
@@ -128,7 +166,7 @@
IMSDK.uuid(),
text
);
console.log(message);
//console.log(message);
return message;
},
async sendTextMessage() {
@@ -141,22 +179,26 @@
if (needClearTypes.includes(message.contentType)) {
this.$refs.customEditor.clear();
}
let method = IMMethods.SendMessage;
// if([MessageType.PictureMessage,MessageType.VoiceMessage,MessageType.VideoMessage,MessageType.FileMessage].includes(message.contentType)){
// method = IMMethods.SendMessageNotOss;
// }
this.$emit("scrollToBottom");
IMSDK.asyncApi(IMMethods.SendMessage, IMSDK.uuid(), {
IMSDK.asyncApi(method, IMSDK.uuid(), {
recvID: user_id,
groupID: group_id,
message,
offlinePushInfo,
})
.then(({data}) => {
console.log(data);
//console.log(data);
this.updateOneMessage({
message: data,
isSuccess: true,
});
})
.catch(({data,errCode,errMsg}) => {
console.log(errCode,errMsg);
console.log(errCode,errMsg,data);
uni.$u.toast(errMsg);
this.updateOneMessage({
message: data,
@@ -175,7 +217,6 @@
});
},
recordAudioMsg(){
if (uni.getSystemInfoSync().platform == "android") {
permission.requestAndroid("android.permission.RECORD_AUDIO"); //Android请求录音权限
} else {
@@ -212,17 +253,7 @@
this.isEmoji = false;
}
},
editorFocus() {
this.isInputFocus = true;
this.$emit("scrollToBottom");
},
editorBlur() {
this.isInputFocus = false;
},
editorInput(e) {
// SimpleEditor 返回的是纯文本,直接使用
this.inputHtml = e.detail.value || e.detail.text || e.detail.html || "";
},
async sendLocationMessage(res){
console.log(res);
const _this = this;
@@ -240,46 +271,22 @@
},
async sendVoiceMessage(audio){
const _this = this;
const message = await IMSDK.asyncApi(
IMMethods.CreateSoundMessageFromFullPath,
IMSDK.uuid(),
{
soundPath:getPurePath(audio.tempFilePath),
duration:audio.contentDuration
}
);
const message = await IM.createVoiceMessage(audio.tempFilePath,audio.contentDuration);
_this.sendMessage(message,_this.storeCurrentConversation.userID,_this.storeCurrentConversation.groupID);
},
// from comp
sendMediaMesage(paths) {
const _this = this;
paths.forEach(async (item) => {
console.log(item);
try {
let message = null;
if(item.search('.mp4')>0){
const realVideoPath = await getPurePath(item);
console.log('处理后的可用路径', realVideoPath);
const info = await getVideoInfo(realVideoPath);
const cover = await getVideoCover(item);
const videoParams = {
videoPath: realVideoPath,
videoType: "mp4",
duration: info.duration,
snapshotPath: getPurePath(cover),
};
console.log('videoParams', videoParams);
message = await IMSDK.asyncApi(
IMMethods.CreateVideoMessageFromFullPath,
IMSDK.uuid(),
videoParams
);
console.log('1');
message = await IM.createVideoMessage(item);
}else{
message = await IMSDK.asyncApi(
IMMethods.CreateImageMessageFromFullPath,
IMSDK.uuid(),
getPurePath(item)
);
console.log('2');
message = await IM.createImageMessage(item);
}
console.log(message);
if(message){
@@ -318,8 +325,45 @@
disposeKeyboardListener() {
uni.offKeyboardHeightChange(this.keyboardChangeHander);
},
onRecodeEvent(e){
onEditorEvent(e){
const _this = this;
if(e.type=="atevent" && this.storeCurrentConversation.groupID){
uni.navigateTo({
url: `/pages/common/contactChoose/chooseGroupMember?groupID=${this.storeCurrentConversation.groupID}&checkedUserIDList=[]&hideUserIDList=[${this.storeCurrentUserID}]&allowType=User`,
events: {
onSelectedConfirm(userList) {
userList.forEach((user)=>{
_this.$refs.customEditor.insertMention(user.remark || user.nickname || user.showName,user.userID);
_this.$refs.customEditor.focus();
})
}
}
});
return ;
}
if(e.type=="ready"){
return ;
}
if(e.type=="focus"){
this.isInputFocus = true;
this.$emit("scrollToBottom");
return ;
}
if(e.type=="blur"){
this.isInputFocus = false;
return ;
}
if(e.type=="onChange"){
return ;
}
if(e.type=="input"){
// SimpleEditor 返回的是纯文本,直接使用
this.inputHtml = e.html || e.text || "";
return ;
}
console.log(e);
},
onRecodEvent(e){
const _this = this;
switch(e.type){
case "voiceIconTextChange":
@@ -335,6 +379,30 @@
break;
}
},
pickMedia(){
const _this = this;
plus.gallery.pick(({files})=>{
console.log(files);
_this.sendMediaMesage(files);
}, (error )=>{
console.log(error);
}, {
animation:true,
confirmText:"确定",
//crop:null,
editable:true,
filename:"_doc/",
filter:"none",//image,none,video
maximum:9,
multiple:true,
permissionAlert:true,
//popover:{},
//selected:[""],
onmaxed(){
console.log("超出最大选择数");
},
});
},
onUserEvent(e){
const _this = this;
switch(e.type){
@@ -366,13 +434,13 @@
// 直接插入文本,不等待 nextTick,减少延迟
this.$refs.customEditor.insertText(e.emoji,() =>{
console.log("插入文字成功");
//console.log("插入文字成功");
// 延迟重置标志,确保其他事件不会隐藏表情栏
setTimeout(() => {
this.isInsertingEmoji = false;
}, 300);
},(err) => {
console.log("插入文字失败", err);
//console.log("插入文字失败", err);
this.isInsertingEmoji = false;
});
break;
@@ -389,27 +457,8 @@
return ;
}
if(e.source == "album"){
plus.gallery.pick(({files})=>{
_this.sendMediaMesage(files);
}, (error )=>{
reject(error);
}, {
animation:true,
confirmText:"确定",
//crop:null,
editable:true,
filename:"_doc/",
filter:"none",
maximum:9,
multiple:true,
permissionAlert:true,
//popover:{},
//selected:[""],
onmaxed(){
console.log("超出最大选择数");
},
});
}
_this.pickMedia();
}
break;
case "prepend_call_message":
this.actionSheetMenu = [...rtcChoose];
@@ -450,11 +499,10 @@
<style lang="scss" scoped>
.custom_editor {
img {
vertical-align: sub;
}
min-height: 60rpx;
max-height: 240rpx;
}
.forbidden_footer {
width: 100%;
height: 112rpx;
@@ -465,7 +513,7 @@
align-items: center;
background: #f0f2f6;
}
.chat_footer {
display: flex;
align-items: flex-end;
@@ -473,16 +521,17 @@
background: #f6f6f6;
// height: 50px;
max-height: 120px;
padding: 0 20rpx;
padding: 10rpx 20rpx;
gap: 20rpx;
.mute_tip{
}
.input_content {
flex: 1;
min-height: 80rpx;
max-height: 240rpx;
border-radius: 8rpx;
position: relative;
.record_btn {
// background-color: #3c9cff;
background: #fff;
@@ -491,37 +540,21 @@
font-size: 24rpx;
}
}
.quote_message {
@include vCenterBox();
justify-content: space-between;
margin-top: 12rpx;
padding: 8rpx;
// padding-top: 20rpx;
border-radius: 6rpx;
background-color: #fff;
color: #666;
.content {
::v-deep uni-view {
@include ellipsisWithLine(2);
}
}
}
.action_btn{
width: 26px;
height: 26px;
margin: 24rpx auto;
width: 50rpx;
height: 50rpx;
margin-bottom: 6rpx;
}
.footer_action_area {
display: flex;
align-items: center;
gap: 20rpx;
}
.send_btn {
height: 30px;
line-height: 30px;
height: 50rpx;
line-height: 50rpx;
background-color: $uni-color-success;
padding: 0 8px;
border-radius: 4px;
@@ -23,12 +23,8 @@
</template>
<script>
import {
mapGetters
} from "vuex";
import {
SessionType
} from "openim-uniapp-polyfill";
import {mapGetters} from "vuex";
import {SessionType} from "openim-uniapp-polyfill";
import MyAvatar from "@/components/MyAvatar/index.vue";
export default {
@@ -6,13 +6,11 @@
<view id="scroll_wrap">
<u-loadmore nomoreText="" :status="loadMoreStatus" />
<view class="msg_item" v-for="item in storeHistoryMessageList" :key="item.clientMsgID" @click="onUserMessageEvent({type:'selected'},item)">
<template v-if="selectFlag">
<uni-icons class="selectedIcon" size="30" color="#07c160" type="checkbox-filled" v-if="selectClientMsgIDItems.indexOf(item.clientMsgID)>-1"></uni-icons>
<uni-icons class="selectedIcon" size="30" color="#ccc" type="circle" v-else></uni-icons>
</template>
<MessageItemRender
@userEvent="onUserMessageEvent"
:source="item"
:selectItems="selectItems"
:selectFlag="selectFlag"
:conversationID="storeCurrentConversation.conversationID"
:isSender="item.sendID === storeCurrentUserID"
/>
@@ -74,13 +72,6 @@
return "nomore";
}
return this.messageLoadState.loading ? "loading" : "loadmore";
},
selectClientMsgIDItems(){
let arr = [];
this.selectItems.forEach((v,k)=>{
arr.push(v.clientMsgID);
});
return arr;
}
},
mounted() {
@@ -8,9 +8,16 @@
export default {
name: "ErrorMessagegRender",
components: {},
props: {
message: Object,
conversationID:String,
},
data() {
return {};
},
created(){
console.log(this.message);
}
};
</script>
@@ -31,11 +31,7 @@
return {
loadingWidth: "120px",
maxHeight:'120px',
src:"",
coverCachePath:"",
coverDownloading:false,
coverExists:false,
coverDownloadProgress:"",
src:""
};
},
computed: {
@@ -52,40 +48,11 @@
methods: {
async init(){
const self = this;
let url = "";
// 如果有远程 snapshotUrl,则下载到 coverCachePath
const snapshotUrl = this.message.FaceElem.data;
const key = md5(snapshotUrl || '');
this.coverCachePath = `_doc/${this.conversationID}/face_${key}.jpg`;
if (typeof plus === 'undefined' || !this.coverCachePath) return;
try {
// 检查封面是否存在
const coverExists = await util.fileExsit(self.coverCachePath);
this.coverExists = !!coverExists;
if (this.coverExists) {
this.src = this.coverCachePath;
return;
}
if (!snapshotUrl) {
this.src="/static/images/sync_error.png";
return;
}
this.coverDownloading = true;
await new Promise((resolve, reject) => {
util.downloadFile(snapshotUrl, self.coverCachePath, function(localPath) {
self.coverDownloading = false;
self.coverExists = true;
resolve(localPath);
}, function(err) {
self.coverDownloading = false;
reject(err);
}, function(progress) {
self.coverDownloadProgress = progress;
});
});
} catch (e) {
this.coverDownloading = false;
}
// 检查封面是否存在
util.cacheFile(snapshotUrl,'face').then((fn)=>{
self.src = fn;
});
},
onLoaded() {
this.loadingWidth = "auto";
@@ -21,14 +21,14 @@
src="/static/images/sync_error.png">
</u--image>
<u--text class="address" :style="{
width:selfWidth+'px'
}" :lines="1" :size="20" :text="desc"></u--text>
width:selfWidth+'px',
paddingTop:'5px'
}" :lines="1" :size="16" :text="desc"></u--text>
</view>
</template>
<script>
import util from "@/util"
import md5 from "md5";
export default {
name: "LocationMessageRender",
components: {},
@@ -39,7 +39,6 @@
data() {
return {
selfWidth:200,
loadingWidth: "200px",
src:"",
coverDownloading:false,
coverDownloadProgress:"",
@@ -49,12 +48,13 @@
};
},
created() {
this.selfWidth = uni.$u.getPx('400rpx');
let loc = this.message.locationElem;
this.desc = loc.description;
this.apisrc = "http://api.tianditu.gov.cn/staticimage?"
+"center="+loc.longitude+","+loc.latitude
+"&width=400"
+"&height=300"
+"&height=250"
+"&zoom=10"
+"&markers="+loc.longitude+","+loc.latitude
+"&tk=5255a4be64441ba9fa2ebe605ca472bf";
@@ -72,30 +72,20 @@
const imageWidth = res.width;
const aspectRatio = imageHeight / imageWidth;
_this.maxHeight = (_this.selfWidth * aspectRatio);
console.log(res)
_this.src = src;
}
})
},
async init(){
const self = this;
let url = "";
// 如果有远程 snapshotUrl,则下载到 coverCachePath
const snapshotUrl = this.apisrc ;
const key = md5(snapshotUrl || '');
const dir_name = `${this.conversationID}`;
const save_file_name = `loc_${key}.png`;
const coverCachePath = `${dir_name}/${save_file_name}`;
if (typeof plus === 'undefined' || !coverCachePath) return;
self.coverDownloading = true;
util.cacheFile(snapshotUrl,coverCachePath,(fn)=>{
util.cacheFile(snapshotUrl,dir_name).then((fn)=>{
self.coverDownloading = false;
self.getImageInfo(fn);
console.log(fn);
},(e)=>{
console.log(e);
},(e)=>{
console.log(e);
//console.log(fn);
});
},
clickMediaItem() {
@@ -105,7 +95,6 @@
})
},
onLoaded() {
this.loadingWidth = "auto";
},
},
@@ -115,7 +104,6 @@
<style lang="scss" scoped>
.location_message_container{
.address{
margin-top: 20rpx;
}
}
</style>
@@ -0,0 +1,44 @@
<template>
<view class="text_message_container bg_container" v-if="notification.contentType == 101">
<u-parse :content="getContent" :previewImg="false" :showImgMenu="false" selectable></u-parse>
</view>
</template>
<script>
import {parseBr} from "@/util/common";
export default {
name: "NotificationRender",
components: {
},
props: {
message: Object,
conversationID:String,
isSender:Boolean
},
computed:{
notification(){
const body = JSON.parse(this.message.notificationElem.detail);
body.data = JSON.parse(body.data);
return body.data;
},
getContent() {
return parseBr(this.notification.textElem?.content);
}
},
data() {
return {
content:"",
};
},
mounted() {
},
methods: {
onMessageEvent(e){
this.$emit('onMessageEvent',e);
}
},
};
</script>
<style lang="scss" scoped></style>
@@ -49,7 +49,7 @@
const imageWidth = this.message.pictureElem.sourcePicture.width;
const aspectRatio = imageHeight / imageWidth;
return 120 * aspectRatio;
},
}
},
created() {
this.init();
@@ -58,25 +58,45 @@
async init(){
const self = this;
let url = "";
const pictureElem = this.message.pictureElem;
// 如果有远程 snapshotUrl,则下载到 coverCachePath
const snapshotUrl = (this.message.pictureElem.snapshotPicture?.url ?? this.message.pictureElem.sourcePath );
const key = md5(snapshotUrl || '');
this.coverCachePath = `${this.conversationID}/img_${key}.jpg`;
util.cacheFile(snapshotUrl,this.coverCachePath,(e)=>{
let snapshotUrl = pictureElem?.sourcePath;
if(snapshotUrl && await util.fileExsit(snapshotUrl)){
self.src = snapshotUrl;
return ;
}
snapshotUrl = (pictureElem?.sourcePicture.url ?? pictureElem.bigPicture?.url);
if(!snapshotUrl){
console.log(this.message);
return;
}
console.log(snapshotUrl);
util.cacheFile(snapshotUrl,`${this.conversationID}`).then((fn)=>{
self.coverDownloading = false;
self.src = coverCachePath;
console.log(e);
},(e)=>{
console.log(e);
},(e)=>{
console.log(e);
self.src = fn;
console.log(fn);
});
},
getShowPath(url){
return new Promise((resolve,reject)=>{
if(url.startsWith('file')||url.startsWith('_')||url.startsWith('http')||url.startsWith('blob')){
return resolve(url);
}
plus.io.resolveLocalFileSystemURL(url, function(entry) {
return resolve(entry.toLocalURL());
}, function(e) {
console.log(e);
resolve(url);
});
});
},
clickMediaItem() {
uni.previewImage({
current: 0,
//urls: [this.message.pictureElem.sourcePicture.url],
urls: ["_doc/"+this.coverCachePath],
urls: [this.src],
indicator: "none",
});
},
@@ -1,6 +1,6 @@
<template>
<view class="text_message_container bg_container">
<u-parse :content="getContent" :previewImg="false" :showImgMenu="false" selectable></u-parse>
<u-parse :content="getContent" :previewImg="false" :showImgMenu="false"></u-parse>
</view>
</template>
@@ -1,14 +1,13 @@
<template>
<view>
<view class="video_message_container" @click="clickMediaItem">
<view class="video_message_container">
<u--image
:showLoading="true"
:src="src"
:width="imgWidth"
:height="maxHeight"
mode="widthFix"
@load="onLoaded"
@click="clickMediaItem">
@load="onLoaded" >
<template v-slot:loading>
<u-loading-icon color="red"></u-loading-icon>
</template>
@@ -21,8 +20,8 @@
</view>
<text class="progress-text">{{ videoDownloadProgress }}%</text>
</view>
<u-icon v-else-if="videoExists" class="play_icon" name="play-circle" size="48" color="#fff"></u-icon>
<uni-icons v-else class="play_icon" type="cloud-download" size="48" color="#fff"></uni-icons>
<u-icon v-else-if="videoExists" class="play_icon" name="play-circle" size="48" color="#ccc"></u-icon>
<uni-icons v-else class="play_icon" type="cloud-download" size="48" color="#ccc"></uni-icons>
</view>
</view>
<!-- 视频播放器组件 -->
@@ -54,7 +53,7 @@
"videoType": "mp4",
"videoSize": 3751005,
"duration": 44,
"snapshotPath": "/storage/emulated/0/Android/data/hk.huisheng.im/apps/__UNI__CA458BA/doc/video_cover_2ea1c8ec.jpg",
"snapshotPath": "/storage/emulated/0/Android/data/hk.huisheng.im/apps/__UNI__E41111F/doc/video_cover_2ea1c8ec.jpg",
"snapshotSize": 36974,
"snapshotUrl": "http://www.axzc.xyz/object/100003/msg_videoSnapshot_d9a596f3e665e84559821a8aa5fb9f16.jpg",
"snapshotWidth": 1087,
@@ -116,55 +115,38 @@
},
methods: {
async init(){
// 如果有远程 snapshotUrl,则下载到 coverCachePath
const _this = this;
console.log(this.message?.videoElem,this.conversationID);
const snapshotUrl = this.message?.videoElem?.snapshotUrl;
const key = md5(this.message?.videoElem?.videoUrl || '');
const coverCachePath = `${this.conversationID}/cover_${key}.jpg`;
this.videoCachePath = `${this.conversationID}/${key}.mp4`;
this.videoExists = await util.fileExists(this.videoCachePath);
self.coverDownloading = true;
util.cacheFile(snapshotUrl,coverCachePath,(e)=>{
self.coverDownloading = false;
self.src = coverCachePath;
console.log(e);
},(e)=>{
console.log(e);
},(e)=>{
console.log(e);
});
},
clickMediaItem() {
uni.previewImage({
current: 0,
urls: [this.message.videoElem?.snapshotUrl],
indicator: "none",
_this.coverDownloading = true;
//console.log(snapshotUrl);
util.cacheFile(snapshotUrl,`${this.conversationID}`).then((fn)=>{
_this.coverDownloading = false;
_this.src = fn;
//console.log(fn);
});
_this.videoExists = util.fileExsit(util.getCachePath(_this.message?.videoElem?.videoUrl,`${_this.conversationID}`));
},
async onLoaded() {
this.loadingWidth = "auto";
},
onOverlayClick() {
const _this = this;
// 点击覆盖层:如果视频已缓存则直接播放,否则开始下载
if (this.videoExists) {
this.playVideo("_doc/"+this.videoCachePath);
return;
}
const url = this.message?.videoElem?.videoUrl || this.message?.videoElem?.videoPath;
const url = _this.message?.videoElem?.videoUrl;
if (!url) {
uni.showToast({ title: '无可下载的视频' });
return;
}
this.videoDownloading = true;
this.videoDownloadProgress = 0;
util.downloadFile(url, this.videoCachePath, (localPath) => {
this.videoDownloading = false;
this.videoExists = true;
this.playVideo("_doc/"+localPath);
}, (err) => {
this.videoDownloading = false;
uni.showToast({ title: '下载失败' });
}, (prog) => {
_this.videoDownloading = true;
_this.videoDownloadProgress = 0;
util.cacheFile(url, `${_this.conversationID}`, (prog) => {
//console.log(prog);
this.videoDownloadProgress = prog;
}).then((fn) => {
_this.videoDownloading = false;
_this.videoExists = true;
_this.playVideo(fn);
});
},
@@ -32,7 +32,6 @@
conversationID:String,
},
data() {
console.log(this.message);
return {
src:"",
};
@@ -45,23 +44,13 @@
},
methods: {
async init(){
console.log(this.message);
//console.log(this.message);
const self = this;
let audio = this.message.soundElem;
//soundPath
// 如果有远程 snapshotUrl,则下载到 cachePath
const snapshotUrl = audio.sourceUrl;
const key = md5(snapshotUrl || '');
const save_file_name = `audio_${key}.${audio.soundType}`;
const cachePath = `${this.conversationID}/${save_file_name}`;
if (typeof plus === 'undefined' || !cachePath) return;
util.cacheFile(snapshotUrl,cachePath,(fn)=>{
util.cacheFile(snapshotUrl,`${this.conversationID}`).then((fn)=>{
self.src = fn;
console.log(fn);
},(e)=>{
console.log(e);
},(e)=>{
console.log(e);
//console.log(fn);
});
},
handleAudio(){
@@ -1,35 +1,43 @@
<template>
<view v-if="!getNoticeContent" :id="`auchor${source.clientMsgID}`" class="message_item"
:class="{ message_item_self: isSender, message_item_active: isActive }">
<my-avatar size="42" :desc="source.senderNickname" :src="source.senderFaceUrl" @click="viewDetail" />
<view class="message_container">
<view class="message_sender" :style="{ 'flex-direction': !isSender ? 'row-reverse' : 'row' }">
<text>{{ formattedMessageTime }}</text>
<text style="margin-left: 2rpx; margin-right: 2rpx">{{ "" }}</text>
<text v-if="!isSingle">{{ source.senderNickname }}</text>
</view>
<view class="message_send_state_box">
<view style="height: 100%;display: flex;justify-items: center;align-items: center;">
<view class="message_send_state">
<u-loading-icon v-if="showSending && !isPreview" />
<image v-if="isFailedMessage && !isPreview" class="state_img" src="@/static/images/chating_message_failed.png" />
</view>
<view v-if="isNoticeMessage" class="notice_message_container" :id="`auchor${source.clientMsgID}`">
<text>{{ getNoticeContent }}</text>
</view>
<view v-else-if="source.contentType == 1519" class="notice_message_container" :id="`auchor${source.clientMsgID}`">
<text @click="toAnnouncement">{{ announcementElem.opUser.nickname }}更新了群公告</text>
</view>
<view v-else class="message_wrapper">
<template v-if="selectFlag">
<uni-icons class="selectedIcon" size="30" color="#07c160" type="checkbox-filled" v-if="selectClientMsgIDItems.indexOf(source.clientMsgID)>-1"></uni-icons>
<uni-icons class="selectedIcon" size="30" color="#ccc" type="circle" v-else></uni-icons>
</template>
<view :id="`auchor${source.clientMsgID}`" class="message_item"
:class="{ message_item_self: isSender, message_item_active: isActive }">
<my-avatar size="42" :desc="source.senderNickname" :src="source.senderFaceUrl" @click="viewDetail" />
<view class="message_container">
<view class="message_sender" :style="{ 'flex-direction': !isSender ? 'row-reverse' : 'row' }">
<text>{{ formattedMessageTime }}</text>
<text style="margin-left: 2rpx; margin-right: 2rpx">{{ "" }}</text>
<text v-if="!isSingle">{{ source.senderNickname }}</text>
</view>
<view class="message_content_wrap message_content_wrap_shadow" :id="`message_content_wrap_${source.clientMsgID}`" @longtap.stop.prevent="longtapEvent($event)">
<component :is="component"
@messageEvent="onMessageEvent"
:isSender="isSender"
:message="source"
:conversationID="conversationID"
></component>
<view class="message_send_state_box">
<view style="height: 100%;display: flex;justify-items: center;align-items: center;">
<view class="message_send_state">
<u-loading-icon v-if="showSending && !isPreview" />
<image v-if="isFailedMessage && !isPreview" class="state_img" src="@/static/images/chating_message_failed.png" />
</view>
</view>
<view class="message_content_wrap message_content_wrap_shadow" :id="`message_content_wrap_${source.clientMsgID}`" @longtap.stop.prevent="longtapEvent($event)">
<component v-if="component" :is="component"
@messageEvent="onMessageEvent"
:isSender="isSender"
:message="source"
:conversationID="conversationID"
></component>
</view>
</view>
</view>
</view>
</view>
<view v-else class="notice_message_container" style="margin: 0 auto;" :id="`auchor${source.clientMsgID}`">
<text>{{ getNoticeContent }}</text>
<view class="selected_overlay" v-if="selectFlag"></view>
</view>
</template>
@@ -53,6 +61,7 @@
import MarkdownMessageRender from "./MarkdownMessageRender";
import StreamMessageRender from "./StreamMessageRender";
import OANotificationRender from "./OANotificationRender";
import NotificationRender from "./NotificationRender";
import ErrorMessageRender from "./ErrorMessageRender";
import {noticeMessageTypes} from "@/constant";
@@ -78,6 +87,7 @@
MarkdownMessageRender,
StreamMessageRender,
OANotificationRender,
NotificationRender,
ErrorMessageRender
},
props: {
@@ -89,6 +99,16 @@
conversationID:String,
isPreview: Boolean,
isActive: Boolean,
selectFlag:{
type:Boolean,
default:false
},
selectItems:{
type:Array,
default(){
return [];
}
}
},
data() {
return {
@@ -97,7 +117,8 @@
toolTipFlag: false,
popPostion:"default",
toolTipData: [],
component:"ErrorMessageRender",
component:"",
announcementElem:{opUser:{nickname:""}}
};
},
computed: {
@@ -113,16 +134,17 @@
formattedMessageTime() {
return formatMessageTime(this.source.sendTime);
},
isNoticeMessage(){
return noticeMessageTypes.includes(this.source.contentType);
},
getNoticeContent() {
const isNoticeMessage = noticeMessageTypes.includes(
this.source.contentType
);
return !isNoticeMessage ?
"" :
tipMessaggeFormat(
if(this.isNoticeMessage){
return tipMessaggeFormat(
this.source,
this.$store.getters.storeCurrentUserID
);
}
return "";
},
isSuccessMessage() {
return this.source.status === MessageStatus.Succeed;
@@ -133,38 +155,50 @@
showSending() {
return this.source.status === MessageStatus.Sending && !this.sendingDelay;
},
selectClientMsgIDItems(){
let arr = [];
this.selectItems.forEach((v,k)=>{
arr.push(v.clientMsgID);
});
return arr;
}
},
mounted() {
const MsgType2Components = {
['type_'+MessageType.TextMessage] : "TextMessageRender",
['type_'+MessageType.PictureMessage] : "PictureMessageRender",
['type_'+MessageType.VoiceMessage] : "VoiceMessageRender",
['type_'+MessageType.VideoMessage] : "VideoMessageRender",
['type_'+MessageType.FileMessage] : "FileMessageRender",
['type_'+MessageType.AtTextMessage] : "AtTextMessageRender",
['type_'+MessageType.MergeMessage] : "MergeMessageRender",
['type_'+MessageType.CardMessage] : "CardMessageRender",
['type_'+MessageType.LocationMessage] : "LocationMessageRender",
['type_'+MessageType.CustomMessage] : "CustomMessageRender",
['type_'+MessageType.TypingMessage] : "TypingMessageRender",
['type_'+MessageType.QuoteMessage] : "QuoteMessageRender",
['type_'+MessageType.FaceMessage] : "FaceMessageRender",
['type_'+MessageType.MarkdownMessage] : "MarkdownMessageRender",
['type_'+MessageType.StreamMessage] : "StreamMessageRender",
['type_'+MessageType.OANotification] : "OANotificationRender"
};
this.component = MsgType2Components['type_'+this.source.contentType] || "ErrorMessageRender";
if(this.source.contentType == MessageType.GroupAnnouncementUpdated){
this.announcementElem = JSON.parse(this.source.notificationElem.detail)
}else{
const MsgType2Components = {
['type_'+MessageType.TextMessage] : "TextMessageRender",
['type_'+MessageType.PictureMessage] : "PictureMessageRender",
['type_'+MessageType.VoiceMessage] : "VoiceMessageRender",
['type_'+MessageType.VideoMessage] : "VideoMessageRender",
['type_'+MessageType.FileMessage] : "FileMessageRender",
['type_'+MessageType.AtTextMessage] : "AtTextMessageRender",
['type_'+MessageType.MergeMessage] : "MergeMessageRender",
['type_'+MessageType.CardMessage] : "CardMessageRender",
['type_'+MessageType.LocationMessage] : "LocationMessageRender",
['type_'+MessageType.CustomMessage] : "CustomMessageRender",
['type_'+MessageType.TypingMessage] : "TypingMessageRender",
['type_'+MessageType.QuoteMessage] : "QuoteMessageRender",
['type_'+MessageType.FaceMessage] : "FaceMessageRender",
['type_'+MessageType.MarkdownMessage] : "MarkdownMessageRender",
['type_'+MessageType.StreamMessage] : "StreamMessageRender",
['type_'+MessageType.OANotification] : "OANotificationRender",
'type_2001' : "NotificationRender"
};
this.component = MsgType2Components['type_'+this.source.contentType] || "ErrorMessageRender";
}
this.$emit('userEvent',{type:"messageItemRender"},this.source.clientMsgID);
this.setSendingDelay();
},
methods: {
viewDetail(){
if(this.isSender){
}else{
uni.navigateTo({
url:"/pages/common/userCard/index?sourceID="+this.source.sendID
})
}
this.$emit('userEvent',{type:"avatarClick"},this.source);
},
toAnnouncement(){
uni.navigateTo({
url:"/pages/conversation/groupSettings/announcement"
})
},
setSendingDelay() {
if (this.source.status === MessageStatus.Sending) {
@@ -174,20 +208,35 @@
}
},
longtapEvent(e){
console.log('longtapEvent');
this.$emit('userEvent',{type:"longtapMsgContent"},this.source);
},
onMessageEvent(e){
this.$emit('userEvent',e);
onMessageEvent(e,data){
this.$emit('userEvent',e,data);
},
},
};
</script>
<style scoped lang="scss">
.message_wrapper{
display: flex;
flex-direction: row;
align-items: flex-start;
width: 100%;
position: relative;
.selected_overlay{
position: absolute;
left:0;
top:0;
bottom: 0;
right: 0;
z-index: 10;
}
}
.message_item {
display: flex;
flex-direction: row;
flex:1;
padding: 16rpx 44rpx;
// padding-top: 48rpx;
position: relative;
@@ -233,7 +282,7 @@
margin-bottom: 6rpx;
}
.message_content_wrap_shadow {
box-shadow: 0px 0px 2px rgba(0,0,0,0.1);
box-shadow: 0px 0px 0px rgba(0,0,0,0.1);
}
.message_content_wrap {
@include vCenterBox();
@@ -296,7 +345,7 @@
// text-align: end;
align-items: flex-end;
.message_content_wrap_shadow {
box-shadow: 0px 0px 2px #95e261;
box-shadow: 0px 0px 0px #95e261;
}
.message_content_wrap {
flex-direction: row-reverse;
@@ -326,10 +375,14 @@
.notice_message_container {
@include ellipsisWithLine(2);
text-align: center;
margin: 24rpx 48rpx;
padding: 24rpx 48rpx;
word-break: break-word;
// font-size: 24rpx;
font-size: 0.85rem;
text-align: center;
color: #999;
width: 100%;
box-sizing: border-box;
position: relative;
}
+94 -34
View File
@@ -19,14 +19,15 @@
<script>
import {mapActions,mapGetters} from "vuex";
import {offlinePushInfo} from "@/util/imCommon";
import {PageEvents,ContactChooseTypes } from "@/constant";
import {PageEvents,ContactChooseTypes,noticeMessageTypes} from "@/constant";
import ChatingHeader from "./components/ChatingHeader";
import ChatingFooter from "./components/ChatingFooter/index";
import ChatingList from "./components/ChatingList";
import SelectHeader from "./components/SelectHeader";
import SelectFooter from "./components/SelectFooter";
import {markConversationAsRead} from "@/util/imCommon";
import IMSDK, {MessageType} from "openim-uniapp-polyfill";
import IMSDK, {MessageType,GroupMemberRole,SessionType} from "openim-uniapp-polyfill";
import util from "@/util/index.js"
export default {
components: {
ChatingHeader,
@@ -50,8 +51,25 @@
},
computed: {
...mapGetters([
"storeCurrentConversation","storeCurrentMsg",'storeCurrentMsgID'
"storeHistoryMessageList",
"storeCurrentUserID",
"storeCurrentConversation",
"storeCurrentMsg",
'storeCurrentMsgID',
"storeCurrentGroup",
"storeCurrentMemberInGroup"
]),
isSingle() {
return (
this.storeCurrentConversation.conversationType === SessionType.Single
);
},
isOwner() {
return this.storeCurrentMemberInGroup.roleLevel === GroupMemberRole.Owner;
},
isAdmin() {
return this.storeCurrentMemberInGroup.roleLevel === GroupMemberRole.Admin;
},
},
created() {
this.Audio = uni.createInnerAudioContext(); //音频
@@ -71,6 +89,16 @@
console.log(r);
this.updateCurrentMsg({});
})
//console.log(this.storeCurrentConversation)
if(process.env.NODE_ENV == 'development'){
// if(this.storeCurrentConversation.groupID){
// setTimeout(()=>{
// uni.navigateTo({
// url:"/pages/conversation/groupAlbum/groupAlbum?groupID="+this.storeCurrentConversation.groupID
// });
// },1000)
// }
}
},
watch:{
storeCurrentMsgID(nv,ov){
@@ -86,13 +114,22 @@
}
},
onLoad(options) {
//console.log("onload");
//console.log("onload",this.storeCurrentConversation);
this.setPageListener();
if (options?.back2Tab) {
this.back2Tab = JSON.parse(options.back2Tab);
}
// IMSDK.subscribe(IMSDK.IMEvents.OnMsgDeleted,({data})=>{
// let list = this.storeHistoryMessageList;
// //console.log(data);
// list = list.filter((item)=>{
// return item.serverMsgID != data.serverMsgID;
// })
// this.$store.commit('message/SET_HISTORY_MESSAGE_LIST',list);
// });
},
onUnload() {
//IMSDK.unsubscribe(IMSDK.IMEvents.OnMsgDeleted);
//console.log("unload");
this.disposePageListener();
markConversationAsRead({...this.$store.getters.storeCurrentConversation,},true);
@@ -102,6 +139,7 @@
if (this.Audio) {
this.Audio.stop();
this.Audio.destroy();
this.Audio = null;
}
// #endif
},
@@ -173,9 +211,6 @@
}
},
sendMessage(message, user_id, group_id) {
//this.scrollToBottom();
//console.log(message);
//console.log(user_id,group_id);
IMSDK.asyncApi(IMSDK.IMMethods.SendMessage, IMSDK.uuid(), {
recvID: user_id,
groupID: group_id,
@@ -183,15 +218,23 @@
offlinePushInfo,
})
.then(({data}) => {
console.log(data);
this.pushNewMessage(message);
if(user_id && this.storeCurrentConversation.userID == user_id){
this.pushNewMessage(message);
this.scrollToBottom();
return ;
}
if(group_id && this.storeCurrentConversation.groupID == group_id){
this.pushNewMessage(message);
this.scrollToBottom();
return ;
}
})
.catch((res = {data,errCode,errMsg}) => {
console.log(res);
uni.$u.toast(errMsg);
});
},
onUserMessageEvent(e, data) {
async onUserMessageEvent(e, data) {
const _this = this;
if (e.type == 'select') {
this.selectFlag = true;
@@ -206,24 +249,23 @@
}
if (e.type == 'selected') {
if (this.selectFlag == true) {
let founded = false;
let arr = [];
for (var index = 0; index < this.selectItems.length; index++) {
var v = this.selectItems[index];
if (v.clientMsgID == data.clientMsgID) {
founded = true;
} else {
arr.push(v);
}
if(noticeMessageTypes.includes(data.contentType)){
return ;
}
if (!founded) {
arr.push(data);
let arr = this.selectItems.filter((item)=>item.clientMsgID != data.clientMsgID);
if(arr.length === this.selectItems.length){
arr.push(data)
}
this.selectItems = [...arr];
}
return;
}
if (e.type == 'deleteMsg') {
if (e.type == 'deleteMsg' || e.type == 'deleteServerMsg') {
let method = IMSDK.IMMethods.DeleteMessageFromLocalStorage;
if(e.type == 'deleteServerMsg'){
method = IMSDK.IMMethods.RevokeMessage
}
//console.log(method);
let deleteMsgs = [];
if (!data) {
deleteMsgs = [...this.selectItems];
@@ -232,19 +274,13 @@
}
for (let i = 0; i < deleteMsgs.length; i++) {
let element = deleteMsgs[i];
IMSDK.asyncApi('deleteMessageFromLocalStorage', IMSDK.uuid(), {
let a = await IMSDK.asyncApi(method, IMSDK.uuid(), {
conversationID: _this.storeCurrentConversation.conversationID,
clientMsgID: element.clientMsgID
}).then(res => {
//console.log(res);
}).catch(res => {
//console.log(res);
}).finally(() => {
//console.log(arguments);
})
});
//console.log(a);
}
this.selectItems = [];
this.$refs.chatingListRef.loadMessageList();
return;
}
if (e.type == 'forward') {
@@ -291,7 +327,11 @@
conversationID: _this.storeCurrentConversation.conversationID,
clientMsgID: data.clientMsgID
}).then(res => {
console.log(res);
let list = _this.storeHistoryMessageList;
list = list.filter((item)=>{
return item.clientMsgID != data.clientMsgID;
});
_this.$store.commit('message/SET_HISTORY_MESSAGE_LIST',list);
}).catch(res => {
console.log(res);
}).finally(() => {
@@ -299,7 +339,7 @@
})
return;
}
if(e.type == 'audio_msg_click'){
if (e.type == 'audio_msg_click'){
if(_this.storeCurrentMsgID){
_this.updateCurrentMsg({clientMsgID:""});
}else{
@@ -318,9 +358,12 @@
let nowTime = new Date().getTime();
let msgTime = data.createTime;
let diff = nowTime - msgTime;
if (this.isSender && diff < 120000) {
if (_this.storeCurrentUserID == data.sendID && diff < 120000) {
menu.push('撤回')
}
if(_this.isAdmin || _this.isOwner){
menu.push('撤回(管理员功能)')
}
uni.showActionSheet({
itemList: menu,
success({tapIndex}) {
@@ -360,6 +403,9 @@
case "删除":
_this.onUserMessageEvent({type: 'deleteMsg'}, data);
break;
case "撤回(管理员功能)":
_this.onUserMessageEvent({type: 'deleteServerMsg'}, data);
break;
default:
break;
}
@@ -367,6 +413,20 @@
})
return;
}
if (e.type == 'avatarClick'){
if(data.sendID == this.storeCurrentUserID){
return ;
}
if(!_this.isSingle){
if(this.storeCurrentMemberInGroup.roleLevel < 60 && this.storeCurrentGroup.lookMemberInfo!=0){
return ;
}
}
uni.navigateTo({
url:"/pages/common/userCard/index?sourceID="+data.sendID
})
return ;
}
}
},
onBackPress() {
@@ -24,7 +24,7 @@
<uni-icons size="24" color="#FFF" type="personadd"></uni-icons>
<text>添加好友</text>
</view>
<view @click="clickMenu({name:'createGroup'})" class="menu_item">
<view @click="clickMenu({name:'scan'})" class="menu_item">
<uni-icons size="24" color="#FFF" type="scan"></uni-icons>
<text>扫一扫</text>
</view>
@@ -191,6 +191,45 @@
padding: 0 24rpx 10rpx;
//margin-top: var(--status-bar-height);
background: #f4f4f4;
.status_notice{
.err-tag {
display: flex;
justify-content: center;
align-items: center;
height: 44rpx;
background: #ffe1dd;
border-radius: 12rpx 12rpx 12rpx 12rpx;
margin-left: 8rpx;
.status {
font-size: 24rpx;
margin-left: 8rpx;
font-weight: 400;
color: #ff381f;
}
}
.tag {
display: flex;
justify-content: center;
align-items: center;
height: 44rpx;
background: #f2f8ff;
border-radius: 12rpx 12rpx 12rpx 12rpx;
margin-left: 8rpx;
.loading {
animation: loading 1.5s infinite;
}
.status {
font-size: 24rpx;
margin-left: 8rpx;
font-weight: 400;
color: #0089ff;
}
}
}
.self_info {
@include btwBox();
@@ -216,46 +255,6 @@
max-width: 240rpx;
}
.err-tag {
display: flex;
justify-content: center;
align-items: center;
width: 152rpx;
height: 44rpx;
background: #ffe1dd;
border-radius: 12rpx 12rpx 12rpx 12rpx;
margin-left: 8rpx;
.status {
font-size: 24rpx;
margin-left: 8rpx;
font-weight: 400;
color: #ff381f;
}
}
.tag {
display: flex;
justify-content: center;
align-items: center;
width: 152rpx;
height: 44rpx;
background: #f2f8ff;
border-radius: 12rpx 12rpx 12rpx 12rpx;
margin-left: 8rpx;
.loading {
animation: loading 1.5s infinite;
}
.status {
font-size: 24rpx;
margin-left: 8rpx;
font-weight: 400;
color: #0089ff;
}
}
.online_state {
@include vCenterBox();
margin-left: 24rpx;
@@ -1,7 +1,10 @@
<template>
<view @longtap.prevent="longtapConversationItem" @tap.prevent="clickConversationItem" :class="['conversation_item',source.isPinned?'pinned' : '']">
<view class="left_info">
<my-avatar :isGroup="isGroup" :isNotify="isNotify" :src="source.faceURL" :desc="source.showName" size="46" />
<view class="avatar">
<my-avatar :isGroup="isGroup" :isNotify="isNotify" :src="source.faceURL" :desc="source.showName" size="46" />
<u-badge v-if="source.unreadCount>0" max="99" :value="source.unreadCount"></u-badge>
</view>
<view class="details">
<view class="title">
<text class="conversation_name">
@@ -9,11 +12,11 @@
</text>
<view class="right_desc">
<text class="send_time">{{ latestMessageTime }}</text>
<u-badge max="99" :value="source.unreadCount"></u-badge>
</view>
</view>
<view class="lastest_msg_wrap">
<text class="lastest_msg_content">{{ latestMessage }}</text>
<uni-icons v-if="source.recvMsgOpt===2" custom-prefix="imfont" type="im-bell-off" size="16" color="#ddd"></uni-icons>
</view>
</view>
</view>
@@ -45,6 +48,7 @@
parsedMessage = JSON.parse(this.source.latestMsg);
} catch (e) {}
if (!parsedMessage) return "";
//console.log(parsedMessage);
return getConversationContent(parsedMessage);
},
latestMessageTime() {
@@ -101,21 +105,29 @@
box-sizing: border-box;
padding: 12rpx 44rpx 20rpx;
flex:1;
.avatar{
position: relative;
.u-badge{
position: absolute;
right: -6rpx;
top: -6rpx;
}
}
.details {
@include colBox(true);
display: flex;
flex-direction: column;
justify-content: space-around;
flex:1;
margin-left: 24rpx;
height: 46px;
color: $uni-text-color;
padding-bottom:20rpx;
color: $u-main-color;
.title{
@include btwBox();
.conversation_name {
@include nomalEllipsis();
max-width: 40vw;
font-size: 32rpx;
font-weight: 500;
font-weight: 400;
}
.right_desc {
@@ -138,6 +150,7 @@
.lastest_msg_wrap {
display: flex;
align-items: center;
font-size: 24rpx;
margin: 10rpx 0;
color: #666;
@@ -160,5 +173,6 @@
}
}
}
}
</style>
+64 -62
View File
@@ -1,15 +1,17 @@
<template>
<view class="conversation_container">
<chat-header ref="chatHeaderRef" />
<scroll-view class="scroll-view" scroll-y="true" refresher-enabled="true" :refresher-triggered="triggered"
:scroll-top="scrollTop" :scroll-with-animation="true" @scroll="scroll" @refresherrefresh="onRefresh"
@refresherrestore="onRestore" @scrolltolower="scrolltolower">
<uni-swipe-action ref="swipe_action">
<uni-swipe-action-item :right-options="swipe_actions" @click="actionClick($event,item)" v-for="item in storeConversationList" :key="item.conversationID" >
<conversation-item @longtapEvent="showExtendMenu(item)" :source="item"/>
</uni-swipe-action-item>
</uni-swipe-action>
</scroll-view>
<uni-swipe-action ref="swipe_action">
<template v-for="item in storeConversationList" >
<uni-swipe-action-item
v-if="item.sendID !='system1'"
:right-options="swipe_actions"
@click="actionClick($event,item)"
:key="item.conversationID" >
<conversation-item @longtapEvent="showExtendMenu(item)" :source="item"/>
</uni-swipe-action-item>
</template>
</uni-swipe-action>
<view class="loading_wrap" v-if="storeProgress > 0 && storeProgress < 100">
<u-loading-icon :vertical="true" :text="storeProgress + '%'"></u-loading-icon>
</view>
@@ -55,12 +57,16 @@
}
}
],
triggered: false,
refreshing: false,
};
},
computed: {
...mapGetters(["storeConversationList", "storeIsSyncing", "storeProgress"]),
...mapGetters([
"storeConversationList",
"storeIsSyncing",
"storeProgress",
'storeCurrentUserID'
]),
},
onReady() {
// #ifdef APP
@@ -69,23 +75,18 @@
},
onLoad() {
//console.log(this.storeConversationList);
this._freshing = false;
this.triggered = true;
setTimeout(()=>{
// uni.navigateTo({
// //url:'/pages/workbench/friend-circle/friend-circle',
// url:'/pages/common/map',
// events: {
// onConfirm(res) {
// console.log(res);
// }
// }
// })
// uni.switchTab({
// url:"/pages/profile/index/index"
// })
prepareConversationState(this.storeConversationList[1]);
},1000)
this.freshing = false;
if(process.env.NODE_ENV == 'development'){
setTimeout(()=>{
// uni.switchTab({
// url:"/pages/user/index/index"
// })
// uni.navigateTo({
// url:"/pages/conversation/groupAlbum/groupAlbum?groupID=1731702760"
// });
//prepareConversationState(this.storeConversationList[0]);
},1000)
}
},
methods: {
test(){
@@ -93,33 +94,33 @@
},
showExtendMenu(item){
const _this = this;
const menu = [item.recvMsgOpt===0 ? '关闭免打扰':'免打扰',item.isPrivateChat? '关闭阅后即焚':'开启阅后即焚','隐藏',item.isPinned ? '取消置顶':'置顶','删除'];
const menu = [item.recvMsgOpt===0 ? '免打扰':'关闭免打扰',item.isPrivateChat? '关闭阅后即焚':'开启阅后即焚','隐藏',item.isPinned ? '取消置顶':'置顶','删除'];
uni.showActionSheet({
itemList:menu,
success(e) {
switch(menu[e.tapIndex]){
case '免打扰':
IMSDK.asyncApi('setConversation', IMSDK.uuid(), {
conversationID: conversationId,
_this.setConversation({
conversationID: item.conversationID,
recvMsgOpt: 2
})
break;
case '关闭免打扰':
IMSDK.asyncApi('setConversation', IMSDK.uuid(), {
conversationID: conversationId,
_this.setConversation({
conversationID: item.conversationID,
recvMsgOpt: 0
})
break;
case '开启阅后即焚':
IMSDK.asyncApi('setConversation', IMSDK.uuid(), {
conversationID: conversationId,
_this.setConversation({
conversationID: item.conversationID,
isPrivateChat: true,
burnDuration:60
})
break;
case '关闭阅后即焚':
IMSDK.asyncApi('setConversation', IMSDK.uuid(), {
conversationID: conversationId,
_this.setConversation({
conversationID: item.conversationID,
isPrivateChat: false
})
break;
@@ -127,10 +128,16 @@
IMSDK.asyncApi('hideConversation', IMSDK.uuid(), item.conversationID)
break;
case '置顶':
_this.setPinConversation(item.conversationID,true);
_this.setConversation({
conversationID: item.conversationID,
isPinned: true
});
break;
case '取消置顶':
_this.setPinConversation(item.conversationID,false);
_this.setConversation({
conversationID: item.conversationID,
isPinned: false
})
break;
case '删除':
_this.deleteConversation(item.conversationID);
@@ -155,43 +162,44 @@
},
deleteConversation(conversationID){
IMSDK.asyncApi(IMSDK.IMMethods.DeleteConversationAndDeleteAllMsg, IMSDK.uuid(), conversationID).then(res=>{
this.$store.dispatch('conversation/deleteConversation',conversationID).then(res=>{
console.log('删除成功');
}).catch(e=>{
})
},
setPinConversation(conversationId,status){
IMSDK.asyncApi('setConversation', IMSDK.uuid(), {
conversationID: conversationId,
isPinned: status
})
async setConversation(data){
await IMSDK.asyncApi('setConversation', IMSDK.uuid(), data);
},
scroll(e) {
this.old.scrollTop = e.detail.scrollTop;
},
onRefresh() {
if (this._freshing) return;
this._freshing = true;
if (this.refreshing) return;
console.log('onRefresh');
this.refreshing = true;
this.queryList(true);
},
onRestore() {
this.triggered = "restore";
console.log("onRestore");
},
scrolltolower() {
this.queryList();
},
async queryList(isFirstPage = false) {
await this.$store.dispatch("conversation/getConversationList",isFirstPage);
this.triggered = false;
this._freshing = false;
uni.stopPullDownRefresh();
this.refreshing = false;
},
closeAllSwipe() {
this.$refs.swipeWrapperRef.closeAll();
},
},
onReachBottom() {
this.scrolltolower();
},
onPullDownRefresh() {
this.queryList(true);
},
onPageScroll(res) {
this.old.scrollTop = res.scrollTop;
}
};
</script>
@@ -199,7 +207,6 @@
.conversation_container {
@include colBox(false);
height: 100vh;
overflow-y: hidden;
}
.conversation_search {
@@ -217,11 +224,6 @@
overflow-y: auto;
}
.scroll-view {
height: 0;
flex: 1;
}
.loading_wrap {
position: absolute;
top: 50%;
@@ -0,0 +1,369 @@
<template>
<view class="group_album_page">
<uni-nav-bar left-icon="back" @clickLeft="back" @clickRight="manage" title="群相册" statusBar fixed>
<view slot="left">
<uni-icons type="back"></uni-icons>
</view>
<view slot="right" v-if="isAdmin">
<text v-if="manageStatus">取消</text>
<text v-else>管理</text>
</view>
</uni-nav-bar>
<view class="list">
<view class="item" v-if="isAdmin">
<view class="pickfile" @click="pickMedia">
<u-icon
color="#D3d3d3"
size="40"
name="plus"
></u-icon>
</view>
</view>
<template v-for="(item,index) in fileList">
<view v-if="item.status!=1" :key="'fileList'+index" class="item">
<c-CacheImage :src="item.url" width="234rpx" height="234rpx"></c-CacheImage>
<view class="overlay"></view>
<u-line-progress :percentage="item.progress" showText></u-line-progress>
</view>
</template>
<view v-for="(item,index) in list" :key="index" class="item" @click="itemClick(item,index)" :class="{cancheck:manageStatus,checked:selectedItemIds.includes(item.id)}">
<c-CacheImage :src="item.url" width="234rpx" height="234rpx" @click="itemClick(item,index)" ></c-CacheImage>
</view>
</view>
<u-loadmore :status="load_status" />
<view class="footer" v-if="manageStatus">
<button @click="selectAll" size="mini">全选</button>
<button @click="deleteItem" type="warn" size="mini">删除({{selectedItemIds.length}})</button>
</view>
</view>
</template>
<script>
import util from "@/util/index.js"
import {upload} from "@/api/login.js"
import {mapGetters} from "vuex";
import {GroupMemberRole} from "openim-uniapp-polyfill";
export default {
data() {
return {
albumWidth: 0,
fileList: [],
list: [
{
id:1,
url: "https://cdn.uviewui.com/uview/album/1.jpg",
},
{
id:2,
url: "https://cdn.uviewui.com/uview/album/2.jpg",
},
{
id:3,
url: "https://cdn.uviewui.com/uview/album/3.jpg",
},
{
id:4,
url: "https://cdn.uviewui.com/uview/album/4.jpg",
},
{
id:5,
url: "https://cdn.uviewui.com/uview/album/5.jpg",
},
{
id:6,
url: "https://cdn.uviewui.com/uview/album/6.jpg",
},
{
id:7,
url: "https://cdn.uviewui.com/uview/album/7.jpg",
},
{
id:8,
url: "https://cdn.uviewui.com/uview/album/8.jpg",
},
{
id:9,
url: "https://cdn.uviewui.com/uview/album/9.jpg",
},
],
manageStatus: false,
selectedItemIds:[],
uploadButtonWidth:0,
load_status:"loadmore",
param: {
groupID: "",
offset: 999999999999,
limit: 10,
trace: 1,
}
};
},
computed: {
...mapGetters([
"storeCurrentConversation",
"storeCurrentMemberInGroup",
"storeCurrentGroup",
]),
isAdmin() {
return this.storeCurrentMemberInGroup.roleLevel > GroupMemberRole.Normal;
},
},
onLoad(opt) {
this.param.groupID = opt.groupID
//this.param.groupID = opt.storeCurrentConversation.groupID;
this.init();
this.uploadButtonWidth = uni.$u.getPx('234rpx');
},
methods: {
init() {
this.getList(true);
},
getList(isFirstPage = false) {
this.param.offset = 21;
if (isFirstPage === true) {
this.list = [];
this.param.offset = 20;
this.param.offset = 999999999999;
}
this.load_status = 'loading';
uni.$u.http.post('/group/album_list', this.param).then(res => {
if(res.length>0){
this.param.offset = res[0].id;
this.list = this.list.concat(res);
this.load_status = 'loadmore';
if(res.length < this.param.limit){
this.load_status = 'nomore';
}
return ;
}
this.load_status = 'nomore';
}).catch(e => {
this.load_status = 'loadmore';
})
},
itemClick(item,index) {
if(this.manageStatus && this.isAdmin){
if(this.selectedItemIds.includes(item.id)){
this.selectedItemIds = this.selectedItemIds.filter(id=>item.id!=id)
}else{
this.selectedItemIds.push(item.id);
}
return ;
}
const urls = this.list.map(item=>util.cdn(item.url));
uni.previewImage({
urls:urls,
current:index,
})
},
deleteItem(){
if(!this.isAdmin || this.selectedItemIds.length == 0){
return ;
}
const _this = this;
uni.showModal({
content:"确定删除吗?一旦删除不能恢复",
success(res) {
if(res.confirm){
_this.doDelete();
}
}
})
},
selectAll(){
if(!this.isAdmin){
return ;
}
const arr = [];
this.list.forEach(item=>{
arr.push(item.id);
})
this.selectedItemIds = [...arr];
},
doDelete(){
if(!this.isAdmin || this.selectedItemIds.length == 0){
return ;
}
uni.$u.http.post('/group/album_delete', {ids:this.selectedItemIds}).then(res => {
this.list = this.list.filter(item=>!this.selectedItemIds.includes(item.id));
this.selectedItemIds = [];
this.manageStatus = false;
}).catch(e => {
uni.$u.toast(e?.msg || e);
})
},
back() {
if (this.manageStatus) {
this.manageStatus = false;
return;
}
util.goto(1);
},
manage() {
if(!this.isAdmin){
return ;
}
this.manageStatus = !this.manageStatus;
},
pickMedia(){
if(!this.isAdmin){
return ;
}
const _this = this;
uni.chooseImage({
success(res) {
//res.tempFilePaths[0]
//res.tempFiles[0].path
//res.tempFiles[0].size
let i = 0;
_this.fileList = res.tempFilePaths.map(path=>{
i++;
return {
"id": 'temp_'+i,
"group_id": 0,
"user_id": 0,
"url": path,
"title": path,
"progress": 0,
"status": 0,
};
});
_this.batchUpload();
}
});
// uni.chooseMedia({
// count: 9,
// mediaType: ['image'],
// sourceType: ['album', 'camera'],
// success(res) {
// console.log(res.tempFiles[0].tempFilePath)
// }
// })
},
async batchUpload(){
if(!this.isAdmin){
return ;
}
const _this = this;
const lists = [].concat(this.fileList);
let result = {};
for (let i = 0; i < lists.length; i++) {
result = await upload(lists[i].url,{
savePath: "album" ,
url:"/group/album_create",
groupID:_this.param.groupID
},(res)=>{
try{
_this.fileList[i].progress = res.progress;
}catch(e){
}
});
console.log(result);
if(result.code == 0){
_this.fileList[i].status = 1;
_this.list.unshift(result.data);
}else{
uni.$u.toast(data.msg);
}
}
_this.fileList = [];
}
},
onPullDownRefresh() {
this.getList(true);
},
onReachBottom() {
this.getList();
}
}
</script>
<style lang="scss" scoped>
.group_album_page{
.footer{
position: fixed;
left: 0;
right: 0;
bottom: 0;
height: 44px;
background-color: #fff;
z-index: 10;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20rpx;
uni-button{
margin: 0;
}
}
.list{
display: flex;
align-items: center;
flex-wrap: wrap;
margin-top: 12rpx;
padding-right: 12rpx;
.item{
position: relative;
margin-bottom: 12rpx;
margin-left: 12rpx;
::v-deep .u-upload__button{
margin: 0;
}
&.cancheck::after{
content: " ";
font-family: uicon-iconfont;
position: relative;
display: flex;
flex-direction: row;
align-items: center;
width: 18px;
height: 18px;
font-size: 20px;
text-align: center;
background-color: rgb(255, 255, 255);
border-color: rgb(200, 201, 204);
border-style: solid;
border-width: 1px;
border-radius: 100%;
position: absolute;
bottom: 0;
left: 0;
}
&.cancheck.checked::after{
content: "";
background-color: rgb(41, 121, 255);
border-color: rgb(41, 121, 255);
color:#FFF;
}
.overlay{
background: rgba(0, 0, 0, 0.6);
position: absolute;
z-index: 9;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.u-line-progress{
position: absolute;
z-index: 10;
top:50%;
margin-top: -6px;
width: 80%;
left: 10%;
}
.pickfile{
display: flex;
align-items: center;
justify-content: center;
background-color: #f6f6f6;
width: 234rpx;
height: 234rpx;
}
}
}
}
</style>
+35 -10
View File
@@ -3,10 +3,12 @@
<u-navbar :autoBack="true" bgColor="#ECECEC" title="群管理" safeAreaInsetTop placeholder fixed></u-navbar>
<uni-list>
<uni-list-item title="群主管理权转让" @click="toTransfer" showArrow clickable></uni-list-item>
<uni-list-item title="设置管理员" @click="setAdmin" showArrow clickable></uni-list-item>
<uni-list-item title="进群验证" @switchChange="updateGroupInfo('needVerification',storeCurrentGroup.needVerification==1 ? 2: 1)" showSwitch :switchChecked="!(storeCurrentGroup.needVerification != 1)"></uni-list-item>
<uni-list-item title="允许查看成员资料" @switchChange="updateGroupInfo('lookMemberInfo',storeCurrentGroup.applyMemberFriend == 1 ? 0 : 1)" showSwitch :switchChecked="storeCurrentGroup.applyMemberFriend==0"></uni-list-item>
<uni-list-item title="允许群内添加好友" @switchChange="updateGroupInfo('applyMemberFriend',storeCurrentGroup.applyMemberFriend == 1 ? 0 : 1)" showSwitch :switchChecked="storeCurrentGroup.applyMemberFriend==0"></uni-list-item>
<uni-list-item :title="isMute ? '解除禁言':'全员禁言'" @switchChange="updateMute" showSwitch :switchChecked="isMute"></uni-list-item>
<!-- <uni-list-item title="进群验证" :rightText="verifyTypeText" @click="changeVerify" clickable></uni-list-item> -->
<uni-list-item title="允许查看成员资料" @switchChange="updateGroupInfo('lookMemberInfo',storeCurrentGroup.lookMemberInfo == 1 ? 0 : 1)" showSwitch :switchChecked="storeCurrentGroup.lookMemberInfo==0"></uni-list-item>
<!-- <uni-list-item title="允许群内添加好友" @switchChange="updateGroupInfo('applyMemberFriend',storeCurrentGroup.applyMemberFriend == 1 ? 0 : 1)" showSwitch :switchChecked="storeCurrentGroup.applyMemberFriend==0"></uni-list-item> -->
<uni-list-item title="全员禁言" @switchChange="updateMute" showSwitch :switchChecked="isMute"></uni-list-item>
</uni-list>
</view>
@@ -14,13 +16,7 @@
<script>
import {mapGetters} from "vuex";
import IMSDK, {
GroupMemberRole,
GroupStatus,
GroupVerificationType,
IMMethods,
MessageReceiveOptType,
} from "openim-uniapp-polyfill";
import IMSDK, {GroupMemberRole,GroupStatus,GroupVerificationType,IMMethods,MessageReceiveOptType,} from "openim-uniapp-polyfill";
import {GroupMemberListTypes} from "@/constant";
export default {
data() {
@@ -34,8 +30,19 @@
"storeCurrentMemberInGroup",
"storeCurrentGroup",
]),
verifyTypeText(){
if(this.storeCurrentGroup.needVerification === 0){
return '0'
}
if(this.storeCurrentGroup.needVerification === 1){
return '1'
}
return '2';
}
},
onLoad() {
this.isMute = this.storeCurrentGroup.status == 3;
console.log(this.storeCurrentGroup);
// IMSDK.asyncApi('getSpecifiedGroupsInfo', IMSDK.uuid(), [this.storeCurrentGroup.groupID]).then(res=>{
// console.log(res);
// }).catch(e=>{
@@ -48,7 +55,13 @@
url: `/pages/conversation/groupMemberList/index?type=${GroupMemberListTypes.Transfer}&groupID=${this.storeCurrentGroup.groupID}`,
});
},
setAdmin(){
uni.navigateTo({
url: `/pages/conversation/groupMemberList/index?type=${GroupMemberListTypes.setAdmin}&groupID=${this.storeCurrentGroup.groupID}`,
});
},
updateMute(){
this.isMute = !this.isMute;
IMSDK.asyncApi('changeGroupMute', IMSDK.uuid(), {
groupID: this.storeCurrentGroup.groupID,
isMute: this.isMute
@@ -67,6 +80,18 @@
console.log(e);
})
},
changeVerify(){
uni.showActionSheet({
itemList:[
'申请进群需要群主或管理员同意;群成员邀请可以直接进群',
'申请进群需要群主或管理员同意;群主和管理员邀请可以直接进群;普通成员邀请进群需群主或管理员同意',
'直接进群'
],
success(res){
console.log(res);
}
})
}
},
};
</script>
@@ -1,126 +1,128 @@
<template>
<custom-nav-bar @leftClick="leftClick" :title="getTitle">
</custom-nav-bar>
<custom-nav-bar @leftClick="leftClick" :title="getTitle">
</custom-nav-bar>
</template>
<script>
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import { ContactChooseTypes } from "@/constant";
export default {
name: "",
components: {
CustomNavBar,
},
props: {
checkVisible: {
type: Boolean,
default: false,
},
isNomal: {
type: Boolean,
default: false,
},
isTransfer: {
type: Boolean,
default: false,
},
isAt: {
type: Boolean,
default: false,
},
isSetAdmin: {
type: Boolean,
default: false,
},
isMute: {
type: Boolean,
default: false,
},
isCall: {
type: Boolean,
default: false,
},
groupID: String,
},
data() {
return {
moreMenuVisible: false,
};
},
computed: {
getTitle() {
if (this.isCall) {
return "邀请成员";
}
if (this.isAt) {
return "选择提醒的人";
}
if (this.isSetAdmin) {
return "设置管理员";
}
if (this.isMute) {
return "禁言成员";
}
if (this.checkVisible) {
return "移除群成员";
}
return "群成员";
},
},
methods: {
leftClick() {
if (this.checkVisible) {
this.$emit("update:checkVisible", false);
}
},
rightClick() {
this.moreMenuVisible = true;
},
checkMenu() {
if (this.moreMenuVisible) {
this.moreMenuVisible = false;
}
},
inviteMember() {
uni.navigateTo({
url: `/pages/common/contactChoose/index?type=${ContactChooseTypes.Invite}&groupID=${this.groupID}`,
});
},
removeMember() {
this.$emit("removeMember");
},
},
};
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import {
ContactChooseTypes
} from "@/constant";
export default {
name: "",
components: {
CustomNavBar,
},
props: {
checkVisible: {
type: Boolean,
default: false,
},
isNomal: {
type: Boolean,
default: false,
},
isTransfer: {
type: Boolean,
default: false,
},
isAt: {
type: Boolean,
default: false,
},
isSetAdmin: {
type: Boolean,
default: false,
},
isMute: {
type: Boolean,
default: false,
},
isCall: {
type: Boolean,
default: false,
},
groupID: String,
},
data() {
return {
moreMenuVisible: false,
};
},
computed: {
getTitle() {
if (this.isCall) {
return "邀请成员";
}
if (this.isAt) {
return "选择提醒的人";
}
if (this.isSetAdmin) {
return "设置管理员";
}
if (this.isMute) {
return "禁言成员";
}
if (this.checkVisible) {
return "移除群成员";
}
return "群成员";
},
},
methods: {
leftClick() {
if (this.checkVisible) {
this.$emit("update:checkVisible", false);
}
},
rightClick() {
this.moreMenuVisible = true;
},
checkMenu() {
if (this.moreMenuVisible) {
this.moreMenuVisible = false;
}
},
inviteMember() {
uni.navigateTo({
url: `/pages/common/contactChoose/index?type=${ContactChooseTypes.Invite}&groupID=${this.groupID}&allowType=User`,
});
},
removeMember() {
this.$emit("removeMember");
},
},
};
</script>
<style lang="scss">
.more_container {
position: relative;
.more_container {
position: relative;
.more_dot {
padding: 24rpx;
margin-right: 20rpx;
}
.more_dot {
padding: 24rpx;
margin-right: 20rpx;
}
.more_menu {
position: absolute;
bottom: 0;
left: 50%;
transform: translate(-100%, 100%);
box-shadow: 0px 0px 6px 2px rgba(0, 0, 0, 0.16);
width: max-content;
border-radius: 8rpx;
background-color: #fff;
.more_menu {
position: absolute;
bottom: 0;
left: 50%;
transform: translate(-100%, 100%);
box-shadow: 0px 0px 6px 2px rgba(0, 0, 0, 0.16);
width: max-content;
border-radius: 8rpx;
background-color: #fff;
.menu_item {
padding: 20rpx 48rpx;
font-size: 28rpx;
color: $uni-text-color;
.menu_item {
padding: 20rpx 48rpx;
font-size: 28rpx;
color: $uni-text-color;
// &:nth-child(1) {
// border-bottom: 1px solid #F0F0F0;
// }
}
}
}
</style>
// &:nth-child(1) {
// border-bottom: 1px solid #F0F0F0;
// }
}
}
}
</style>
+460 -299
View File
@@ -1,314 +1,475 @@
<template>
<view @click="pageClick" class="group_members_container">
<group-member-list-header
ref="navHeaderRef"
:checkVisible.sync="showCheck"
:isTransfer="isTransfer"
:isNomal="!isOwner && !isAdmin"
:groupID="groupID"
@removeMember="showMemberCheck"
/>
<view @click="pageClick" class="group_members_container">
<group-member-list-header
ref="navHeaderRef"
:checkVisible.sync="showCheck"
:isTransfer="isTransfer"
:isSetAdmin="isSetAdmin"
:isNomal="!isOwner && !isAdmin"
:groupID="groupID"
@removeMember="showMemberCheck"
/>
<view class="search_bar_wrap">
<u-search
disabled
class="search_bar"
shape="square"
placeholder="搜索"
:showAction="false"
v-model="keyword"
/>
</view>
<view class="search_bar_wrap">
<u-search class="search_bar" shape="square" placeholder="搜索" :showAction="false"
v-model="keyword" />
</view>
<u-list
class="member_list"
@scrolltolower="loadMore"
lowerThreshold="100"
height="1"
>
<u-list-item v-for="member in groupMemberList" :key="member.userID">
<user-item
@itemClick="userClick"
@updateCheck="updateCheck"
:checked="isChecked(member.userID)"
:checkVisible="showCheck"
:disabled="!canCheck(member) && showCheck"
:item="member"
/>
</u-list-item>
<view
v-show="loadState.loading"
class="member_loading"
>
<u-loading-icon></u-loading-icon>
</view>
</u-list>
<u-list class="member_list" @scrolltolower="loadMore" lowerThreshold="100" height="1">
<u-list-item v-for="member in filterGroupMemberList" :key="member.userID">
<user-item
@itemClick="userClick"
@updateCheck="updateCheck"
@longtapEvent="longtap"
:checked="isChecked(member.userID)"
:checkVisible="showCheck"
:disabled="!canCheck(member) && showCheck" :item="member"
/>
</u-list-item>
<view v-show="loadState.loading" class="member_loading">
<u-loading-icon></u-loading-icon>
</view>
</u-list>
<template v-if="showCheck" >
<choose-index-footer
:comfirmLoading="comfirmLoading"
@removeItem="updateCheck"
@confirm="confirm"
:choosedData="getChoosedData"
:isRemove="isRemove"
:maxLength="groupMemberLength" />
</template>
<choose-index-footer
v-if="showCheck"
:comfirmLoading="comfirmLoading"
@removeItem="updateCheck"
@confirm="confirm"
:choosedData="getChoosedData"
:isRemove="isRemove"
:maxLength="groupMemberLength"
/>
<u-modal
:show="showConfirmModal"
asyncClose
showCancelButton
@confirm="modalConfirm"
@cancel="() => (showConfirmModal = false)"
:content="
isRemove
? '确定移除已选群成员?'
: `确定要把群主转移给${choosedTransferMember.nickname}吗?`
"
/>
<u-toast ref="uToast"></u-toast>
</view>
<u-modal
:show="showConfirmModal"
asyncClose
showCancelButton
@confirm="modalConfirm"
@cancel="() => (showConfirmModal = false)" :content="isRemove? '确定移除已选群成员?': `确定要把群主转移给${choosedTransferMember.nickname}吗?`
" />
<u-toast ref="uToast"></u-toast>
</view>
</template>
<script>
let moreActionArea;
import { GroupMemberListTypes } from "@/constant";
import IMSDK, { GroupMemberRole } from "openim-uniapp-polyfill";
import UserItem from "@/components/UserItem/index.vue";
import GroupMemberListHeader from "./components/GroupMemberListHeader.vue";
import ChooseIndexFooter from "@/components/ChooseIndexFooter/index.vue";
export default {
components: {
GroupMemberListHeader,
ChooseIndexFooter,
UserItem,
},
data() {
return {
showCheck: false,
groupID: "",
keyword: "",
showConfirmModal: false,
comfirmLoading: false,
groupMemberList: [],
choosedMemberIDList: [],
choosedTransferMember: {},
type: GroupMemberListTypes.Preview,
isRightKick: true,
loadState: {
hasMore: true,
loading: false,
},
};
},
computed: {
getChoosedData() {
const tmpList = [...this.choosedMemberIDList];
return this.groupMemberList.filter(
(member) => {
const idx = tmpList.findIndex((item) => item === member.userID);
if (idx > -1) {
tmpList.splice(idx, 1);
}
return idx > -1;
},
);
},
isRemove() {
return this.type === GroupMemberListTypes.Kickout;
},
isTransfer() {
return this.type === GroupMemberListTypes.Transfer;
},
isChecked() {
return (userID) => this.choosedMemberIDList.includes(userID);
},
isOwner() {
return (
this.$store.getters.storeCurrentMemberInGroup.roleLevel ===
GroupMemberRole.Owner
);
},
isAdmin() {
return (
this.$store.getters.storeCurrentMemberInGroup.roleLevel ===
GroupMemberRole.Admin
);
},
canCheck() {
return ({ roleLevel, userID }) => {
if (this.type === GroupMemberListTypes.Kickout) {
return (
(this.isOwner ||
(this.isAdmin && roleLevel !== GroupMemberRole.Owner)) &&
userID !== this.$store.getters.storeCurrentUserID
);
}
let moreActionArea;
import {mapActions,mapGetters} from "vuex";
import {GroupMemberListTypes} from "@/constant";
import IMSDK, {GroupMemberRole} from "openim-uniapp-polyfill";
import UserItem from "@/components/UserItem/index.vue";
import GroupMemberListHeader from "./components/GroupMemberListHeader.vue";
import ChooseIndexFooter from "@/components/ChooseIndexFooter/index.vue";
import util from "@/util/index.js"
export default {
components: {
GroupMemberListHeader,
ChooseIndexFooter,
UserItem,
},
data() {
return {
showCheck: false,
groupID: "",
keyword: "",
showConfirmModal: false,
comfirmLoading: false,
groupMemberList: [],
filterGroupMemberList:[],
choosedMemberIDList: [],
choosedTransferMember: {},
type: GroupMemberListTypes.Preview,
isRightKick: true,
loadState: {
hasMore: true,
loading: false,
},
adminUserIdList:[]
};
},
computed: {
...mapGetters([
'storeCurrentUserID',
'storeCurrentGroup',
"storeCurrentMemberInGroup"
]),
getChoosedData() {
const tmpList = [...this.choosedMemberIDList];
return this.groupMemberList.filter(
(member) => {
const idx = tmpList.findIndex((item) => item === member.userID);
if (idx > -1) {
tmpList.splice(idx, 1);
}
return idx > -1;
},
);
},
isRemove() {
return this.type === GroupMemberListTypes.Kickout;
},
isTransfer() {
return this.type === GroupMemberListTypes.Transfer;
},
isSetAdmin() {
return this.type === GroupMemberListTypes.setAdmin;
},
isChecked() {
return (userID) => this.choosedMemberIDList.includes(userID);
},
isOwner() {
return (
this.storeCurrentMemberInGroup.roleLevel ===
GroupMemberRole.Owner
);
},
isAdmin() {
return (
this.storeCurrentMemberInGroup.roleLevel === GroupMemberRole.Admin
);
},
canCheck() {
return ({
roleLevel,
userID
}) => {
if (this.type === GroupMemberListTypes.Kickout) {
return (
(this.isOwner || (this.isAdmin && roleLevel !== GroupMemberRole.Owner)) &&
userID !== this.$store.storeCurrentUserID
);
}
return userID !== this.$store.getters.storeCurrentUserID;
};
},
groupMemberLength() {
return this.$store.getters.storeCurrentGroup?.memberCount ?? 0;
},
},
onLoad(options) {
const { groupID, type } = options;
this.loadMemberList(groupID);
this.type = type;
this.groupID = groupID;
this.isRightKick = type === GroupMemberListTypes.Kickout;
if (
this.isRightKick
) {
this.showCheck = true;
}
},
methods: {
async pageClick(e) {
if (!moreActionArea) {
moreActionArea = await this.getEl(".more_container");
}
if (!moreActionArea) return;
if (
e.target.y < moreActionArea.top ||
e.target.y > moreActionArea.bottom ||
e.target.x < moreActionArea.left
) {
this.$refs.navHeaderRef.checkMenu();
}
},
confirm() {
this.showConfirmModal = true;
},
modalConfirm() {
let func = () => {};
if (this.type === GroupMemberListTypes.Kickout) {
func = IMSDK.asyncApi(IMSDK.IMMethods.KickGroupMember, IMSDK.uuid(), {
groupID: this.getChoosedData[0].groupID,
reason: "",
userIDList: this.getChoosedData.map((member) => member.userID),
});
} else {
func = IMSDK.asyncApi(
IMSDK.IMMethods.TransferGroupOwner,
IMSDK.uuid(),
{
groupID: this.choosedTransferMember.groupID,
newOwnerUserID: this.choosedTransferMember.userID,
},
);
}
func
.then(() => this.showToast("操作成功", () => uni.navigateBack()))
.catch(() => this.showToast("操作失败"))
.finally(() => (this.showConfirmModal = false));
},
updateChoosedData(userID) {
if (this.choosedMemberIDList.includes(userID)) {
const idx = this.choosedMemberIDList.findIndex(
(item) => item === userID,
);
const tmpArr = [...this.choosedMemberIDList];
tmpArr.splice(idx, 1);
this.choosedMemberIDList = tmpArr;
} else {
this.choosedMemberIDList = [...this.choosedMemberIDList, userID];
}
console.log(this.choosedMemberIDList);
},
userClick(member) {
if (this.type === GroupMemberListTypes.Transfer) {
if (member.userID === this.$store.getters.storeCurrentUserID) return;
this.choosedTransferMember = member;
this.showConfirmModal = true;
} else {
uni.$u.route("/pages/common/userCard/index", {
sourceID: member.userID,
memberInfo: JSON.stringify(member),
});
}
},
updateCheck(member) {
this.updateChoosedData(member.userID);
},
showMemberCheck() {
this.type = GroupMemberListTypes.Kickout;
this.showCheck = true;
},
loadMore() {
const stateKey = "loadState";
const methodKey = "loadMemberList";
if (this[stateKey].hasMore && !this[stateKey].loading) {
this[methodKey]();
}
},
loadMemberList(groupID) {
this.loadState.loading = true;
IMSDK.asyncApi(IMSDK.IMMethods.GetGroupMemberList, IMSDK.uuid(), {
groupID: groupID ?? this.groupID,
filter: 0,
offset: this.groupMemberList.length,
count: 500,
})
.then(({ data }) => {
this.groupMemberList = [...this.groupMemberList, ...data];
this.loadState.hasMore = data.length === 500;
})
.finally(() => (this.loadState.loading = false));
},
showToast(message, complete = null) {
this.$refs.uToast.show({
message,
complete,
});
},
getEl(el) {
return new Promise((resolve) => {
const query = uni.createSelectorQuery().in(this);
query
.select(el)
.boundingClientRect((data) => {
// data
resolve(data);
})
.exec();
});
},
},
onBackPress(options) {
if (this.showCheck && this.isRightKick) {
this.showCheck = false;
this.type = GroupMemberListTypes.Preview;
return true;
}
return false;
},
};
return userID !== this.$store.storeCurrentUserID;
};
},
groupMemberLength() {
return this.storeCurrentGroup?.memberCount ?? 0;
},
},
watch:{
keyword(v){
this.filterGroupMemberList = this.getFilterGroupMemberList();
},
groupMemberList(v){
this.filterGroupMemberList = this.getFilterGroupMemberList();
},
},
onLoad(options) {
const {
groupID,
type
} = options;
this.loadMemberList(groupID);
this.type = type;
this.groupID = groupID;
this.isRightKick = (type == GroupMemberListTypes.setAdmin|| type === GroupMemberListTypes.Kickout);
if (this.isRightKick) {
this.showCheck = true;
}
if(type == GroupMemberListTypes.setAdmin){
IMSDK.asyncApi('getGroupMemberOwnerAndAdmin', IMSDK.uuid(), this.groupID).then(({data}) => {
let arr = [];
data.forEach((item)=>{
if(item.roleLevel == GroupMemberRole.Admin){
arr.push(item.userID);
}
});
this.adminUserIdList = arr;
this.choosedMemberIDList = arr;
console.log(arr);
//
})
.catch(({ errCode, errMsg }) => {
console.log(errCode, errMsg );
//
})
}
},
methods: {
getFilterGroupMemberList(){
if(this.keyword){
return this.groupMemberList.filter((item)=>{
const kw = this.keyword.toLowerCase();
if(item.nickname && item.nickname.toLowerCase().includes(kw)){
return true;
}
if(item.showName && item.showName.toLowerCase().includes(kw)){
return true;
}
if(item.remark && item.remark.toLowerCase().includes(kw)){
return true;
}
if(item.groupName && item.groupName.toLowerCase().includes(kw)){
return true;
}
return false;
});
}
return this.groupMemberList;
},
async pageClick(e) {
if (!moreActionArea) {
moreActionArea = await this.getEl(".more_container");
}
if (!moreActionArea) return;
if (
e.target.y < moreActionArea.top ||
e.target.y > moreActionArea.bottom ||
e.target.x < moreActionArea.left
) {
this.$refs.navHeaderRef.checkMenu();
}
},
async confirm() {
if(this.isSetAdmin){
uni.showLoading({mask:true})
const remveUserIds = this.adminUserIdList.filter(userID=>{
return !this.choosedMemberIDList.includes(userID);
});
const addUserIds = this.choosedMemberIDList.filter(userID=>{
return !this.adminUserIdList.includes(userID);
});
remveUserIds.forEach(async (userID)=>{
await IMSDK.asyncApi(IMSDK.IMMethods.SetGroupMemberInfo,IMSDK.uuid(),{
roleLevel:GroupMemberRole.Normal,
groupID:this.groupID,
userID:userID
});
})
addUserIds.forEach(async (userID)=>{
await IMSDK.asyncApi(IMSDK.IMMethods.SetGroupMemberInfo,IMSDK.uuid(),{
roleLevel:GroupMemberRole.Admin,
groupID:this.groupID,
userID:userID
});
})
uni.hideLoading();
this.groupMemberList = [];
this.loadMemberList(this.groupID);
uni.navigateBack();
}else{
this.showConfirmModal = true;
}
},
modalConfirm() {
let func = () => {};
if (this.type === GroupMemberListTypes.Kickout) {
func = IMSDK.asyncApi(IMSDK.IMMethods.KickGroupMember, IMSDK.uuid(), {
groupID: this.getChoosedData[0].groupID,
reason: "",
userIDList: this.getChoosedData.map((member) => member.userID),
});
} else {
func = IMSDK.asyncApi(
IMSDK.IMMethods.TransferGroupOwner,
IMSDK.uuid(), {
groupID: this.choosedTransferMember.groupID,
newOwnerUserID: this.choosedTransferMember.userID,
},
);
}
func
.then(() => this.showToast("操作成功", () => uni.navigateBack()))
.catch(() => this.showToast("操作失败"))
.finally(() => (this.showConfirmModal = false));
},
updateChoosedData(userID) {
if (this.choosedMemberIDList.includes(userID)) {
const idx = this.choosedMemberIDList.findIndex(
(item) => item === userID,
);
const tmpArr = [...this.choosedMemberIDList];
tmpArr.splice(idx, 1);
this.choosedMemberIDList = tmpArr;
} else {
this.choosedMemberIDList = [...this.choosedMemberIDList, userID];
}
},
userClick(member) {
if (this.type === GroupMemberListTypes.Transfer) {
if (member.userID === this.storeCurrentUserID) return;
this.choosedTransferMember = member;
this.showConfirmModal = true;
} else {
if(member.userID == this.storeCurrentUserID){
return ;
}
if(this.storeCurrentMemberInGroup.roleLevel < 60 && this.storeCurrentGroup.lookMemberInfo!=0){
return ;
}
const s = util.aesencode(member);
uni.$u.route("/pages/common/userCard/index", {
sourceID: member.userID,
memberInfo: s,
});
}
},
updateCheck(member) {
this.updateChoosedData(member.userID);
},
showMemberCheck() {
this.type = GroupMemberListTypes.Kickout;
this.showCheck = true;
},
loadMore() {
const stateKey = "loadState";
const methodKey = "loadMemberList";
if (this[stateKey].hasMore && !this[stateKey].loading) {
this[methodKey]();
}
},
loadMemberList(groupID) {
this.loadState.loading = true;
IMSDK.asyncApi(IMSDK.IMMethods.GetGroupMemberList, IMSDK.uuid(), {
groupID: groupID ?? this.groupID,
filter: 0,
offset: this.groupMemberList.length,
count: 500,
})
.then(({
data
}) => {
this.groupMemberList = [...this.groupMemberList, ...data];
this.filterGroupMemberList = this.getFilterGroupMemberList();
this.loadState.hasMore = data.length === 500;
})
.finally(() => (this.loadState.loading = false));
},
longtap(member){
const _this = this;
if(this.isRightKick){
return ;
}
if(!this.isOwner&&!this.isAdmin){
return ;
}
if(this.storeCurrentMemberInGroup.roleLevel <= member.roleLevel ){
return ;
}
let itemList = [];
if(this.isOwner){
itemList.push(member.roleLevel == GroupMemberRole.Admin ? '取消管理员' : '设为管理员');
}
if(this.isOwner || this.isAdmin){
itemList.push(member.muteEndTime > 0 ? '取消禁言':'设置禁言');
itemList.push('踢出群聊');
}
uni.showActionSheet({
itemList:itemList,
async success({tapIndex}) {
const label = itemList[tapIndex];
if(['设为管理员','取消管理员'].includes(label)){
let roleId = label=='设为管理员' ? GroupMemberRole.Admin : GroupMemberRole.Normal;
await IMSDK.asyncApi(IMSDK.IMMethods.SetGroupMemberInfo,IMSDK.uuid(),{
roleLevel:roleId,
groupID:_this.groupID,
userID:member.userID
});
_this.groupMemberList = _this.groupMemberList.map((item)=>{
if(item.userID == member.userID){
item.roleLevel = roleId
}
return item;
});
return ;
}
if(['取消禁言','设置禁言'].includes(label)){
if(label == '取消禁言'){
_this.setMute(member.userID,0)
return ;
}
uni.showActionSheet({
itemList:['2小时','8小时','1天','3天','7天','15天','30天','1年'],
success(res){
const secs = [3600*2,3600*8,3600*24,3600*72,3600*24*7,3600*24*15,3600*24*30,3600*24*365];
_this.setMute(member.userID,secs[res.tapIndex])
}
});
return ;
}
if(label=="踢出群聊"){
await IMSDK.asyncApi(IMSDK.IMMethods.KickGroupMember,IMSDK.uuid(),{
groupID:_this.groupID,
reason:"",
userIDList:[member.userID]
});
_this.groupMemberList = _this.groupMemberList.filter((item)=>{
return item.userID != member.userID;
});
return ;
}
}
})
},
async setMute(userID,mutedSeconds){
const _this = this;
await IMSDK.asyncApi(IMSDK.IMMethods.ChangeGroupMemberMute,IMSDK.uuid(),{
mutedSeconds:mutedSeconds,
groupID:_this.groupID,
userID:userID
});
this.groupMemberList = this.groupMemberList.map((item)=>{
if(item.userID == userID){
item.muteEndTime = mutedSeconds===0 ? 0 : (new Date().getTime())+mutedSeconds*1000
}
return item;
});
},
showToast(message, complete = null) {
this.$refs.uToast.show({
message,
complete,
});
},
getEl(el) {
return new Promise((resolve) => {
const query = uni.createSelectorQuery().in(this);
query
.select(el)
.boundingClientRect((data) => {
// data
resolve(data);
})
.exec();
});
},
},
onBackPress(options) {
if (this.showCheck && this.isRightKick) {
this.showCheck = false;
this.type = GroupMemberListTypes.Preview;
return true;
}
return false;
},
};
</script>
<style lang="scss" scoped>
.group_members_container {
@include colBox(false);
height: 100vh;
overflow: hidden;
.group_members_container {
@include colBox(false);
height: 100vh;
overflow: hidden;
.search_bar_wrap {
height: 34px;
padding: 12px 22px;
}
.search_bar_wrap {
height: 34px;
padding: 12px 22px;
}
.at_all_btn {
font-weight: 500;
padding: 24rpx 44rpx;
}
.at_all_btn {
font-weight: 500;
padding: 24rpx 44rpx;
}
::v-deep.u-popup {
flex: none;
}
::v-deep.u-popup {
flex: none;
}
.member_list {
flex: 1;
}
}
</style>
.member_list {
flex: 1;
}
}
</style>
@@ -1,17 +1,29 @@
<template>
<view style="display: flex;flex-direction: column;width: 100vw;height: 100vh;">
<view>
<u-navbar
:autoBack="true"
title="群公告"
safeAreaInsetTop
placeholder
fixed
:autoBack="true"
>
<view class="u-nav-slot" slot="right" v-if="isOwner || isAdmin">
<u-button type="primary" size="mini" @click="save">确定</u-button>
<u-button type="primary" @click="save">保存</u-button>
</view>
</u-navbar>
<u--textarea :disabled="!isOwner && !isAdmin" count confirmType="done" focus autoHeight maxlength="-1" border="none" v-model="announcement"></u--textarea >
<u-parse v-if="!isOwner && !isAdmin" :content="announcement"></u-parse>
<u--textarea v-else
count
confirmType="done"
focus
autoHeight
height="500"
maxlength="1500"
border="none"
:adjustPosition="false"
class="textarea"
placeholder="暂无公告"
v-model="announcement"
>
</u--textarea>
</view>
</template>
@@ -38,14 +50,14 @@
}
},
onLoad() {
this.announcement = this.storeCurrentGroup.notification;
this.announcement = this.storeCurrentGroup.notification;
},
methods: {
back(){
uni.navigateBack();
},
save(){
if(!isOwner && !isAdmin){
if(!this.isOwner && !this.isAdmin){
return ;
}
IMSDK.asyncApi(IMSDK.IMMethods.SetGroupInfo, IMSDK.uuid(), {
@@ -62,6 +74,12 @@
}
</script>
<style>
<style scoped lang="scss">
.textarea{
}
.u-nav-slot{
.u-button{
height: 60rpx;
}
}
</style>
@@ -11,6 +11,7 @@
<view class="member_item" v-for="(member, index) in groupMemberList" :key="member.userID">
<my-avatar :src="member.faceURL" :desc="member.nickname" :key="member.userID" size="48" />
<view class="ower" v-if="member.roleLevel === 100">群主</view>
<view class="ower" v-if="member.roleLevel === 60">管理员</view>
<text class="member_item_name">{{ member.nickname }}</text>
</view>
<view class="member_item">
@@ -75,7 +76,7 @@
},
inviteMember() {
uni.navigateTo({
url: `/pages/common/contactChoose/choose?type=${ContactChooseTypes.Invite}&groupID=${this.groupID}`,
url: `/pages/common/contactChoose/choose?type=${ContactChooseTypes.Invite}&groupID=${this.groupID}&allowType=User`,
});
},
kickMember() {
+45 -28
View File
@@ -3,35 +3,26 @@
<u-navbar :autoBack="true" bgColor="#ECECEC" :title="'群聊设置('+storeCurrentGroup.memberCount+')'" safeAreaInsetTop placeholder fixed></u-navbar>
<view class="group_settings_content">
<view class="setting_row info_row" v-if="1==2">
<view class="group_avatar" @click="updateGroupAvatar">
<my-avatar :src="storeCurrentConversation.faceURL" :isGroup="true" size="46" />
<image v-if="isOwner" class="edit_icon" src="@/static/images/group_setting_edit.png" alt="" />
</view>
<view class="group_info">
<view class="group_info_name">
<text class="group_name">{{ storeCurrentConversation.showName }}({{storeCurrentGroup.memberCount}})</text>
<image v-if="isOwner || isAdmin" @click="toUpdateGroupName" style="width: 24rpx; height: 24rpx" src="@/static/images/group_edit.png" alt="" />
</view>
<text @click="copyGroupID" class="sub_title">{{storeCurrentConversation.groupID}}</text>
</view>
</view>
<group-member-row v-if="isJoinGroup" :isNomal="!isAdmin && !isOwner"
:groupID="storeCurrentConversation.groupID" :memberCount="storeCurrentGroup.memberCount"
:groupMemberList="groupMemberList" />
<uni-list>
<uni-list-item title="群聊名称" :rightText="storeCurrentConversation.showName" @click="editGroupName" :clickable="isOwner || isAdmin" :showArrow="isOwner || isAdmin"></uni-list-item>
<uni-list-item title="群图标" @click="updateGroupAvatar" clickable showArrow>
<template v-slot:footer>
<my-avatar :src="storeCurrentConversation.faceURL" :isGroup="true" size="46" />
</template>
</uni-list-item>
<uni-list-item title="群名称" :rightText="storeCurrentConversation.showName" @click="editGroupName" :clickable="isOwner || isAdmin" :showArrow="isOwner || isAdmin"></uni-list-item>
<uni-list-item title="群公告" to="/pages/conversation/groupSettings/announcement" clickable showArrow></uni-list-item>
<uni-list-item title="群二维码" :to="'/pages/common/userOrGroupQrCode?groupID='+storeCurrentConversation.groupID" clickable showArrow></uni-list-item>
<uni-list-item title="群二维码" :to="getGroupQrcdeUrl" clickable showArrow></uni-list-item>
<uni-list-item v-if="isOwner || isAdmin" title="群管理" to="/pages/conversation/groupManage/index" clickable showArrow></uni-list-item>
<uni-list-item v-if="1==2" title="备注" :rightText="storeCurrentConversation.showName" @click="editGroupName" :clickable="isOwner || isAdmin" :showArrow="isOwner || isAdmin"></uni-list-item>
</uni-list>
<u-gap></u-gap>
<uni-list>
<uni-list-item title="查找聊天内容" to=""></uni-list-item>
<uni-list-item title="群相册" :to="'/pages/conversation/groupAlbum/groupAlbum?groupID='+storeCurrentConversation.groupID" showArrow></uni-list-item>
<uni-list-item title="查找聊天内容" :to="'/pages/common/search/index?type=conversation&conversationID='+storeCurrentConversation.conversationID" showArrow></uni-list-item>
</uni-list>
<u-gap></u-gap>
<uni-list>
@@ -56,24 +47,21 @@
</view>
<u-toast ref="uToast"></u-toast>
<c-cut-avatar ref="cutAvatar" @save="saveAvatar" />
</view>
</template>
<script>
import {mapGetters} from "vuex";
import {GroupMemberListTypes} from "@/constant";
import IMSDK, {
GroupMemberRole,
GroupStatus,
GroupVerificationType,
IMMethods,
MessageReceiveOptType,
} from "openim-uniapp-polyfill";
import IMSDK, {GroupMemberRole,GroupStatus,GroupVerificationType,IMMethods,MessageReceiveOptType,} from "openim-uniapp-polyfill";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import MyAvatar from "@/components/MyAvatar/index.vue";
import SettingItem from "@/components/SettingItem/index.vue";
import GroupMemberRow from "./components/GroupMemberRow.vue";
import {getPurePath} from "@/util/common";
import util from "@/util/index.js"
import {upload} from "@/api/login.js";
const ConfirmTypes = {
Dismiss: "Dismiss",
@@ -100,7 +88,7 @@
};
},
onShow() {
console.log(this.storeCurrentConversation);
//console.log(this.storeCurrentConversation);
/*
this.$store.commit("conversation/SET_CURRENT_CONVERSATION", {
"conversationID": "sg_1793688611",
@@ -192,6 +180,15 @@
isAdmin() {
return this.storeCurrentMemberInGroup.roleLevel === GroupMemberRole.Admin;
},
getGroupQrcdeUrl(){
const info = util.aesencode({
code : this.storeCurrentConversation.groupID,
showName: `${this.storeCurrentConversation.showName}(${this.storeCurrentGroup.memberCount})`,
faceURL : this.storeCurrentConversation.faceURL,
type : "group",
});
return `/pages/common/userOrGroupQrCode?sourceInfo=${info}`;
},
getGroupVerStr() {
if (
this.storeCurrentGroup.needVerification ===
@@ -210,6 +207,8 @@
return this.storeCurrentGroup.status === GroupStatus.Muted;
},
},
onLoad() {
},
methods: {
editGroupName(){
if(this.isOwner || this.isAdmin){
@@ -242,9 +241,9 @@
if (!this.isAdmin && !this.isOwner) {
return;
}
const s = util.aesencode(this.storeCurrentGroup);
uni.navigateTo({
url: `/pages/conversation/updateGroupOrNickname/index?sourceInfo=${JSON.stringify(this.storeCurrentGroup,)}`,
url: `/pages/conversation/updateGroupOrNickname/index?sourceInfo=${s}`,
});
},
copyGroupID() {
@@ -276,6 +275,22 @@
console.log(e);
})
},
saveAvatar(e){
if (!this.isAdmin && !this.isOwner) {
return;
}
this.tempFilePath = e.path;
upload(e.path,{
'url':"/group/avatar",
savePath: "groupavatar",
groupID: this.storeCurrentConversation.groupID,
}).then((res) => {
console.log("上传成功",res);
//userStore.selfInfo.faceURL = res.data.faceURL;
}).catch((res1) => {
console.log("上传失败",res1);
});
},
updateGroupAvatar() {
if (!this.isAdmin && !this.isOwner) {
return;
@@ -288,6 +303,8 @@
tempFilePaths
}) => {
const path = tempFilePaths[0];
this.$refs.cutAvatar.enterEditor(path);
return ;
const nameIdx = path.lastIndexOf("/") + 1;
const typeIdx = path.lastIndexOf(".") + 1;
const fileName = path.slice(nameIdx);
@@ -1,92 +1,89 @@
<template>
<view class="page_container">
<custom-nav-bar :title="getTitle">
<view class="nav_right_action" slot="more">
<u-button type="primary" v-show="!updateLoading" @click="comfirmUpdate">保存</u-button>
<u-loading-icon v-show="updateLoading" />
</view>
</custom-nav-bar>
<view class="page_container">
<custom-nav-bar :title="getTitle">
<view class="nav_right_action" slot="more">
<u-button type="primary" v-show="!updateLoading" @click="comfirmUpdate">保存</u-button>
<u-loading-icon v-show="updateLoading" />
</view>
</custom-nav-bar>
<view class="content_row">
<u-input
v-model="content"
disabledColor="transparent"
maxlength="16"
placeholder="请输入内容"
clearable
>
</u-input>
</view>
</view>
<view class="content_row">
<u-input v-model="content" disabledColor="transparent" maxlength="16" placeholder="请输入内容" clearable>
</u-input>
</view>
</view>
</template>
<script>
import IMSDK from "openim-uniapp-polyfill";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import MyAvatar from "@/components/MyAvatar/index.vue";
export default {
components: {
CustomNavBar,
MyAvatar,
},
data() {
return {
sourceInfo: {},
content: "",
updateLoading: false,
};
},
computed: {
getTitle() {
return "修改群聊名称";
},
getSubTitle() {
return "修改群名称后,将在群内通知其他成员";
},
},
onLoad(options) {
const { sourceInfo } = options;
this.sourceInfo = JSON.parse(sourceInfo);
this.content = this.sourceInfo.groupName;
},
methods: {
comfirmUpdate() {
this.updateLoading = true;
IMSDK.asyncApi(IMSDK.IMMethods.SetGroupInfo, IMSDK.uuid(), {
groupID: this.sourceInfo.groupID,
groupName: this.content,
})
.then(() => {
uni.$u.toast("修改成功");
setTimeout(() => uni.navigateBack(), 250);
})
.catch(() => uni.$u.toast("修改失败"))
.finally(() => (this.updateLoading = false));
},
},
};
import IMSDK from "openim-uniapp-polyfill";
import CustomNavBar from "@/components/CustomNavBar/index.vue";
import MyAvatar from "@/components/MyAvatar/index.vue";
import util from "@/util/index.js"
export default {
components: {
CustomNavBar,
MyAvatar,
},
data() {
return {
sourceInfo: {},
content: "",
updateLoading: false,
};
},
computed: {
getTitle() {
return "修改群聊名称";
},
getSubTitle() {
return "修改群名称后,将在群内通知其他成员";
},
},
onLoad(options) {
const {
sourceInfo
} = options;
this.sourceInfo = util.aesdecode(sourceInfo);
this.content = this.sourceInfo.groupName;
},
methods: {
comfirmUpdate() {
this.updateLoading = true;
IMSDK.asyncApi(IMSDK.IMMethods.SetGroupInfo, IMSDK.uuid(), {
groupID: this.sourceInfo.groupID,
groupName: this.content,
})
.then(() => {
uni.$u.toast("修改成功");
setTimeout(() => uni.navigateBack(), 250);
})
.catch(() => uni.$u.toast("修改失败"))
.finally(() => (this.updateLoading = false));
},
},
};
</script>
<style lang="scss" scoped>
.page_container {
@include colBox(false);
height: 100vh;
.page_container {
@include colBox(false);
height: 100vh;
.nav_right_action {
margin-right: 36rpx;
}
.nav_right_action {
margin-right: 36rpx;
}
.content_row {
margin-top: 96rpx;
margin: 72rpx 44rpx 0;
.content_row {
margin-top: 96rpx;
margin: 72rpx 44rpx 0;
.u-input {
background-color: #e8eaef;
}
.u-input {
background-color: #e8eaef;
}
.u-button {
height: 60rpx;
}
}
}
</style>
.u-button {
height: 60rpx;
}
}
}
</style>
@@ -1,12 +1,17 @@
<template>
<view class="content">
<u-navbar title="选择背景图" :background="{ background: '#ffffff'}"
:border-bottom="false">
<view class="slot-wrap" slot="right">
<uni-nav-bar
left-icon="back"
@clickLeft="uni.$u.route({type:'back'})"
statusBar
backgroundColor="#ffffff"
:border="false"
title="选择背景图">
<template v-slot:footer>
<u-button :custom-style="customBtnStyle" size="mini" :disabled="submitFlag"
:type="submitFlag?'info ':'success'" @click="handleLink()">设置</u-button>
</view>
</u-navbar>
</template>
</uni-nav-bar>
<u-grid @click="clickGrid">
<u-grid-item v-for="(item, index) in bgList" :key="index" :index="index" :custom-style="item.isCheck?girdItemCustomStyle:{}">
<u-image :src="item.src" width="200rpx" height="200rpx" mode="aspectFill"></u-image>
@@ -46,7 +51,10 @@ export default {
handleLink(){
const item = this.bgList.find(it=>it.isCheck);
if(item){
this.$u.vuex('circleBgImg', item.src);
this.$store.commit('circle/SET_SETTINGS',{
...this.$store.storeCircleSettings,
bg:item.src
});
uni.navigateBack({
delta:2
})
@@ -0,0 +1,74 @@
<template>
<view>
<uni-nav-bar
left-icon="back"
@clickLeft="uni.$u.route({type:'back'})"
statusBar
backgroundColor="#ffffff"
title="选择背景图">
</uni-nav-bar>
<uni-list>
<uni-list-item disabled="true" title="选择内置背景图" @click="linkToBuiltinBgImg" clickable showArrow></uni-list-item>
<uni-list-item title="通过手机选择" @click="chooseImg" clickable showArrow></uni-list-item>
</uni-list>
</view>
</template>
<script>
import {upload} from "@/api/login"
export default {
data() {
return {
titleStyle:{
marginLeft:"20rpx",
fontSize:"32rpx",
color:"#000000"
}
};
},
methods: {
chooseImg() {
let that=this;
uni.chooseImage({
count: 1, //9
sizeType: ['original', 'compressed'], //
sourceType: ['camera','album'], //
success: function(res) {
console.log("res",res);
let tempFilePaths = res.tempFilePaths;
that.myUpload(tempFilePaths[0]);
return;
}
});
return;
},
//
myUpload(filePath) {
let _this=this;
let obj = {
filePath:filePath,
'url':"/friendcircle/upload_bg",
savePath: "circle" //
}
console.log("通过手机选择",obj);
upload(filePath,obj).then((res1) => {
_this.$store.commit('circle/SET_SETTINGS',{
..._this.$store.storeCircleSettings,
bg:res1.data.url
});
uni.navigateBack();
}).catch((res1) => {
console.log("上传失败",res1);
});
},
linkToBuiltinBgImg() {
this.$u.route('/pages/tabbar/find/friend-circle/builtinBgImg');
},
},
};
</script>
<style></style>
@@ -6,7 +6,8 @@
<view class="content">
<view class="content-name" @tap="linkToBusinessCard(item.user_id)">{{ item.user.nickname || item.user.remark }}</view>
<view class="content-desc">
<u--text :lines="5" :text="item.body" />
<text class="u-line-5" selectable user-select>{{item.body}}</text>
<!-- <u-text :lines="5" :text="item.body" selectable /> -->
</view>
<!-- 图片,视频 -->
<view class="content-img" v-if="item.files!=null&&item.files.length>0">
@@ -14,12 +15,12 @@
<view v-if="item.files.length==1&&item.releaseType==1"
class="u-flex u-row-left u-col-center"
style="width:100%;min-height: 200rpx;">
<u-image width="280rpx" :src="cdn(item.files[0])" mode="widthFix" @tap="previewImg(0)">
<u-image width="280rpx" :src="cdn(item.files[0])" mode="aspectFill" @tap="previewImg(0)">
<u-loading-icon slot="loading"></u-loading-icon>
<view slot="error"
class="u-flex u-row-left u-col-center"
style="font-size: 24rpx;width: 200rpx;height: 100rpx;margin-top: -50rpx;">
<u-icon name="photo" size="100" label="加载失败" label-pos="bottom"></u-icon>
<u-icon name="photo" size="100" label="加载失败" label-pos="bottom"></u-icon>
</view>
</u-image>
</view>
@@ -38,14 +39,9 @@
<!-- 视频 -->
<view class="u-m-b-30 u-m-t-30 u-flex u-row-left u-col-center"
<view class="u-m-b-30 u-m-t-30 u-flex u-row-left u-col-center"
v-if="item.files.length > 0&&item.releaseType==2" @tap="previewImg(0, item)">
<view v-if="vuex_OSPlat=='ios'">
<u-image width="280rpx" :src="getVideoPoster(item.files[0])" mode="widthFix">
<u-loading-icon slot="loading"></u-loading-icon>
</u-image>
</view>
<view v-else class="u-flex u-row-center u-col-center"
<view class="u-flex u-row-center u-col-center"
style="border: 1rpx solid #36648b;width:280rpx;height:120rpx;border-radius: 16rpx;">
<u-icon name="play-circle" size="48" color="#36648b"
label="点击查看视频" label-pos="bottom"></u-icon>
@@ -54,16 +50,16 @@
</view>
<!-- 地点 -->
<view v-if="(item.address&&item.address.chooseFlag)==true" class="u-line-2 u-m-t-10 u-m-b-10" style="font-size: 30rpx;color: #36648b;">
<u-icon name="map" color="#36648b" size="30" :custom-style="{marginLeft:'-4rpx'}"></u-icon>
<text> {{ item.address.name}}</text>
<view v-if="(item.address&&item.address.chooseFlag)==true" @click="gotomap(item)" class="content-address">
<u-icon name="map" color="#36648b"></u-icon>
<u-text class="name" :lines="1" :text="item.address.name"></u-text>
</view>
<!-- 相对时间 点赞按钮等 -->
<view class="relavivetime" :id="`comment-${'null'}-${index}`">
<view class="time">
<view>{{ item.created_at }}</view>
<view @click="deleteCircle(item,index)" style="color:#36648b;margin-left: 20rpx;" v-if="item.user_id==selfInfo.userID">删除</view>
<view @click="deleteCircle(item,index)" style="color:#36648b;margin-left: 20rpx;" v-if="item.user_id==storeSelfInfo.userID">删除</view>
</view>
<view class="icon-box u-flex u-row-between u-col-center">
@@ -80,22 +76,29 @@
<!-- 点赞人 评论 -->
<view class="msg-box">
<view class="thumbinfo u-border-bottom" v-if="item.likes!=null&&item.likes.length">
<uni-icons size="30" type="heart" color="#36648b" class="u-m-r-10"></uni-icons>
<text class="thumbinfo-name" v-for="(userInfo, pindex) in item.likes" :index="pindex"
:key="pindex" @tap="linkToBusinessCard(userInfo.userId)">
{{ userInfo.nickame }}{{ pindex != item.likes.length - 1 ? '' : '' }}
<uni-icons size="24" type="heart" color="#36648b" class="u-m-r-10"></uni-icons>
<text class="thumbinfo-name" v-for="(ui, pindex) in item.likes" :index="pindex"
:key="pindex" @tap="linkToBusinessCard(ui.user_id)">
{{ ui.nickname }}{{ pindex != item.likes.length - 1 ? '' : '' }}
</text>
</view>
<view class="comment" v-if="item.comments!=null&&item.comments.length">
<view class="comment-box" v-for="(comment, commentIndex) in item.comments" :index="comment.id"
:key="comment.id" hover-class="comment-hover-class"
<view
class="comment-box"
v-for="(comment, commentIndex) in item.comments"
:index="comment.id"
:key="comment.id"
hover-class="comment-hover-class"
:id="`comment-${item.id}-${comment.id}`"
@tap="handleComment(comment, index)">
<text class="comment-box-name">
{{ comment.user.nickname }}
<text class="callback u-m-l-4 u-m-r-4">回复</text>
</text>
<text v-if="comment.reply_user_id" class="comment-box-name">{{ comment.user.nickname }}</text>
<text class="callback u-m-l-4 u-m-r-4" v-if="comment.reply_user_id">回复</text>
<text v-if="comment.reply_user_id" class="comment-box-name">
{{ comment.user.nickname }}
</text>
<text></text>
<text class="comment-box-content">{{ comment.body }}</text>
</view>
</view>
@@ -106,10 +109,10 @@
<script>
import MyAvatar from "@/components/MyAvatar/index.vue";
import videoPlayer from '@/components/videoPlayer.vue';
import util from "@/util/index.js";
import { mapGetters } from "vuex";
export default{
components:{videoPlayer ,MyAvatar},
components:{MyAvatar},
props:{
index:{
type:Number,
@@ -123,9 +126,7 @@
}
},
computed:{
selfInfo() {
return this.$store.getters.storeSelfInfo;
},
...mapGetters(["storeSelfInfo"]),
},
data(){
//console.log(this.item);
@@ -138,6 +139,9 @@
}
},
methods:{
log(v){
console.log(v)
},
clickThumb(item,index){
this.$emit('userEvent',{type:'clickThumb',item,index});
},
@@ -173,6 +177,12 @@
return "http://192.168.31.125:9090/we-chat/images/friendCircle/1715421601709.mp4";
//return videoSrc;
},
gotomap(item){
const addr = item.address;
uni.navigateTo({
url:`/pages/common/map?type=viewlocation&lng=${addr.longitude}&lat=${addr.latitude}&address=${addr.address}`
})
},
cdn:util.cdn
}
}
@@ -215,6 +225,9 @@
padding-top: 4rpx;
//line-height: 36rpx;
font-size: 32rpx;
uni-text{
}
}
&-img {
@@ -242,7 +255,18 @@
}
}
}
&-address{
font-size: 26rpx;
color: #36648b;
display: flex;
align-items: center;
margin-top: 10rpx;
.u-icon{
}
.name{
}
}
.msg-box {
width: 100%;
background-color: #FFF;
@@ -290,8 +314,8 @@
&-content {
word-break: break-all;
}
}
}
}
}
}
@@ -2,72 +2,71 @@
<view class="content" id="content">
<u-navbar
@leftClick="leftClick"
bgColor="transparent"
:bgColor="scrollTop>bannarHeight?'#f4f4f4':'transparent'"
title="朋友圈"
title-size="36"
leftIconColor="#fff"
:titleStyle ="{color:'#fff'}"
:leftIconColor="scrollTop>bannarHeight?'#333':'#fff'"
:titleStyle ="{color:scrollTop>bannarHeight?'#333':'#fff'}"
:title-bold="true"
:border-bottom="false">
<view slot="right" class="u-margin-right-20" @click="showTypeSheet"
@longpress="linkToRelease({releaseType:0})">
<u-icon name="camera" color="#fff" size="32"></u-icon>
<u-icon name="camera" :color="scrollTop>bannarHeight?'#333':'#fff'" size="32"></u-icon>
</view>
</u-navbar>
<scroll-view :scroll-x="false" :scroll-y="true" class="scrollView"
:scroll-with-animation="scrollWithAnimation" :scroll-top="scrollTop" @scroll="scrolling"
@scrolltolower="loadMore"
:style="'height:'+scrollViewHeight+'px'">
<!-- 我的朋友圈基本信息 -->
<view class="content-imgbox">
<image class="bgimg" v-if="settings.bg" :src="settings.bg" mode="scaleToFill" @tap="showSheet"></image>
<view class="bgimg" v-else @tap="showSheet"></view>
<MyAvatar class="headimg" :src="selfInfo.faceURL" :desc="selfInfo.nickname || selfInfo.remark"
:isGroup="Boolean(selfInfo.groupID)" size="66" @tap="linkToBusinessCard(selfInfo.userID)" />
<text class="nickname">{{ selfInfo.nickname || selfInfo.remark }}</text>
</view>
<!-- 个性签名 -->
<view class="signature">
<view class="">{{ selfInfo.bio }}</view>
</view>
<!-- 我的朋友圈基本信息 -->
<view class="content-imgbox" :class="{top:scrollTop>bannarHeight}">
<image class="bgimg" v-if="storeCircleSettings.bg" :src="cdn(storeCircleSettings.bg)" mode="scaleToFill" @tap="showSheet"></image>
<view class="bgimg" v-else @tap="showSheet"></view>
<view v-if="unreadCount>0" style="display: flex;justify-content: center;">
<view @click="clearUnReadCount()"
style="width: 300rpx;height:70rpx;line-height:70rpx;background-color: #333333;color: #ffffff;border-radius:10rpx;display: flex;align-items: center;">
<view style="width:80rpx;padding:0 10rpx;">
<MyAvatar :src="selfInfo.faceURL" class="headimg" desc=" " size="24"/>
</view>
<view style="flex:1;">
{{unreadCount}}条新信息
</view>
<MyAvatar class="headimg" :src="storeSelfInfo.faceURL" :desc="storeSelfInfo.nickname || storeSelfInfo.remark"
:isGroup="Boolean(storeSelfInfo.groupID)" size="66" @tap="linkToBusinessCard(storeSelfInfo.userID)" />
<text class="nickname">{{ storeSelfInfo.nickname || storeSelfInfo.remark }}</text>
</view>
<!-- 个性签名 -->
<view class="signature">
<u--text :text="storeSelfInfo.bio" :lines="4" size="13" color="#999"></u--text>
</view>
<view v-if="storeCircleUnreadCount>0" style="display: flex;justify-content: center;">
<view @click="clearUnReadCount()"
style="width: 300rpx;height:70rpx;line-height:70rpx;background-color: #333333;color: #ffffff;border-radius:10rpx;display: flex;align-items: center;">
<view style="width:80rpx;padding:0 10rpx;">
<MyAvatar :src="storeSelfInfo.faceURL" class="headimg" desc=" " size="24"/>
</view>
<view style="flex:1;">
{{storeCircleUnreadCount}}条新信息
</view>
</view>
<!-- 朋友圈列表 -->
<view class="content-circle">
<!-- circleData是vuex变量不在本页面定义 -->
<template v-if="circleData!=null&&circleData.length>0">
<template v-for="(item, index) in circleData" >
<CircleItem :key="index" :index="index" :item="item" @userEvent="onUserEvent"></CircleItem>
</template>
</view>
<!-- 朋友圈列表 -->
<view class="content-circle">
<!-- storeCircleData是vuex变量不在本页面定义 -->
<template v-if="storeCircleData!=null&&storeCircleData.length>0">
<template v-for="(item, index) in storeCircleData" >
<CircleItem :key="index" :index="index" :item="item" @userEvent="onUserEvent"></CircleItem>
</template>
<template v-else>
<view style="margin-top: 30%;">
<u-empty text="暂无动态,发一条试试吧~"></u-empty>
</view>
</template>
</view>
</scroll-view>
</template>
<template v-else>
<view style="margin-top: 30%;">
<u-empty text="暂无动态,发一条试试吧~"></u-empty>
</view>
</template>
</view>
<u-overlay :show="showInput" @click="showInput = false">
<view class="input-box" style="height: 100rpx;" @tap.stop>
<view class="input-box" :style="{
height: '100rpx',
bottom:keyboardHeight+'px'
}" @tap.stop>
<view class="input-box-flex">
<view class="input-box-flex-grow">
<input :adjust-position="false" type="text" class="content" id="input" v-model="content"
:confirm-type="'send'" :confirm-hold="true" placeholder-style="color:#DDD;" :cursor-spacing="6"
:placeholder="placeholder" :focus="true" @confirm="sendMsg" />
:placeholder="placeholder" :focus="true" @confirm="commitComment" />
</view>
<u-button class="btn" type="primary" size="mini" @tap.prevent.stop="sendMsg">发送</u-button>
<u-button class="btn" type="primary" size="mini" @tap.prevent.stop="commitComment">发送</u-button>
</view>
</view>
</u-overlay>
@@ -77,14 +76,12 @@
<videoPlayer :previewVideoFlag="previewVideoFlag" :previewVideoSrc="previewVideoSrc" @quitPlay="previewVideoFlag=false"></videoPlayer>
</view>
<!-- 删除朋友圈确认框 -->
<view v-if="delCircleObj.delCircleModelFlag==true">
<u-modal v-model="delCircleObj.delCircleModelFlag" :show-title="false"
:show-confirm-button="true" confirm-text="删除" confirm-color="#fa3534"
:show-cancel-button="true" cancel-text="取消" cancel-color="#000000"
content="删除该朋友圈?" :content-style="{color:'#000000',fontSize:'32rpx',fontWeight:'bold'}"
<u-modal :show="delCircleObj.delCircleModelFlag"
:showConfirmButton="true" confirmText="删除" confirmColor="#fa3534"
:showCancelButton="true" cancelText="取消" cancelColor="#000000"
content="删除该朋友圈?"
@confirm="confirmDelCircle()" @cancel="cancelDelCircle()">
</u-modal>
</view>
</view>
</template>
@@ -93,53 +90,31 @@
import {getFriendCircle} from "@/api/login.js"
import UserBase from "@/components/User.vue"
import MyAvatar from "@/components/MyAvatar/index.vue";
import util from "@/util/index.js";
import CircleItem from "./components/circleItem.vue"
import { mapGetters } from "vuex";
import util from "@/util";
export default {
name: 'firendCircle',
mixins:[UserBase],
components:{videoPlayer ,MyAvatar,CircleItem},
computed: {
selfInfo() {
return this.$store.getters.storeSelfInfo;
},
circleData() {
return this.$store.getters.storeCircleData;
},
unreadCount() {
return this.$store.getters.storeCircleUnreadCount;
},
last_unread_item() {
return this.$store.getters.storeCircleLastUnreadItem;
},
settings() {
return this.$store.getters.storeCircleSettings;
},
computed:{
...mapGetters(["storeSelfInfo",'storeCircleData','storeCircleUnreadCount','storeCircleTopUnreadItems','storeCircleSettings']),
},
data() {
return {
loadMoreFlag:true,//
platFrom:'',
girdItemCustomStyle:{
padding: '0',
margin:'0',
border:'1rpx solid #f2f6fc'
},
scrollViewHeight: 0,
bannarHeight:202,
scrollTop: 0,
currentTop: 0,
currentScroll: 0,
scrollWithAnimation: false,
content: '',
placeholder: '',
showInput: false,
keyboardHeight:0,
focus: false,
id: '', //id
commentInfo: {},
currentItem: {},
previewVideoFlag:false,
previewVideoSrc:'',
videoContext:null,
page:1,
limit:5,
//
@@ -152,37 +127,28 @@
},
//vuex
watch:{
circleData:function(val){
console.log("监听到朋友圈内容有变动",val.length);
storeCircleData:function(val){
//console.log("",val.length);
}
},
onReady() {
console.log(this.selfInfo);
let that = this;
uni.onKeyboardHeightChange(res => {
that.keyboardHeight = res.height;
if (res.height == 0) {
let windowHeight = this.$u.sys().windowHeight;
this.scrollViewHeight = windowHeight - 90;
this.showInput = false;
this.currentTop = 0;
that.showInput = false;
return;
} else {
this.currentTop = 999;
let windowHeight = this.$u.sys().windowHeight;
this.scrollViewHeight = (windowHeight - res.height) - 120;
}
});
},
onShow: function(option) {
let windowHeight = this.$u.sys().windowHeight;
this.scrollViewHeight = windowHeight - 90;
console.log("onshow",this.page);
//let windowHeight = this.$u.sys().windowHeight;
//this.scrollViewHeight = windowHeight - 90;
//console.log("onshow",this.page);
},
onLoad:function(){
let that=this;
this.platFrom= this.$u.os();
console.log("onload",this.platFrom);
let param={
page:1,
limit:this.limit,
@@ -192,11 +158,11 @@
this.getCircleDataList(param);
uni.$on("handleFriendCircle", function(friendCircleMessage) {
console.log("监听到朋友圈动态有更新",friendCircleMessage);
let id= friendCircleMessage.content.id;
let newPraise= friendCircleMessage.content.likes;
const index = that.circleData.findIndex(i => i.id ==id);
that.circleData[index].likes=JSON.parse(newPraise);
if(friendCircleMessage.content.is_liked&&friendCircleMessage.userId!=that.selfInfo.userID){
//let id= friendCircleMessage.content.id;
//let newPraise= friendCircleMessage.content.likes;
//const index = that.storeCircleData.findIndex(i => i.id ==id);
//that.storeCircleData[index].likes=JSON.parse(newPraise);
if(friendCircleMessage.content.is_liked&&friendCircleMessage.userId!=that.storeSelfInfo.userID){
that.$store.dispatch('circle/updateUnreadCount',1);
}
})
@@ -219,6 +185,7 @@
methods: {
goto:util.goto,
cdn:util.cdn,
clearUnReadCount(){
this.$store.dispatch('circle/updateUnreadCount',0-this.unreadCount);
},
@@ -229,16 +196,16 @@
},
//
scrolling: function(event) {
scrolling: function(scrollTop) {
//console.log("event",event);
let that = this;
if (that.showInput == true) {
return;
}
let scrollTop = event.detail.scrollTop;
setTimeout(function() {
that.currentScroll = scrollTop;
}, 300);
//let scrollTop = event.detail.scrollTop;
// setTimeout(function() {
// that.currentScroll = scrollTop;
// }, 300);
},
//
loadMore:function(){
@@ -267,7 +234,7 @@
itemList:['更换相册封面'],
success: function (res) {
if(res.tapIndex == 0){
_this.goto('/pages/workbench/friend-circle/chooseCircleBgImg');
_this.goto('/pages/find/friend-circle/chooseCircleBgImg');
}
},
fail: function (res) {
@@ -278,11 +245,13 @@
showTypeSheet() {
const _this = this;
uni.showActionSheet({
itemList:['选择照片','选择视频'],
itemList:['文字','照片','视频'],
success: function (res) {
//toChooseRelease
if(res.tapIndex<2){
if([1,2].includes(res.tapIndex)){
_this.toChooseRelease(res.tapIndex);
}else{
_this.linkToRelease({releaseType:0})
}
},
fail: function (res) {
@@ -295,28 +264,20 @@
clickThumb(item,index) {
let that=this;
let flag=true;
item.is_liked = !item.is_liked;
if (item.is_liked) {
item.likes.push({
userId: this.selfInfo.userID,
userName: this.selfInfo.nickname
});
} else {
const index = item.likes.findIndex(i => i.userId == this.selfInfo.userID);
item.likes.splice(index, 1);
flag=false;
}
console.log("当前动态下标",index);
console.log("点赞列表",item);
let param={
id:item.id,
likes:JSON.stringify(item.likes),
user_id: this.storeSelfInfo.userID,
nickname: this.storeSelfInfo.nickname,
avatar: this.storeSelfInfo.avatar,
is_liked:flag
};
this.$store.dispatch('circle/like',param).then(res=>{
console.log("点赞更新成功",res.data);
item.likes=JSON.parse(res.data.likes);
that.circleData[index]=item;
}).catch(e=>{
item.is_liked = !item.is_liked;
console.log("评论失败",e);
@@ -324,7 +285,7 @@
},
//
linkToBusinessCard(userId) {
if(userId==this.selfInfo.userID){
if(userId==this.storeSelfInfo.userID){
return;
}
this.goto('/pages/common/userCard?sourceID='+userId);
@@ -343,16 +304,9 @@
let param={
id:delCircleId
};
deleteCircle(param).then(res => {
if(res.code==200){
that.delCircleObj.delCircleModelFlag=false;
that.delCircleObj.tempDelCircleId="";
that.delCircleObj.tempDelIndex="";
let tempData=that.circleData;
tempData.splice(delIndex,1);
this.$u.vuex('circleData', tempData);
}
});
that.delCircleObj.delCircleModelFlag=false;
this.$store.dispatch('circle/deleteFriendCircleList',param);
},
cancelDelCircle:function(){
let that=this;
@@ -365,14 +319,13 @@
handleComment(comment, index) {
let that = this;
this.content = '';
that.currentItem = that.circleData[index];
that.currentTop = 0;
that.currentItem = that.storeCircleData[index];
this.id = that.currentItem.id;
this.commentInfo = comment || {};
this.placeholder = '评论:';
if (comment) {
//console.log("",comment);
if (comment.comment.user_id == this.selfInfo.userID) {
if (comment.comment.user_id == this.storeSelfInfo.userID) {
//
this.placeholder = `评论:`;
} else {
@@ -383,7 +336,7 @@
this.showInput = true;
},
//
sendMsg() {
commitComment() {
let that=this;
if (!that.$u.trim(that.content)) {
return;
@@ -420,7 +373,6 @@
//
closeInputModel() {
this.showInput = false;
this.currentTop = 0;
this.content = '';
this.id = '';
this.commentInfo = {};
@@ -432,7 +384,7 @@
let that = this;
let tempFilePaths = [];
//
if (index == 0) {
if (index == 1) {
uni.chooseImage({
count: 9, //9
sizeType: ['original', 'compressed'], //
@@ -447,9 +399,10 @@
return;
}
});
return ;
}
//
if (index == 1) {
if (index == 2) {
uni.chooseVideo({
sourceType: ['camera', 'album'],
maxDuration: 60,
@@ -465,13 +418,14 @@
return;
}
});
return ;
}
},
//
linkToRelease(params) {
console.log(params);
let url = "/pages/workbench/friend-circle/releaseFriendCircle?";
let url = "/pages/find/friend-circle/releaseFriendCircle?";
for(let key in params){
if(key!="tempFilePaths"){
url+=key+"="+params[key]+"&";
@@ -490,7 +444,7 @@
//return videoSrc;
},
leftClick(e){
this.goto('/pages/workbench/index/index',"2");
this.goto('/pages/find/index/index',"2");
},
onUserEvent(e){
switch(e.type){
@@ -500,6 +454,9 @@
case 'handleComment':
this.handleComment(e.comment,e.index);
break;
case 'deleteCircle':
this.deleteCircle(e.item,e.index);
break;
case 'linkToBusinessCard':
this.linkToBusinessCard(e.userID);
break;
@@ -507,6 +464,14 @@
},
cdn:util.cdn
},
onReachBottom() {
this.loadMore();
},
onPageScroll({scrollTop}) {
this.scrollTop = scrollTop;
//console.log(scrollTop)
//this.scrolling(scrollTop);
}
};
</script>
@@ -533,6 +498,9 @@
}
&-imgbox {
position: relative;
&.top{
background: #f4f4f4;
}
.bgimg {
width: 100%;
@@ -22,7 +22,7 @@
:adjust-position="true" :auto-blur="true" @linechange="inputLineChange" @input="inputing"
:confirm-hold="true" :show-confirm-bar="false"
:focus="inputFocusFlag" :disable-default-padding="true"
v-model="content" :cursor="content.length" :maxlength="-1" />
v-model="content" :cursor="content.length" :maxlength="1500" />
</scroll-view>
<!-- 文件选择区 -->
<view v-if="releaseType!=0" class="uploadBox">
@@ -49,29 +49,33 @@
</view>
<!-- 选项 -->
<view class="tips">
<u-cell-group>
<!-- :value="address.name" :value-style="customValueStyle" :label="address.address" -->
<u-cell bg-color="#ffffff"
:title="address.chooseFlag?(address.name):'所在位置'" :title-style="customTitleStyle"
:value="address.chooseFlag?(address.address):'请选择'" isLink
@click="toChooseLocation()">
<view slot="icon" class="u-flex u-row-center u-col-center">
<u-icon name="map" size="32" :color="address.chooseFlag?'#19be6b':'#606266'"></u-icon>
</view>
</u-cell>
<u-cell bg-color="#ffffff" title="提醒谁看" :title-style="customTitleStyle" @click="toRemind">
<view slot="icon" class="u-flex u-row-center u-col-center">
<u-icon name="/static/images/friendCircle/at.png" width="24" height="24" color="#606266"></u-icon>
</view>
</u-cell>
<u-cell bg-color="#ffffff" @click="toSetPromission()"
title="谁可以看" :title-style="customTitleStyle"
:value="'公开'" :value-style="customValueStyle">
<view slot="icon" class="u-flex u-row-center u-col-center">
<u-icon name="account" size="32" color="#606266"></u-icon>
</view>
</u-cell>
</u-cell-group>
<uni-list>
<uni-list-item
title="所在位置"
:rightText="address.chooseFlag?(address.name):'请选择'"
:show-extra-icon="true"
:extra-icon="{color: '#666',size: '22',type: 'location-filled'}"
clickable
showArrow
@click="toChooseLocation">
</uni-list-item>
<uni-list-item v-if="1==2"
title="提醒谁看"
:rightText="address.chooseFlag?(address.address):'请选择'"
clickable
showArrow
@click="toRemind">
</uni-list-item>
<uni-list-item
title="谁可以看"
rightText="公开"
:show-extra-icon="true"
:extra-icon="{color: '#666',size: '22',type: 'notification-filled'}"
clickable
showArrow
@click="toSetPromission">
</uni-list-item>
</uni-list>
</view>
</view>
@@ -273,21 +277,23 @@
//
toChooseLocation:function(){
let that=this;
uni.chooseLocation({
success: function (res) {
// console.log('' + res.name);
// console.log('' + res.address);
// console.log('' + res.latitude);
//console.log('' + res.longitude);
that.address=res;
that.address.chooseFlag=true;
//console.log("that.address",that.address);
},
fail:function(){
that.address={};
that.address.chooseFlag=false;
uni.navigateTo({
url:"/pages/common/map",
events:{
onConfirm(res) {
//_this.sendLocationMessage(res);
that.address={
address:res.address,
name:`${res.addressComponent.city}${res.addressComponent.town}`,
latitude:res.lat,
longitude:res.lng,
};
that.address.chooseFlag=true;
console.log(res);
}
}
});
})
return ;
},
//
toSetPromission:function(){
@@ -422,28 +428,16 @@
let that=this;
console.error('submitPublish');
uni.$u.http.post('/friendcircle/create',param).then(res => {
let newCircle=res;
if(newCircle.address!=null&&newCircle.address.length>0){
newCircle.address=JSON.parse(newCircle.address);
}else{
newCircle.address={"chooseFlag":false};
}
if(newCircle.fileList!=null&&newCircle.fileList.length>0){
newCircle.fileList=JSON.parse(newCircle.fileList);
}else{
newCircle.fileList=[];
}
if(newCircle.praise!=null){
newCircle.praise=JSON.parse(newCircle.praise);
}else{
newCircle.praise=[];
}
if(newCircle.comment!=null){
newCircle.comment=JSON.parse(newCircle.comment);
}
else{
newCircle.comment=[];
}
console.log("发布成功",res);
let newCircle=res.data;
newCircle['user'] = {
"id": this.$store.getters.storeSelfInfo.userID,
"nickname": this.$store.getters.storeSelfInfo.nickname,
"avatar": this.$store.getters.storeSelfInfo.faceURL,
};
newCircle['likes'] = [];
newCircle['comments'] = [];
newCircle['is_liked'] = false;
let circleDataList=[...this.circleData];
circleDataList.unshift(newCircle);
that.$store.commit('circle/SET_LIST',circleDataList);
+70
View File
@@ -0,0 +1,70 @@
<template>
<view class="workbench_page">
<uni-nav-bar
title="发现"
statusBar
fixed
background-color="transparent"></uni-nav-bar>
<uni-list>
<uni-list-item title="朋友圈"
thumb="/static/images/find/01.png"
:thumbSize="thumbSize"
showArrow
to="/pages/find/friend-circle/friend-circle">
<view slot="footer" v-if="storeCircleTopUnreadItems.length>0">
<view class="avatargroup">
<MyAvatar :src="item.user.avatar" shape="circle" size="32" v-for="(item,index) in storeCircleTopUnreadItems" :key="index"></MyAvatar>
<u-avatar :text="'+'+storeCircleUnreadCount" size="32" bg-color="#353432" shape="circle"></u-avatar>
</view>
</view>
</uni-list-item>
</uni-list>
<u-gap :height="10"></u-gap>
<uni-list>
<uni-list-item title="扫一扫" thumb="/static/images/find/03.png" :thumbSize="thumbSize" @click="scan" clickable showArrow></uni-list-item>
<uni-list-item v-if="1==2" title="摇一摇" thumb="/static/images/find/05.png" :thumbSize="thumbSize" to="/pages/find/shake/index" showArrow></uni-list-item>
<uni-list-item v-if="1==2" title="看一看" thumb="/static/images/find/06.png" :thumbSize="thumbSize" to="/pages/find/friend-circle/friend-circle" showArrow></uni-list-item>
<uni-list-item v-if="1==2" title="听一听" thumb="/static/images/find/06.png" :thumbSize="thumbSize" to="/pages/find/music/index" showArrow></uni-list-item>
<uni-list-item v-if="config.near_user_open == '1'" title="附近" thumb="/static/images/find/08.png" :thumbSize="thumbSize" to="/pages/find/near/near" showArrow></uni-list-item>
<uni-list-item v-if="1===2" title="购物" thumb="/static/images/find/09.png" :thumbSize="thumbSize" :to="'/pages/common/webview?url='+encodeURI('http://pinduoduo.com')" showArrow></uni-list-item>
</uni-list>
</view>
</template>
<script>
import { mapGetters } from "vuex";
import MyAvatar from "@/components/MyAvatar/index.vue";
import util from "@/util";
export default {
components:{MyAvatar},
data() {
return {
thumbSize:"base"
};
},
computed:{
...mapGetters(["config",'storeCircleUnreadCount','storeCircleTopUnreadItems']),
},
onShow: function() {
let unreadCount= 1;
this.$store.dispatch('circle/getFriendCircleInfo');
},
methods: {
scan:util.scan
}
};
</script>
<style lang="scss" scoped>
.workbench_page {
background-color: #ececec !important;
}
.avatargroup{
display: flex;
.u-avatar {
position: relative;
margin-left: -16px;
//border: 1px solid #333;
}
}
</style>
+130
View File
@@ -0,0 +1,130 @@
<template>
<view v-if="currentPlaySong != null" class="play-bar flex-v-center" @click="bindOnShowPlayViewPopup">
<view class="music-cover flex-v-h-center" :class="currentPlayer.playState ? 'spin-start':'spin-stop'">
<u-avatar :src="currentPlaySong.pic" mode="circle" size='70'></u-avatar>
</view>
<view style="width: 64%;">
<view class="music-info flex-v-center text-line-1">
<view class="music-name text-line-1">{{currentPlaySong.songName}}</view>
<view class="line">-</view>
<view class="music-singer text-line-1">{{currentPlaySong.artist}}</view>
</view>
<view class="u-p-l-20 u-p-r-20 u-flex u-row-between u-col-center">
<view style="width: 80%;">
<u-line-progress active-color="#19be6b"
:striped="true"
:height="8"
:striped-active="true"
:show-percent="false"
:percent="currentPlayer.progress"></u-line-progress>
</view>
<view class="u-font-24 u-tips-color">
<text> {{currentPlaySong.songTimeMinutes}}</text>
</view>
</view>
</view>
<view class="bars flex-v-center flex-space-between">
<view style="width: 64rpx;">
<view class="u-progress-content flex-v-h-center btn-active" @tap.stop="bindOnControllerPlay">
<u-icon :class="currentPlayer.playState ? 'play' : 'pause'"
:name="currentPlayer.playState ? 'pause-circle' : 'play-circle'" size="54"></u-icon>
</view>
</view>
<view class="tools-item btn-active" style="display: inline-block;" @tap.stop="bindOnShowPlayListPopup">
<u-icon name="music-list" custom-prefix="custom-icon" size="54"></u-icon>
</view>
</view>
</view>
<view v-else class="play-bar flex-v-center test-border u-tips-color">
<view style="text-align: center;width: 100%;">
<text>当前暂无播放歌曲记录</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
};
},
methods:{
bindOnControllerPlay(){
this.$emit("changePlayStatus");
},
bindOnShowPlayListPopup(){
this.$emit("showPlayList")
},
bindOnShowPlayViewPopup(){
this.$emit("showPlayView")
},
}
}
</script>
<style>
@import "@/static/music/music.css";
</style>
<style lang="scss" scoped>
.play-bar{
position: fixed;
bottom: 30px;
left: 0;
width: 100%;
height: 120rpx;
background-color: #ffffff;
padding: 0 40rpx;
z-index: 1000;
.music-cover{
width: 100rpx;
height: 100rpx;
background-image: url('@/static/music/disc-plus.png');
background-size: 100% 100%;
flex-shrink:0;
-webkit-transform: rotate(360deg);
animation: rotation 10s linear infinite;
-moz-animation: rotation 10s linear infinite;
-webkit-animation: rotation 10s linear infinite;
-o-animation: rotation 10s linear infinite;
}
@-webkit-keyframes rotation{
from {-webkit-transform: rotate(0deg);}
to {-webkit-transform: rotate(360deg);}
}
.spin-stop{
-webkit-animation-play-state:paused
}
.spin-start{
-webkit-animation-play-state:running
}
.music-info{
display: flex;
padding: 0 20rpx;
flex: 1;
.music-name{
max-width: 65%;
flex-shrink:0;
}
.line{
padding: 0 10rpx;
color: $uni-text-color-disable;
font-size: $uni-font-size-sm;
}
.music-singer{
max-width: 35%;
color: $uni-text-color-disable;
font-size: $uni-font-size-sm;
}
}
.bars{
width: 160rpx;
text-align: right;
height: 100%;
.u-progress-content{
width: 100%;
height: 100%;
padding-left: 4rpx;
}
}
}
</style>
+265
View File
@@ -0,0 +1,265 @@
<template>
<view>
<u-navbar :is-back="true" title="" :background="{ background: '#ffffff' }"
:border-bottom="false" z-index="1001">
<view class="slot-wrap u-font-32">
<view class="u-font-32" style="width: 100%;">
<u-tabs :list="tabList" active-color="#2ebe4b"
:is-scroll="false" :show-bar="true" :font-size="34"
:current="currentTab" :active-item-style="activeItemStyle"
@change="tabChange"></u-tabs>
</view>
</view>
</u-navbar>
<view>
<view class="u-p-l-40 u-p-r-40">
<view class="u-m-t-20 u-m-b-20">
<u-search placeholder="支持搜索歌手,歌名,专辑"
border-color="#eeeeee"
bg-color="#ffffff"
shape="square"
:clearabled="true"
:show-action="true"
:action-style="actionStyle"
:animation="false"
height="80"
@search="search"
@custom="search"
v-model="searchWord">
</u-search>
</view>
<scroll-view
:scroll-x="false" :scroll-y="true" :style="'height:'+(pageHeight)+'px'">
<u-row>
<u-col :span="12">
<view class="u-p-30 u-font-36" style="font-weight: bold;">
<text>热门男歌手</text>
</view>
</u-col>
<u-col :span="4" v-for="(item,index) in singerManList" :key="index">
<view class="u-m-b-30" @click="toSongList(item)">
<view class="u-flex u-row-center u-col-center">
<view>
<u-image width="100%" height="130rpx"
border-radius="16rpx"
mode="heightFix"
:src="item.avatar"></u-image>
</view>
</view>
<view style="text-align: center;margin-top:4rpx;font-size: 26rpx;color: #000000;">
<text>{{item.singerName}}</text>
</view>
</view>
</u-col>
</u-row>
<u-row>
<u-col :span="12">
<view class="u-p-30 u-font-36" style="font-weight: bold;">
<text>热门女歌手</text>
</view>
</u-col>
<u-col :span="4" v-for="(item,index) in singerWomenList" :key="index">
<view class="u-m-b-30" @click="toSongList(item)">
<view class="u-flex u-row-center u-col-center">
<view>
<u-image width="100%" height="130rpx"
border-radius="16rpx"
mode="heightFix"
:src="item.avatar"></u-image>
</view>
</view>
<view style="text-align: center;margin-top:4rpx;font-size: 26rpx;color: #000000;">
<text>{{item.singerName}}</text>
</view>
</view>
</u-col>
</u-row>
</scroll-view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
pageHeight:0,
tabList:[
{
name: '在线听'
},
{
name: '本地听'
},
],
currentTab:0,
activeItemStyle:{
fontSize:'36rpx',
},
actionStyle:{
backgroundColor:'#46be72',
color:'#ffffff',
height:'80rpx',
lineHeight:'80rpx',
position:'relative',
left:'0rpx',
textAlign:'center',
width:'140rpx',
borderRadius:'10rpx',
borderTopRightRadius:'10rpx',
borderBottomRightRadius:'10rpx'
},
searchWord:'',
singerManList:[
{
avatar:'https://y.qq.com/music/photo_new/T001R500x500M0000025NhlN2yWrP4.jpg?max_age=2592000',
singerName:'周杰伦'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R500x500M000001BLpXF2DyJe2.jpg?max_age=2592000',
singerName:'林俊杰'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R500x500M000002J4UUk29y8BY.jpg?max_age=2592000',
singerName:'薛之谦'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R500x500M000003Nz2So3XXYek.jpg?max_age=2592000',
singerName:'陈奕迅'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R500x500M000002azErJ0UcDN6.jpg?max_age=2592000',
singerName:'张杰'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R300x300M000003aQYLo2x8izP_4.jpg?max_age=2592000',
singerName:'刘德华'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R300x300M000004NMZuf2BLjg8_2.jpg?max_age=2592000',
singerName:'周传雄'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R500x500M000000aHmbL2aPXWH.jpg?max_age=2592000',
singerName:'李荣浩'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R500x500M000003fA5G40k6hKc.jpg?max_age=2592000',
singerName:'周深'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R500x500M000001IoTZp19YMDG.jpg?max_age=2592000',
singerName:'易烊千玺'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R500x500M000001z2JmX09LLgL.jpg?max_age=2592000',
singerName:'汪苏泷'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R500x500M000000CK5xN3yZDJt.jpg?max_age=2592000',
singerName:'许嵩'
}
],
singerWomenList:[
{
avatar:'https://y.qq.com/music/photo_new/T001R500x500M000001fNHEf1SFEFN.jpg?max_age=2592000',
singerName:'G.E.M.邓紫棋'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R500x500M0000003ZpE43ypssl.jpg?max_age=2592000',
singerName:'张碧晨'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R500x500M000000oCQfT3kdonw.jpg?max_age=2592000',
singerName:'黄霄雲'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R500x500M000002lKaDq2lLLtk.jpg?max_age=2592000',
singerName:'蔡健雅'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R500x500M000002azErJ0UcDN6.jpg?max_age=2592000',
singerName:'苏星婕'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R500x500M000003iPzNg35cWzp.jpg?max_age=2592000',
singerName:'程响'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R300x300M000000aw4WC2EQYTv_5.jpg?max_age=2592000',
singerName:'张靓颖'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R300x300M000000GGDys0yA0Nk_2.jpg?max_age=2592000',
singerName:'梁静茹'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R500x500M000001pWERg3vFgg8.jpg?max_age=2592000',
singerName:'孙燕姿'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R500x500M000000GDDuQ3sGQiT.jpg?max_age=2592000',
singerName:'王菲'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R300x300M000003jJGvv3C82KZ_5.jpg?max_age=2592000',
singerName:'刘若英'
},
{
avatar:'https://y.qq.com/music/photo_new/T001R300x300M000002raUWw3PXdkT_5.jpg?max_age=2592000',
singerName:'张韶涵'
}
]
};
},
created:function(){
let pageHeight= this.$u.sys().windowHeight*0.82;
this.pageHeight=pageHeight;
},
methods: {
tabChange:function(index){
this.currentTab = index;
},
search:function(value){
console.log("搜索关键字",value);
let that=this;
if(value.length>0){
this.$u.route({
url:'pages/tabbar/find/music/song-list',
params:{
keyWords:value
}
})
}
},
toSongList:function(singer){
let that=this;
this.$u.route({
url:'pages/tabbar/find/music/song-list',
params:{
keyWords:singer.singerName
}
})
}
},
}
</script>
<style lang="scss">
.slot-wrap {
display: flex;
align-items: center;
/* 如果您想让slot内容占满整个导航栏的宽度 */
flex: 1;
/* 如果您想让slot内容与导航栏左右有空隙 */
padding: 0 30rpx;
}
.status_bar {
height: var(--status-bar-height);
width: 100%;
}
</style>
+218
View File
@@ -0,0 +1,218 @@
<template>
<view>
<u-navbar :is-back="true" :title="navbarTitle" :background="{ background: '#ffffff' }"
:title-bold="true" title-size="34"
:border-bottom="true" z-index="1001">
</u-navbar>
<view>
<view class="u-p-30 u-flex u-row-between u-col-center">
<view style="font-size: 36rpx;font-weight: bold;">
<text>列表</text>
<text>({{songList.length}})</text>
</view>
<view class="u-flex u-row-left u-col-center">
<view class="u-m-r-30">
<u-icon name="play-circle-fill" size="42" color="#2979ff" @click="playAll()"
label="播放全部" label-size="32"></u-icon>
</view>
<view class="u-m-l-30">
<u-icon name="heart-fill" size="42" color="#fa3534" @click="addToLocal()"
label="添加本地" label-size="32"></u-icon>
</view>
</view>
</view>
<scroll-view :scroll-x="false" :scroll-y="true" :style="'height:'+(pageHeight-50)+'px'" @scrolltolower="loadMore">
<view class="u-flex u-row-between u-col-center u-p-30 u-border-bottom"
v-for="(song,index) in songList" :key="index">
<view class="u-flex u-row-left u-col-center">
<view class="u-m-r-30">
<u-image :src="song.pic" width="100rpx" height="100rpx" border-radius="12rpx"></u-image>
</view>
<view>
<view class="u-font-34 u-m-b-10 u-line-1" style="color: #000000;width: 400rpx;">{{escape2Html(song.name)}}</view>
<view class="u-font-28 u-m-t-10 u-flex u-row-left u-col-center u-line-3" style="color: #808288;width: 400rpx;">
{{escape2Html(song.artist)}}-{{escape2Html(song.album)}}
</view>
</view>
</view>
<view class="u-flex u-row-around u-col-center">
<view>
<u-icon v-if="currentPlaySong&&currentPlaySong.id==song.rid&&currentPlayer.playState" name="pause-circle" size="50" color="#797979"></u-icon>
<u-icon v-else @click="toPlaySong(song)" name="play-circle" size="50" color="#797979"></u-icon>
</view>
<view class="u-m-l-30">
<u-icon @click="download(song)" name="download" size="50" color="#797979"></u-icon>
</view>
</view>
</view>
</scroll-view>
</view>
<view>
<play-bar @changePlayStatus="changePlayStatus" @showPlayView='bindOnPlayBarChange' @showPlayList='bindOnShowPlayListPopup'></play-bar>
</view>
</view>
</template>
<script>
import playBar from './components/play-bar.vue';
import playerUtil from '@/util/music/music-player.js';
export default {
components:{
playBar
},
data() {
return {
navbarTitle:"歌曲列表",
pageHeight:0,
pageNum:1,
pageSize:30,
searchWord:"",
total:0,
songList:[],
loadMoreFlag:false
};
},
watch:{
currentPlaySong:function(newSong){
let that=this;
console.log("====监听到播放歌曲发生变化======");
if(newSong!=null&&newSong.id){
playerUtil.initPlayer(that);
}
}
},
created:function(){
let pageHeight= this.$u.sys().windowHeight*0.79;
this.pageHeight=pageHeight;
},
onLoad:function(option){
let that=this;
console.log("搜索词",option);
let keyWords= option.keyWords;
if(keyWords){
this.navbarTitle=keyWords;
this.searchWord=keyWords;
};
this.getSongList();
that.$u.vuex("currentPlayer.canPlay",false);
//true false
if(playerUtil.bgAudioManager.paused==true){
playerUtil.initPlayer(that);
}
},
methods:{
getSongList:function(){
let that=this;
let param={
pageNum:that.pageNum,
pageSize:that.pageSize,
keyWords:encodeURI(this.searchWord)
};
console.log("搜索歌曲列表",param);
this.$u.api.music.searchMusicList(param).then(res => {
//console.log("",res);
if(res.code==200){
let result= res.data;
if(result!=null&&result!=undefined){
let reqData= result.data;
this.total= reqData.total;
let list=reqData.list;
if(that.loadMoreFlag==true){
this.songList=this.songList.concat(list);
return;
}else{
this.songList=list;
}
}
}
});
},
loadMore:function(){
let that=this;
if(that.songList.length>=that.total){
that.globalUtil.utilAlert("暂无更多");
that.loadMoreFlag=false;
return;
}
that.pageNum++;
that.loadMoreFlag=true;
console.log("加载更多");
that.getSongList();
},
toPlaySong:function(song){
//console.log("",song);
//this.$u.vuex("currentPlaySong",song);
let param={
musicId:song.rid
};
this.$u.api.music.getSongInfoAndLrc(param).then(res => {
if(res.code==200){
let baseInfoResult= res.data;
let lrclist= baseInfoResult.data.lrclist;
let songInfo= baseInfoResult.data.songinfo;
songInfo.lrclist=lrclist;
this.$u.api.music.getSongSrc(param).then(res => {
if(res.code==200){
let songSrcResult= res.data;
let songSrc=songSrcResult.data.url;
songInfo.playSrc=songSrc;
console.log("获取歌曲详细信息结果",songInfo);
this.$u.vuex("currentPlaySong",songInfo);
}
})
}
})
//this.$forceUpdate();
},
changePlayStatus:function(){
let that=this;
console.log("改变状态")
playerUtil.bindOnControllerPlay(that);
},
bindOnPlayBarChange(){
//this.$refs.playViewPopup.show();
},
bindOnShowPlayListPopup(){
//this.$refs.playListPopup.show();
},
playAll:function(){
},
addToLocal:function(){
let that=this;
},
download:function(song){
let that=this;
let param={
musicId:song.rid,
songName:that.escape2Html(song.name),
saveDir:that.escape2Html(song.artist),
};
console.log("下载参数",param);
this.$u.api.music.downLoadSong(param).then(res => {
console.log("下载结果",res);
if(res.code==200){
that.globalUtil.utilAlert("下载成功");
let playSrc= res.data.url;
}else{
that.globalUtil.utilAlert("下载失败");
}
})
},
escape2Html:function(str) {
var arrEntities={'lt':'<','gt':'>','nbsp':' ','amp':'&','quot':'"','$':','};
return str.replace(/&(lt|gt|nbsp|amp|quot);/ig,function(all,t){
return arrEntities[t];
});
}
}
}
</script>
<style lang="scss">
</style>
+249
View File
@@ -0,0 +1,249 @@
<template>
<view>
<u-navbar :is-back="true" back-icon-color="#333333"
title="通过朋友验证" :title-bold="true" :title-size="34"
:background="{ background: '#ffffff' }"
title-color="#404133" :border-bottom="false">
</u-navbar>
<view class="">
<scroll-view :scroll-x="false" :scroll-y="true" class="u-p-t-10 u-p-b-30"
:style="'width:100%;height:'+scrollviewHeight+'px'">
<view class="u-p-l-40 u-p-r-40">
<view class="u-p-l-20 u-title-color u-font-28">
<text>设置备注</text>
</view>
<view class="inputBox">
<u-input :custom-style="customInputStyle"
v-model="form.nickName" type="text"
:border="false" :height="60" :clearable="false"/>
</view>
<view class="u-title-color u-p-l-30 u-m-t-10">
<text>
<text style="font-size: 28rpx;">"{{form.userRemark}}"</text>
<text style="color: #36648b;margin-left: 20rpx;">选词填入</text>
</text>
</view>
</view>
<view class="u-m-t-30 u-p-l-40 u-p-r-40">
<view class="u-p-l-20 u-title-color u-font-28">
<text>添加标签与描述</text>
</view>
<view style="background-color: #f0f0f0;border-radius: 16rpx;margin-top: 20rpx;font-size: 32rpx;">
<view class="u-flex u-row-between u-col-center" style="padding:16rpx 26rpx;">
<view class="u-p-20 label-font">
<text>标签</text>
</view>
<view class="u-p-20">
<u-icon name="arrow-right" color="#909399" :size="32"></u-icon>
</view>
</view>
<u-gap :height="1" bg-color="#e5e8ec"></u-gap>
<view class="u-flex u-row-between u-col-center" style="padding:16rpx 26rpx;">
<view class="u-p-20 label-font">
<text>描述</text>
</view>
<view class="u-p-20">
<u-icon name="arrow-right" color="#909399" :size="32"></u-icon>
</view>
</view>
</view>
</view>
<view class="u-m-t-30 u-p-l-40 u-p-r-40">
<view class="u-p-l-20 u-title-color u-font-28">
<text>设置朋友权限</text>
</view>
<view style="background-color: #f0f0f0;border-radius: 16rpx;margin-top: 20rpx;font-size: 32rpx;">
<view class="u-flex u-row-between u-col-center"
@click="changePower(0)"
style="padding:16rpx 26rpx;">
<view class="u-p-20 label-font">
<text>聊天朋友圈微信运动等</text>
</view>
<view class="u-p-20" v-show="form.friendPower==0">
<u-icon name="checkbox-mark" color="#19be6b" :size="32"></u-icon>
</view>
</view>
<u-gap :height="1" bg-color="#e5e8ec"></u-gap>
<view class="u-flex u-row-between u-col-center"
@click="changePower(1)"
style="padding:16rpx 26rpx;">
<view class="u-p-20 label-font">
<text>仅聊天</text>
</view>
<view class="u-p-20" v-show="form.friendPower==1">
<u-icon name="checkbox-mark" color="#19be6b" :size="32"></u-icon>
</view>
</view>
</view>
</view>
<view class="u-m-t-30 u-p-l-40 u-p-r-40" v-if="form.friendPower==0">
<view class="u-p-l-20 u-title-color u-font-28">
<text>朋友圈和状态</text>
</view>
<view style="background-color: #f0f0f0;border-radius: 16rpx;margin-top: 20rpx;font-size: 32rpx;">
<view class="u-flex u-row-between u-col-center" style="padding:16rpx 26rpx;">
<view class="u-p-20 label-font">
<text>不让他()</text>
</view>
<view class="u-p-20">
<u-switch v-model="form.forbidSelf" :size="40" active-color="#19be6b" inactive-color="#eee"></u-switch>
</view>
</view>
<u-gap :height="1" bg-color="#e5e8ec"></u-gap>
<view class="u-flex u-row-between u-col-center" style="padding:16rpx 26rpx;">
<view class="u-p-20 label-font">
<text>不看他()</text>
</view>
<view class="u-p-20">
<u-switch v-model="form.forbidHis" :size="40" active-color="#19be6b" inactive-color="#eee"></u-switch>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<view :style="'height:'+footerHeight+'px'"
class="u-flex u-row-center u-col-top footerbox">
<u-button type="success" :custom-style="customBtnStyle" @click="submitApply">完成</u-button>
</view>
<u-popup v-model="loadingShow"
:mask-close-able="false" :mask="false"
mode="center" border-radius="20" width="250rpx" height="250rpx">
<view class="u-flex u-row-center u-col-center"
style="flex-direction: column; text-align: center;
background-color: #000000;color: #ffffff;height: 250rpx;width: 250rpx;">
<view v-if="loadingStep==1">
<view>
<!-- <u-circle-progress :percent="100" bg-color="none"
inactive-color="#909399"
active-color="#ffffff"
:border-width="6"
:duration="1000" :width="100">
</u-circle-progress> -->
<u-loading mode="circle" :size="80" color="#909399"></u-loading>
</view>
<view class="u-m-t-20 u-font-32" style="font-weight: 420rpx;">正在处理...</view>
</view>
<view v-if="loadingStep==2">
<view>
<u-icon name="checkmark" color="#ffffff" :size="80"></u-icon>
</view>
<view class="u-m-t-20 u-font-32" style="font-weight: 420rpx;">已通过验证</view>
</view>
</view>
</u-popup>
</view>
</template>
<script>
export default {
data() {
return {
scrollviewHeight:0,
footerHeight:0,
customInputStyle:{
fontSize:'32rpx',
color:'#909399',
paddingLeft:'10rpx'
},
form:{
id:'3',
avatar:'/static/image/default/default-user/3.jpg',
nickName:'王工',
signature:'',
userRemark:'你好,美女',
address:'浙江 杭州',
sex:0,
friendPower:0,
forbidSelf:false,
forbidHis:false,
},
customBtnStyle:{
padding:'20rpx 40rpx',
width:'300rpx'
},
loadingShow:false,
loadingStep:1,
};
},
created:function(){
let pageHeight= this.$u.sys().windowHeight;
this.scrollviewHeight=pageHeight*0.783;
this.footerHeight=pageHeight*0.09;
},
onLoad:function(option){
let that=this;
/* let param=JSON.parse(decodeURIComponent(option.personInfo));
this.form=param; */
this.form=this.yyy_current_user;
},
methods:{
friendNameFocus:function(){
let that=this;
},
changePower:function(status){
let that=this;
},
submitApply:function(){
let that=this;
that.loadingStep=1;
that.loadingShow=true;
setTimeout(function(){
that.loadingStep=2;
setTimeout(function(){
that.loadingShow=false;
uni.navigateTo({
animationType:'slide-in-left',
url:'/pages/find/shake/friend_home?personInfo='+encodeURIComponent(JSON.stringify(that.form))
});
/* that.$u.route({
url:'/pages/find/shake/friend_home',
animationType:'slide-in-left',
params:{
personInfo:encodeURIComponent(JSON.stringify(that.form))
},
}) */
},400)
},800);
}
}
}
</script>
<style lang="scss" scoped>
.inputBox{
background-color: #f0f0f0;
border-radius: 16rpx;
margin-top: 20rpx;
padding:16rpx 26rpx;
font-size: 32rpx;
}
.footerbox{
position: absolute;
bottom: 0px;
width: 100%;
background-color: #ffffff;
//opacity: 0.7;
}
.label-font{
font-size: 34rpx;
color: #000000;
font-weight: 420;
}
.u-title-color{
color: #606266 !important;
font-size: 30rpx;
}
</style>
<style lang="scss">
page{
background-color: #ffffff;
}
</style>
@@ -0,0 +1,554 @@
<template>
<view class="message-item-container"
:class="checkBoxFlag?'u-flex u-row-left u-col-center':''">
<view v-if="checkBoxFlag">
<template v-if="item.messageType!=0">
<u-checkbox :size="46" v-model="item.checked"
shape="circle" active-color="#19be6b" @click.native="checkChange(item)"></u-checkbox>
</template>
</view>
<view class="u-p-b-20 u-p-t-20" style="width: 100%;">
<!-- 系统信息-->
<view v-if="item.messageType==0">
<view v-if="item.showTimeFlag" style="text-align: center;color:#aaaaaa;font-size: 28rpx;margin-top: 20rpx;">
<text v-if="item.formatTimeStr&&item.formatTimeStr.length>0">
{{item.formatTimeStr}}
</text>
<text v-else>
{{item.createTime.substring(10,16)}}
</text>
</view>
<view style="text-align: center;color:#aaaaaa;font-size: 30rpx;margin-top: 20rpx;">
{{parseSYSContent(item)}}
</view>
</view>
<view v-else>
<view class="chat-time">
<text v-if="item.showTimeFlag">
<text v-if="item.formatTimeStr&&item.formatTimeStr.length>0">
{{item.formatTimeStr}}
</text>
<text v-else>
{{item.createTime.length>10?item.createTime.substring(10,16):item.createTime}}
</text>
</text>
</view>
<view :class="{'chat-container':true,'chat-location-me':item.meFlag}">
<view class="chat-icon-container u-flex u-row-center u-col-center"
@click="toYYYPerson(item)">
<view hover-class="my-hover-class"
class="u-flex u-row-center u-col-center">
<u-image :class="{'chat-icon':true,'chat-icon-me':item.meFlag}"
:src="item.userAvatar" width="80rpx" height="80rpx"
mode="aspectFill">
<view slot="error"
class="u-flex u-row-center u-col-center u-p-t-10"
style="width: 80rpx;height: 80rpx;border-radius: 12rpx;">
<view>
<image src="/static/image/default/default-user/default-user.png"
style="width: 50rpx;height:50rpx;"></image>
</view>
</view>
</u-image>
</view>
</view>
<view class="chat-content-container u-p-t-10">
<!-- 文本信息 -->
<view v-if="item.contentType == messageApi.CONTENT_TYPE.TEXT_CONTENT_TYPE">
<zb-tooltip placement="top" :visible.sync="toolTipFlag" color="#4c4c4c">
<view slot="content" style="width: 550rpx;"
class="u-flex u-flex-wrap u-row-left u-col-center">
<view v-for="(item,index) in toolTipData" :key="index"
style="text-align: center;padding: 20rpx 30rpx;">
<view class="u-flex u-row-center u-col-center" @click="handleMessage(item)">
<view>
<u-image :src="item.icon" :width="36" :height="36"></u-image>
</view>
</view>
<view style="font-size:26rpx;margin-top: 6rpx;">
<text>{{item.title}}</text>
</view>
</view>
</view>
<view @longpress="toolTipFlag=!toolTipFlag"
:class="{'chat-text-container':true,'chat-text-container-me u-text-reserve':item.meFlag}">
<u-parse :html="parseTextContent(item)"
:tag-style="tagStyle">
</u-parse>
</view>
</zb-tooltip>
</view>
<!-- 图片信息 -->
<view v-if="item.contentType == messageApi.CONTENT_TYPE.IMG_CONTENT_TYPE">
<view class="content contentType3" @tap="showPic(item)">
<u-image :width="item.content.width*2" :height="item.content.height*2"
:lazy-load="true" mode="widthFix" :src="parseImageSrc(item)">
<u-loading slot="loading"></u-loading>
</u-image>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
var previewSrcArr = [];
export default {
name: "message-item",
props: {
item: {
type: Object,
default: function() {
return {
"id": "",
"chatGroupId": "",
"userId": 0,
"meFlag": true,
"userName": '',
"userAvatar": "",
'messageType': "",
"contentType": "",
"content": {},
"timestamp": "",
"createTime": "",
"platFrom": this.messageApi.msgPlat,
}
}
},
checkBoxFlag:{
type:Boolean,
default:false
}
},
data() {
return {
toolTipFlag: false,
toolTipData: [{
id: 1,
icon: '/static/image/wx/handle-msg/copy.jpg',
title: '复制',
disabled: false
},
{
id: 2,
icon: '/static/image/wx/handle-msg/zhuanfa.jpg',
title: '转发', //
disabled: false
},
{
id: 4,
icon: '/static/image/wx/handle-msg/shoucang.jpg',
title: '收藏',
disabled: false
},
{
id: 5,
icon: '/static/image/wx/handle-msg/shanchu.jpg',
title: '删除',
disabled: false
},
{
id: 6,
icon: '/static/image/wx/handle-msg/duoxuan.jpg',
title: '多选',
disabled: false
},
{
id: 7,
icon: '/static/image/wx/handle-msg/yinyong.jpg',
title: '引用',
disabled: false
},
{
id: 8,
icon: '/static/image/wx/handle-msg/tixing.jpg',
title: '提醒',
disabled: false
},
{
id: 9,
icon: '/static/image/wx/handle-msg/souyisou.jpg',
title: '搜一搜',
disabled: false
},
],
tagStyle:{
img:'width:22px;height:22px;position:relative;top:10rpx;',
span:'margin:0px;padding:0px;font-size:30rpx',
},
previewVideoFlag:false, //
previewVideoSrc:'',
redbagPopFlag:false,
maskCustomStyle:{
background: 'rgba(255, 255, 255, 0.7)'
},
currentRedBagInfo:null,
};
},
methods: {
parseSYSContent:function(item){
let that=this;
let contentType = item.contentType;
let contentObj=item.content;
if (contentType == 1) {
let msgStr = contentObj.text;
console.log("transText",msgStr);
return msgStr;
}
},
//
parseTextContent: function(item) {
let that=this;
let contentType = item.contentType;
let contentObj=item.content;
let msgStr = contentObj.text;
let sysType = contentObj.contentType; //
let optionType = contentObj.optionType;
let transText = this.transform(msgStr,'','',contentObj.linkName);
return transText;
},
//
showPic:function(item){
let url= item.content.fullPath;
let target= item.content.fileSaveTarget;
if(target=="local"){
url= this.$u.api.multipartAddress.getFileByPath+url;
}
uni.previewImage({
urls: previewSrcArr.length > 0 ? previewSrcArr : [url],
current: url,
loop: false,
});
},
parseImageSrc:function(item){
let that=this;
let msgId= item.id;
let localSrc= uni.getStorageSync("image"+msgId);
if(localSrc){
return localSrc;
}
let contentObj=item.content;
let url= contentObj.fullPath;
let target= contentObj.fileSaveTarget;
if(target=="local"){
let fileName= contentObj.fileName;
url=this.$u.api.multipartAddress.showFile+fileName;
}
that.setImageLocalSrc(url,msgId);
return url;
},
parseImgSize: function(item, type) {
let contentObj=item.content;
let height = contentObj.height;
let width = contentObj.width;
if (type == "h") {
if (height == null || height == undefined) {
return "auto";
} else {
return height * 2;
}
} else {
if (width == null || width == undefined) {
return 300;
} else {
return width * 2;
}
}
},
setImageLocalSrc(url,msgId){
let that=this;
that.messageApi.setImageLocalSrc(url,msgId);
},
toYYYPerson:function(item){
let that=this;
if(item.meFlag==false){
that.$emit("toYYYPerson",item);
}
},
checkChange:function(msg){
let that=this;
that.$emit("checkChange",msg);
},
handleMessage:function(menuItem){
this.toolTipFlag=false;
this.$emit("handleMessage",menuItem,this.item);
},
transform (content, fileSize, fileSuffix,linkName) {
if (fileSize === undefined) {
fileSize = '';
}
if (fileSuffix === undefined) {
fileSuffix = '';
}
let html = function(end) {
return new RegExp('\\n*\\[' + (end || '') +
'(code|pre|div|span|p|table|thead|th|tbody|tr|td|ul|li|ol|li|dl|dt|dd|h2|h3|h4|h5)([\\s\\S]*?)]\\n*',
'g');
};
if (content)
{
content = content.replace(/&(?!#?[a-zA-Z0-9]+;)/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/'/g, '&#39;')
.replace(/"/g, '&quot;') // XSS
.replace(html(), '<$1 $2>')
.replace(html('/'), '</$1>') // HTML
.replace(/\n/g, '<br>'); //
content=getUrl(content);
content=content.replace(/face\[([^\s\[\]]+?)]/g, function(face) {
let alt = face.replace(/^face/g, '');
let fa =faceUtil.emojiMap;
let srcPrefix=faceUtil.emojiUrl;
let imageSrc=srcPrefix+ fa[alt];
//console.log("",imageSrc);
let imgContent= '<img class="emoji" src="' +imageSrc+ '"/>';
return imgContent;
})
//
.replace(/img\[([^\s]+?)]/g, function(img) {
let href = img.replace(/(^img\[)|(]$)/g, '');
return '<img class="message-img" src="' + href + '" alt="消息图片不能加载">';
})
//
.replace(/file\([\s\S]+?\)\[[\s\S]*?]/g, function(str) {
let href = (str.match(/file\(([\s\S]+?)\)\[/) || [])[1];
let text = (str.match(/\)\[([\s\S]*?)]/) || [])[1];
if (!href) return str;
return '<div class="flex"><i class="iconfont icon-xiazai-yun"></i><a class="message-file" href="' +
href + '">' + (text || href) + '</a><span>' + fileSize + '</span></div>'
})
//
.replace(/audio\[([^\s]+?)]/g, function(audio) {
return '<div class="message-audio" data-src="' + audio.replace(/(^audio\[)|(]$)/g, '') +
'"><i class="layui-icon">&#xe652;</i><p>音频消息</p></div>';
})
//
.replace(/video\[([^\s]+?)]/g, function(video) {
return '<div class="message-video" data-src="' + video.replace(/(^video\[)|(]$)/g, '') +
'"><i class="layui-icon">&#xe652;</i></div>';
})
//
.replace(/a\([\s\S]+?\)\[[\s\S]*?]/g, function(str) {
let href = (str.match(/a\(([\s\S]+?)\)\[/) || [])[1];
let text = (str.match(/\)\[([\s\S]*?)]/) || [])[1];
if (!href) return str;
if(linkName&&linkName.length>0){
return '<a href="' + href + '" target="_blank">' + linkName + '</a>';
};
return '<a href="' + href + '" target="_blank">' + (text || href) + '</a>';
})
}
return content;
}
}
}
</script>
<style lang="scss" scoped>
.message-item-container {
padding:0rpx 20rpx;
}
.chat-time {
padding: 4rpx 0rpx;
text-align: center;
font-size: 28rpx;
color: #aaaaaa;
}
.chat-container {
width: 100%;
display: flex;
flex-direction: row;
justify-content: flex-start;
justify-items: flex-start;
align-items: flex-start;
}
.chat-location-me {
flex-direction: row-reverse;
text-align: right;
}
.chat-icon-me {
margin-right: 10rpx;
}
.chat-icon-container {
margin-top: 12rpx;
}
.chat-icon {
width: 80rpx;
height: 80rpx;
border-radius: 10rpx;
background-color: #ffffff;
}
.chat-content-container {
margin: 0rpx 15rpx;
max-width: 70%;
}
.chat-user-name {
font-size: 26rpx;
color: #888888;
}
.chat-text-container {
//word-break: break-all;
word-wrap:break-word;
background-color: #ffffff;
border-radius: 10rpx;
padding: 20rpx 20rpx;
margin-top: 5rpx;
font-size: 32rpx;
}
.chat-text-container-me {
background-color: #95ec69;
}
.emoji {
width: 40rpx !important;
height: 40rpx !important;
position: relative;
top: 10rpx;
}
//
.content-audio-container {
background-color: #ffffff;
border-radius: 10rpx;
padding: 25rpx 20rpx;
margin-top: 5rpx;
/* #ifndef APP-NVUE */
max-width: 500rpx;
/* #endif */
display: flex;
flex-direction: row;
align-items: center;
.voice_icon {
height: 34rpx;
width: 34rpx;
background-repeat: no-repeat;
background-size: 100%;
}
.voice_icon_right {
background-image: url('@/static/images/chat/voice/voice-left-3.png');
margin-left: 10rpx;
}
.voice_icon_left {
background-image: url('@/static/images/chat/voice/voice-right-3.png');
margin-right: 10rpx;
}
.voice_icon_right_an {
animation: voiceAn_right 1s linear alternate infinite;
}
.voice_icon_left_an {
animation: voiceAn_left 1s linear alternate infinite;
}
@keyframes voiceAn_right {
0% {
background-image: url('@/static/images/chat/voice/voice-left-1.png');
}
50% {
background-image: url('@/static/images/chat/voice/voice-left-2.png');
}
100% {
background-image: url('@/static/images/chat/voice/voice-left-3.png');
}
}
@keyframes voiceAn_left {
0% {
background-image: url('@/static/images/chat/voice/voice-right-1.png');
}
50% {
background-image: url('@/static/images/chat/voice/voice-right-2.png');
}
100% {
background-image: url('@/static/images/chat/voice/voice-right-3.png');
}
}
}
//
.chat-audio-container-me {
background-color: #95ec69;
flex-direction: row-reverse;
}
//
.contentType-file {
width: 450rpx;
padding: 0;
border-radius: 10rpx;
border: 1rpx solid #e4e7ed;
background-color: #ffffff !important;
}
//
.contentType-redbag {
padding: 20rpx 30rpx;
padding-bottom: 6rpx;
border-radius: 10rpx;
background-color: #f29100;
color: #ffffff;
}
//
.contentType-location {
width: 500rpx;
padding: 0rpx 10rpx;
border-radius: 10rpx;
border: 1rpx solid #e4e7ed;
background-color: #ffffff !important;
}
//
.contentType-fixed {
border-radius: 10rpx;
font-size: 32rpx;
word-break: break-word;
padding: 20rpx;
background-color: #95ec69 !important;
border: 1rpx solid #e4e7ed;
max-width: 400rpx;
.img {
width: 200rpx;
height: auto;
max-width: 300rpx;
max-height: 400rpx;
}
}
.u-flex-reserve {
flex-direction: row-reverse;
}
.u-text-reserve {
text-align: left;
}
</style>
File diff suppressed because it is too large Load Diff
+441
View File
@@ -0,0 +1,441 @@
<template>
<view class="zhuanfa-container">
<template v-if="moreFlag==false">
<view>
<u-navbar :is-back="false"
back-icon-color="#333333"
title=""
:border-bottom="false"
:background="{ background: '#f0f0f0'}"
z-index="1001">
<view class="slot-wrap u-flex u-row-between u-col-center u-font-32">
<view @click="goback()">
<text>关闭</text>
</view>
<view style="font-weight: bold;font-size: 34rpx;color: #000000;">
<text>选择一个聊天</text>
</view>
<view @click="changeMoreFlag(1)">
<text>多选</text>
</view>
</view>
</u-navbar>
<view>
<view style="background-color: #f0f0f0;" class="u-p-30">
<view class="u-flex u-row-center u-col-center"
style="background-color: #ffffff;height:70rpx;border-radius: 10rpx;color: #c0c0c0;">
<view class="u-m-r-20">
<u-icon class="u-clear-icon" top="6rpx" :size="40" name="search" color="#c0c0c0"></u-icon>
</view>
<view style="color: #c0c0c0;font-size: 34rpx;">搜索</view>
</view>
<view class="u-m-t-40 u-p-b-20" v-if="latestZhuanFa.length>0">
<view style="font-size: 30rpx;font-weight: bold;">
<text>最近转发</text>
</view>
<view class="u-flex u-row-left u-col-center u-m-t-20" style="text-align: center;">
<view class="u-m-r-40" v-for="(lastestItem,index) in latestZhuanFa" :key="index">
<view class="u-flex u-row-center u-col-center">
<view style="height: 100rpx;width: 100rpx;">
<u-image :src="lastestItem.avatar"
:width="100" :height="100" :border-radius="12"></u-image>
</view>
</view>
<view class="u-line-2 u-tips-color"
style="width: 100rpx;height: 64rpx;line-height: 30rpx;font-size: 26rpx;padding:8rpx;">
<text>{{lastestItem.nickName}}</text>
</view>
</view>
</view>
</view>
</view>
<view class="u-p-30" style="background-color: #ffffff;border-top-left-radius: 20rpx;border-top-right-radius: 20rpx;">
<view class="u-flex u-row-between u-col-center u-font-30">
<view style="color: #000000;font-weight: bold;">
<text>最近聊天</text>
</view>
<view style="color:#36648b;">
<text>创建聊天</text>
</view>
</view>
<view v-if="lastestChatUsers.length>0" class="u-m-t-30">
<view v-for="(user,uIndex) in lastestChatUsers" :key="uIndex"
class="u-flex u-row-left u-col-center u-p-t-16 u-p-b-16">
<view style="width:90rpx;" class="u-flex u-row-left u-col-center">
<view>
<u-image :src="user.avatar" :width="76" :height="76" border-radius="8rpx"></u-image>
</view>
</view>
<view style="width: 600rpx;"
class="u-flex u-row-between u-col-center u-p-20 u-border-bottom">
<view style="text-align: left;">
<view class="u-line-1" style="font-size: 32rpx;color: #000000;">
<text>{{user.nickName}}</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<template v-if="moreFlag==true">
<view>
<u-navbar :is-back="false"
back-icon-color="#333333"
title=""
:border-bottom="false"
:background="{ background: '#f0f0f0'}"
z-index="1001">
<view class="slot-wrap u-flex u-row-between u-col-center u-font-32">
<view @click="changeMoreFlag(0)">
<text>取消</text>
</view>
<view style="font-weight: bold;font-size: 34rpx;color: #000000;">
<text>选择多个聊天</text>
</view>
<view>
<view v-if="checkedUsers.length<1" style="border-radius: 10rpx;background-color: #dddddd;color: #909399;padding: 10rpx 20rpx;">
<text>完成</text>
</view>
<view v-if="checkedUsers.length>0"
@click="openZhuanFa()"
style="border-radius: 10rpx;background-color: #19be6b;color: #ffffff;padding: 10rpx 20rpx;">
<text>完成({{checkedUsers.length}})</text>
</view>
</view>
</view>
</u-navbar>
<view style="background-color: #f0f0f0;">
<view v-if="checkedUsers.length<1" class="u-p-30">
<view class="u-flex u-row-center u-col-center"
style="background-color: #ffffff;height:70rpx;border-radius: 10rpx;color: #c0c0c0;">
<view class="u-m-r-20">
<u-icon class="u-clear-icon" top="6rpx" :size="40" name="search" color="#c0c0c0"></u-icon>
</view>
<view style="color: #c0c0c0;font-size: 34rpx;">搜索</view>
</view>
</view>
<view v-else class="u-p-30">
<view class="u-flex u-row-left u-col-center"
style="background-color: #ffffff;border-radius: 10rpx;color: #c0c0c0;">
<view class="u-p-20" style="width: 400rpx;">
<scroll-view class="hidden-scroll-bar"
:scroll-into-view="scrollIntoViewId"
:scroll-with-animation="true"
:scroll-y="false" :scroll-x="true" style="width:400rpx;">
<view class="u-flex u-row-left u-col-center">
<view class="u-m-l-20"
v-for="(checkeItem,index) in checkedUsers"
:key="index">
<view :id="'u-'+checkeItem.chatNumber"
class="u-flex u-row-center u-col-center">
<view>
<u-image :src="checkeItem.avatar"
:width="80" :height="80" :border-radius="12"></u-image>
</view>
</view>
</view>
<view class="u-p-l-30" v-if="checkedUsers.length<4" style="color: #909399;font-size: 34rpx;">
<text>搜索</text>
</view>
</view>
</scroll-view>
</view>
<view v-if="checkedUsers.length>=4" class="u-p-l-30" style="color: #909399;font-size: 34rpx;" >
<text>搜索</text>
</view>
</view>
</view>
<view class="u-p-30" v-if="latestZhuanFa.length>0">
<view style="font-size: 30rpx;font-weight: bold;">
<text>最近转发</text>
</view>
<view class="u-flex u-row-left u-col-center u-m-t-20" style="text-align: center;">
<view class="u-m-r-40" v-for="(lastestItem,index) in latestZhuanFa" :key="index">
<view class="u-flex u-row-center u-col-center">
<view>
<u-image :src="lastestItem.avatar"
:width="100" :height="100" :border-radius="12"></u-image>
</view>
</view>
<view class="u-line-2 u-tips-color"
style="width: 100rpx;height: 64rpx;line-height: 30rpx;font-size: 26rpx;padding: 8rpx;">
<text>{{lastestItem.nickName}}</text>
</view>
</view>
</view>
</view>
<view class="u-p-30"
style="background-color: #ffffff;border-top-left-radius: 20rpx;border-top-right-radius: 20rpx;">
<view class="u-flex u-row-between u-col-center u-font-30">
<view style="color: #000000;font-weight: bold;">
<text>最近聊天</text>
</view>
<view style="color:#36648b;">
<text>从通讯录选择</text>
</view>
</view>
<view v-if="lastestChatUsers.length>0" class="u-m-t-30">
<view v-for="(user,uIndex) in lastestChatUsers" :key="uIndex"
class="u-flex u-row-left u-col-center u-m-b-20">
<view>
<u-checkbox :size="46" v-model="user.checked" @click.native="checkChange(user)"
shape="circle" active-color="#19be6b"></u-checkbox>
</view>
<view class="u-flex u-row-left u-col-center u-p-b-20">
<view style="width:90rpx;" class="u-flex u-row-center u-col-center">
<view>
<u-image :src="user.avatar" :width="76" :height="76" border-radius="8rpx"></u-image>
</view>
</view>
<view class="u-flex u-row-between u-col-center u-p-20 u-border-bottom">
<view style="text-align: left;">
<view class="u-line-1" style="font-size: 32rpx;color: #000000;width: 480rpx;">
<text>{{user.nickName}}</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<u-popup v-model="openZhuanFaFlag" mode="center" border-radius="16"
:mask-close-able="false"
width="625rpx" :height="checkedUsers.length>3?'600rpx':'520rpx'">
<view class="u-p-30">
<view style="font-size: 34rpx;font-weight: bold;">
<text>分别发送给:</text>
</view>
<view class="u-m-t-30">
<view class="u-flex u-flex-wrap u-row-left u-col-center">
<view v-for="(checkeItem,index) in checkedUsers" class="u-m-r-16 u-m-t-10"
:key="index">
<view class="u-flex u-row-center u-col-center">
<view>
<u-image :src="checkeItem.avatar"
:width="80" :height="80" :border-radius="12"></u-image>
</view>
</view>
</view>
</view>
</view>
<view class="u-flex u-row-between u-col-center u-m-t-30 u-p-t-30 u-p-b-10 u-tips-color u-border-top">
<view>
<text>[逐条转发]{{selectedMessages.length>0?selectedMessages.length:2}}条信息</text>
</view>
<view>
<u-icon name="arrow-right" size="32"></u-icon>
</view>
</view>
<view class="u-m-t-20 u-p-b-20">
<view style="background-color: #f7f7f7;width: 550rpx;padding:16rpx 20rpx;text-align: left;color: #909399;font-size: 30rpx;">
<text>给朋友留言</text>
</view>
</view>
<view style="position: absolute;width: 560rpx;bottom:30rpx;"
class="u-flex u-row-between u-col-center u-font-34 u-p-t-30 u-border-top">
<view style="color: #000000;text-align: center;width: 50%;">
<view @click="cancelZhuanFa()">
<text>取消</text>
</view>
</view>
<view style="color: #36648b;text-align: center;width: 50%;">
<view @click="confirmSend()">
<text>发送({{checkedUsers.length}})</text>
</view>
</view>
</view>
</view>
</u-popup>
<u-popup v-model="maxLengthFlag" mode="center" border-radius="16"
:mask-close-able="false" height="300rpx"
width="620rpx">
<view style="text-align: center;font-weight: bold;font-size: 34rpx;">
<view class="u-flex u-row-center u-col-center" style="width: 100%; height: 200rpx;">
<view>
<text>最多只能选择9个聊天</text>
</view>
</view>
<view @click="maxLengthFlag=false"
style="color: #36648b;padding-top:30rpx;padding-bottom: 30rpx; width: 100%;" class="u-border-top">
<text>我知道了</text>
</view>
</view>
</u-popup>
</view>
</template>
<script>
export default {
data() {
return {
moreFlag:false,
scrollHidden:false,
checkNum:0,
latestZhuanFa:[],
checkedUsers:[],
scrollIntoViewId:"",
lastestChatUsers:[],
openZhuanFaFlag:false,
maxLengthFlag:false,
selectedMessages:[],
};
},
onPageScroll:function(e){
let top= e.scrollTop;
let pageHeight= this.$u.sys().windowHeight;
if((top/pageHeight)>0.25){
console.log("隐藏");
this.scrollHidden=true;
}else{
this.scrollHidden=false;
}
},
onLoad:function(option){
if(option.checkedMessages){
this.selectedMessages=JSON.parse(decodeURIComponent(option.checkedMessages));
this.selectedMessages[0].showTimeFlag=true;
};
this.checkedUsers=[];
this.latestZhuanFa=this.vuex_lastestZhuanFaUsers;
this.latestZhuanFa.map(function(item){
item.checked=false;
});
this.lastestChatUsers=this.vuex_yyyFriendList.concat(this.latestZhuanFa);
this.lastestChatUsers.map(function(item){
item.checked=false;
});
},
methods:{
goback:function(){
uni.navigateBack();
},
changeMoreFlag:function(value){
this.moreFlag=value;
},
checkChange:function(user){
let that=this;
let isExist = this.checkedUsers.some(item=>item.chatNumber===user.chatNumber);
if(!isExist){
if(that.checkedUsers.length>=9){
this.maxLengthFlag=true;
return;
};
this.checkedUsers.push(user);
this.$nextTick(function(){
that.scrollIntoViewId="u-"+user.chatNumber;
})
}else{
this.checkedUsers = this.checkedUsers.filter(function(item) {
return item.chatNumber!=user.chatNumber
});
}
},
openZhuanFa:function(){
this.openZhuanFaFlag=true;
},
confirmSend:function(){
let that=this;
console.log("===选择的用户===",that.checkedUsers);
console.log("===转发的信息===",that.selectedMessages);
let lastMsg=that.selectedMessages[that.selectedMessages.length-1];
let friendList=that.vuex_yyyFriendList;
for (var i = 0; i < friendList.length; i++) {
let friend= friendList[i];
friend.checked=false;
let isExist = that.checkedUsers.some(item=>item.chatNumber===friend.chatNumber);
if(isExist){
that.setLastMsgContent(friend,lastMsg,that.selectedMessages)
}
};
that.$nextTick(function(){
that.$u.vuex("vuex_yyyFriendList",friendList);
that.openZhuanFaFlag=false;
uni.navigateBack();
});
},
cancelZhuanFa:function(){
this.openZhuanFaFlag=false;
},
setLastMsgContent:function(friend,message){
let that=this;
let formatContent="";
let contentType=message.contentType;
let contentObj=message.content;
switch (contentType+""){
case "1":
formatContent=contentObj.text;
formatContent=formatContent.replace(/face\[([^\s\[\]]+?)]/g, function(face) {
return face.replace(/^face/g, '')
});
break;
case "2":
formatContent="[图片]";
break;
case "3":
formatContent="[视频]";
break;
case "4":
formatContent="[语音]";
break;
case "7":
formatContent="[红包]";
break;
case "10":
formatContent="[聊天记录]";
break;
case "11":
formatContent="[图文]";
break;
case "12":
formatContent="[视频文字]";
break;
default:
formatContent="[其他]";
break;
};
console.log("最后一条信息",formatContent);
console.log("群号",friend.chatNumber);
if(friend.chatNumber=="000000"){
let obj={
"lastMsgContent":formatContent,
"messageList":that.selectedMessages,
"lastMsgTime":message.createTime,
};
this.$u.vuex("vuex_fileAssist",obj);
}else{
friend.lastMsgContent=formatContent;
friend.sendTimeStr=message.createTime;
friend.messageList=that.selectedMessages;
}
},
},
}
</script>
<style lang="scss" scoped>
.slot-wrap {
display: flex;
align-items: center;
/* 如果您想让slot内容占满整个导航栏的宽度 */
flex: 1;
/* 如果您想让slot内容与导航栏左右有空隙 */
padding: 0 30rpx;
}
</style>
+239
View File
@@ -0,0 +1,239 @@
<template>
<view>
<view>
<u-navbar :custom-back="goback" title=" " :background="{ background: '#ffffff' }" :border-bottom="false">
<view class="slot-wrap" @click="toDetail()">
<view>
<u-icon name="more-dot-fill" color="#000000" :size="35"></u-icon>
</view>
</view>
</u-navbar>
</view>
<view class="friendInfo">
<view class="img">
<u-image :src="friendInfo.avatar"
mode="aspectFill" width="120" height="120" border-radius="12">
<u-loading slot="loading"></u-loading>
<view slot="error"
class="u-flex u-row-center u-col-center u-p-t-10"
style="width: 90rpx;height: 90rpx;background-color: #f1f1f1;border-radius: 12rpx;">
<view>
<image src="/static/image/default/default-user/default-user.png"
style="width: 64rpx;height: 64rpx;"></image>
</view>
</view>
</u-image>
</view>
<view class="friendInfo-desc">
<view class="friendInfo-desc-name u-flex u-row-left u-col-center">
<view class="u-line-2" style="font-size:42rpx;max-width: 400rpx;">
<text>{{friendInfo.nickName}}</text>
</view>
<view class="u-m-l-10 u-flex u-row-center u-col-center u-p-b-10">
<view>
<u-image v-if="friendInfo.sex==1" src="/static/image/chat/addFriend/woman.png" width="34rpx" height="34rpx"></u-image>
<u-image v-if="friendInfo.sex==0" src="/static/image/chat/addFriend/man.png" width="34rpx" height="34rpx"></u-image>
</view>
</view>
</view>
<view class="friendInfo-desc-gray u-line-1 u-m-b-4" style="max-width: 500rpx;">微信号{{friendInfo.chatNumber}}</view>
<view v-if="friendInfo.address&&friendInfo.address!=0&&friendInfo.address.length>0" class="friendInfo-desc-gray">地区{{friendInfo.address}}</view>
</view>
</view>
<u-cell-group>
<u-cell-item title="备注和标签" :title-style="titleStyle"></u-cell-item>
<u-cell-item :border-bottom="false" title="朋友权限"
:title-style="titleStyle" @click="setFiendPermission"></u-cell-item>
<u-gap height="20" bg-color="#eee" margin-top="1" margin-bottom="1"></u-gap>
<u-cell-item :title-style="titleStyle">
<view slot="title" class="u-flex u-row-left u-col-center">
<view>
<text>朋友圈</text>
</view>
<view class="u-flex u-row-left u-col-center u-m-l-50"
v-if="friendInfo.friendCircle&&friendInfo.friendCircle.length>0">
<template v-for="(circle,index) in friendInfo.friendCircle.length">
<view class="u-m-l-10" v-if="circle!=circle&&circle.length>0">
<u-image :src="circle" :width="90" :height="90"></u-image>
</view>
</template>
</view>
</view>
</u-cell-item>
<u-cell-item title="视频号" :title-style="titleStyle"></u-cell-item>
<u-cell-item :border-bottom="false" title="更多信息" :title-style="titleStyle" @click="linkToMoreInfoMation"></u-cell-item>
</u-cell-group>
<u-gap height="20" bg-color="#eee" margin-top="0" margin-bottom="1"></u-gap>
<view style="background-color: #ffffff;">
<view class="u-flex u-row-center u-col-center u-p-30"
style="font-size: 32rpx;color: #335f83;">
<view class="u-m-r-20">
<!-- width="36rpx" height="36rpx" -->
<u-image src="/static/image/chat/sms-send.png" mode="aspectFit" width="40rpx" height="40rpx"></u-image>
</view>
<view style="font-weight: 450;">
<text>发信息</text>
</view>
</view>
<u-gap height="1" bg-color="#eee"></u-gap>
<view class="u-flex u-row-center u-col-center u-p-30"
style="font-size: 32rpx;color: #335f83;">
<view class="u-m-r-20">
<!-- src="/static/image/chat/yinshipin.jpg" width="40rpx" height="40rpx"-->
<u-image src="/static/image/chat/video-call.png" mode="aspectFit" width="50rpx" height="50rpx"></u-image>
</view>
<view style="font-weight: 450;">
<text>音视频通话</text>
</view>
</view>
</view>
<u-action-sheet :list="callList" v-model="callShow" @click="callClick"></u-action-sheet>
<u-top-tips ref="uTips"></u-top-tips>
</view>
</template>
<script>
export default {
data() {
return {
titleStyle: {
marginLeft: '10rpx',
color: '#000000',
fontSize: "34rpx",
},
friendInfo: {},
callList:[
{text: '视频通话'},
{text: '语音通话'}
],
callShow:false,
}
},
onLoad(option) {
let that=this;
/* let param=JSON.parse(decodeURIComponent(option.personInfo));
this.friendInfo=param; */
this.friendInfo=this.yyy_current_user;
//
let arr=JSON.parse(JSON.stringify(this.vuex_yyyFriendList));
let isExist = arr.some(item=>item.chatNumber===this.friendInfo.chatNumber);
if(!isExist){
arr.push(this.friendInfo);
};
this.$u.vuex("vuex_yyyFriendList",arr);
console.log("this.vuex_yyyFriendList=======",this.vuex_yyyFriendList);
},
methods: {
goback(){
console.log("自定义返回");
uni.navigateBack({
delta:3,
animationType:"slide-out-right"
});
},
//
createSingleChat:function(){
let that=this;
},
//
linkToChatting:function(groupInfo) {
let that=this;
},
//
toCall() {
return;
},
//
callClick:function(index){
this.globalUtil.utilAlert("不能进,没完事~敬请期待");
},
//
setFiendTag() {
this.globalUtil.utilAlert("不能进,没完事~敬请期待");
return;
},
//
setFiendPermission() {
this.globalUtil.utilAlert("不能进,没完事~敬请期待");
return;
},
//
viewHisFiendCircle() {
this.globalUtil.utilAlert("不能进,没完事~敬请期待");
return;
},
//
linkToMoreInfoMation() {
this.$u.route({
url: "/pages/tabbar/contact/friends/friend-more-info",
params: {
signature: this.friendInfo.signature
}
})
},
toDetail:function(){
this.$u.route({
url: "/pages/tabbar/contact/friends/friend-home-detail",
params: {
friendInfo:encodeURIComponent(JSON.stringify(this.friendInfo))
}
})
}
},
}
</script>
<style scoped lang="scss">
.slot-wrap {
display: flex;
align-items: center;
/* 如果您想让slot内容占满整个导航栏的宽度 */
flex: 1;
width: 100%;
/* 如果您想让slot内容与导航栏左右有空隙 */
padding: 0 30rpx;
flex-direction: row;
justify-content: flex-end;
}
.perch {
height: 10rpx;
}
.friendInfo {
display: flex;
justify-content: flex-start;
align-items: flex-start;
padding: 20rpx 30rpx 40rpx 40rpx;
background-color: #FFFFFF;
.img {
display: block;
width: 130rpx;
height: 130rpx;
border-radius: 10rpx;
}
&-desc {
padding-left: 30rpx;
&-name {
font-weight: bold;
font-size: 36rpx;
transform: translateY(-10rpx);
color: #000000;
}
&-gray {
color: #757575;
font-size: 32rpx;
font-weight: 420rpx;
}
}
}
</style>
<style>
page{
background-color: #eeeeee;
}
</style>
+80
View File
@@ -0,0 +1,80 @@
<template>
<view>
<u-navbar :is-back="true"
back-icon-color="#ffffff"
title="摇一摇"
:background="{ background: '#101010'}"
title-color="#ffffff"
:title-bold="true"
title-size="34"
:border-bottom="false"
z-index="1001">
<view slot="right" class="u-p-r-30" @click="toSet()">
<u-icon name="more-dot-fill" color="#ffffff" :size="36"></u-icon>
</view>
</u-navbar>
<view class="u-flex u-row-center u-col-center" style="padding-top: 60%;">
<view>
<u-image src="/static/image/wx/yaoyiyao-1.jpg" :width="200" :height="200"></u-image>
</view>
</view>
<view class="footer-box u-flex u-row-around u-col-center">
<view @click="activeTab=0">
<view class="u-flex u-row-center u-col-center">
<view>
<u-image v-if="activeTab==0" src="/static/image/wx/yaoyiyao-2.jpg" :width="48" :height="48"></u-image>
<u-image v-if="activeTab==1" src="/static/image/wx/yaoyiyao-b.jpg" :width="48" :height="48"></u-image>
</view>
</view>
<view class="u-m-t-8" :style="activeTab==1?'color:#595959;':''">
<text></text>
</view>
</view>
<view @click="activeTab=1">
<view class="u-flex u-row-center u-col-center">
<view>
<u-image v-if="activeTab==0" src="/static/image/wx/yaoyiyao-3.jpg" :width="48" :height="48"></u-image>
<u-image v-if="activeTab==1" src="/static/image/wx/yaoyiyao-c.jpg" :width="48" :height="48"></u-image>
</view>
</view>
<view class="u-m-t-8" :style="activeTab==0?'color:#595959;':''">
<text>歌曲和哼唱</text>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
activeTab:0,
};
},
methods:{
toSet:function(){
console.log("去摇一摇设置");
uni.$u.route("/pages/find/shake/set");
}
}
}
</script>
<style lang="scss" scoped>
.footer-box{
color: #ffffff;
width: 100%;
position: absolute;
bottom: 80rpx;
padding: 20rpx 100rpx;
text-align: center;
font-size: 22rpx;
}
</style>
<style>
page{
background-color: #101010;
}
</style>
+139
View File
@@ -0,0 +1,139 @@
<template>
<view>
<u-navbar :is-back="true"
back-icon-color="#333333"
title="打招呼的人"
:background="{ background: '#f0f0f0'}"
title-color="#000000"
:title-bold="true"
title-size="34"
:border-bottom="false"
z-index="1001">
<view slot="right" class="u-p-r-30 u-font-34" @click="clear()">
<text :style="linkedUsers.length<1?'color:#909399;':'color:#000000'">清空</text>
</view>
</u-navbar>
<view class="u-p-30">
<view v-if="linkedUsers.length>0">
<view class="u-p-b-20">
<text>近三天</text>
</view>
<view v-for="(user,uIndex) in linkedUsers" :key="uIndex"
class="u-flex u-row-left u-col-center u-p-b-12">
<view style="width:100rpx;" class="u-flex u-row-left u-col-center">
<view @longpress="delUser(user,uIndex)">
<u-image :src="user.avatar" :width="76" :height="76" border-radius="8rpx">
<view slot="error"
class="u-flex u-row-center u-col-center u-p-t-10 "
style="width: 76rpx;height: 76rpx;background-color: #f0f0f0;border-radius: 12rpx;">
<view>
<image src="/static/image/default/default-user/default-user.png"
style="width: 40rpx;height: 40rpx;"></image>
</view>
</view>
</u-image>
</view>
</view>
<view @click="toPersonInfo(user)"
style="width: 600rpx;"
class="u-flex u-row-between u-col-center u-p-t-6 u-p-b-12 u-border-bottom">
<view style="text-align: left;">
<view class="u-line-1" style="font-size: 32rpx;color: #000000;">
<text>{{user.nickName}}</text>
</view>
<view class="u-tips-color u-font-28">
<text>{{user.userRemark}}</text>
</view>
</view>
<template v-if="!idAdd(user)">
<view style="background-color: #f0f0f0;color: #000000;padding: 10rpx 20rpx;border-radius: 6rpx;text-align: center;font-size: 30rpx;">
<text>查看</text>
</view>
</template>
<template v-else>
<view class="u-font-28" style="position:relative;top: -16rpx;color: #595b5f;">
<text>已添加</text>
</view>
</template>
</view>
</view>
</view>
<view v-else style="padding-top: 40%;">
<view style="text-align: center;color: #000000;font-size: 34rpx;">
<text>暂时没有人向你打招呼</text>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
linkedUsers:[],
};
},
onLoad:function(){
this.linkedUsers=JSON.parse(JSON.stringify(this.vuex_linkedUsers));
},
methods:{
idAdd:function(user){
let that=this;
if(that.vuex_yyyFriendList.length<1){
return false;
}else{
for (var i = 0; i < that.vuex_yyyFriendList.length; i++) {
let item= that.vuex_yyyFriendList[i];
if(item.chatNumber==user.chatNumber){
return true;
}
}
}
return false;
},
clear:function(){
this.$u.vuex("vuex_linkedUsers",[]);
this.$u.vuex("vuex_yyyFriendList",[]);
this.$u.vuex("vuex_lastestZhuanFaUsers",[]);
this.$u.vuex("yyy_current_user",null);
this.linkedUsers=[];
},
toPersonInfo:function(user){
let that=this;
that.$u.vuex("yyy_current_user",user);
this.$u.route({
url:'/pages/tabbar/find/yaoyiyao/yaoyiyao-user-detail',
});
},
delUser:function(user,index){
let that=this;
uni.showModal({
title: '操作',
content: user.sendTimeStr,
cancelText:'删除',
confirmText:'修改时间',
editable:true,
success: function (res) {
if (res.confirm) {
let newTime=res.content;
if(newTime.length>0){
that.linkedUsers[index].sendTimeStr=newTime;
that.$u.vuex("vuex_linkedUsers",that.linkedUsers);
}
return;
} else if (res.cancel) {
that.linkedUsers.splice(index,1);
that.$u.vuex("vuex_linkedUsers",that.linkedUsers);
}
}
});
}
}
}
</script>
<style lang="scss">
</style>
+454
View File
@@ -0,0 +1,454 @@
<template>
<view>
<u-navbar :is-back="true"
back-icon-color="#333333"
title="摇一摇设置"
:background="{ background: '#f0f0f0'}"
title-color="#000000"
:title-bold="true"
title-size="34"
:border-bottom="false"
z-index="1001">
</u-navbar>
<view>
<u-cell-group>
<u-cell-item @click="showNewUserPop()" title="背景图片" :title-style="titleStyle"></u-cell-item>
<u-cell-item title="音效" :title-style="titleStyle" :arrow="false">
<u-switch slot="right-icon" size="48" active-color="#19BE6B" v-model="checked"></u-switch>
</u-cell-item>
<u-gap height="20" bg-color="#eee" margin-top="1" margin-bottom="1"></u-gap>
<u-cell-item title="打招呼的人" :title-style="titleStyle" @click="toLinkedList()"></u-cell-item>
<u-cell-item title="摇到的历史" @click="addAndUpdate()" :title-style="titleStyle"></u-cell-item>
<u-gap height="20" bg-color="#eee" margin-top="1" margin-bottom="1"></u-gap>
<u-cell-item title="摇一摇信息" :title-style="titleStyle" @click="toSetLatestUsers()"></u-cell-item>
</u-cell-group>
</view>
<u-popup v-model="newLatestFlag" mode="right"
safe-area-inset-bottom
:mask-close-able="false"
length="100%">
<scroll-view class="u-p-30 u-p-t-80" scroll-y="true" style="height:85%;width: 700rpx;">
<view>
<u-collapse :head-style="{borderBottom:'1rpx solid #909399'}">
<u-collapse-item :title="'第'+(index+1)+'位'"
v-for="(newUser,index) in lastestZhuanFaUsers"
:key="index">
<view style="border: 1rpx solid #595b5f;padding: 10rpx 20rpx;">
<u-form :model="newUser" :ref="'newUser-'+index"
label-width="300rpx"
label-position="top">
<u-form-item label="姓名">
<u-input v-model="newUser.nickName" />
</u-form-item>
<u-form-item label="微信号">
<u-input v-model="newUser.chatNumber" />
</u-form-item>
<u-form-item label="头像">
<u-input type="textarea" v-model="newUser.avatar" />
</u-form-item>
</u-form>
</view>
</u-collapse-item>
</u-collapse>
</view>
</scroll-view>
<view class="u-flex u-row-between u-col-center u-p-30">
<u-button @click="addNewLatestUserItem()">新增</u-button>
<u-button v-if="lastestZhuanFaUsers.length>1" @click="delNewLatestUser()">删除</u-button>
<u-button type="success" @click="submitAddLatestUsers()">确定</u-button>
</view>
</u-popup>
<u-popup v-model="addAndUpdateFlag" mode="right"
safe-area-inset-bottom
:mask-close-able="false"
length="100%">
<scroll-view class="u-p-30 u-p-t-80" scroll-y="true" style="height:85%;width: 700rpx;">
<view>
<u-collapse :head-style="{borderBottom:'1rpx solid #909399'}">
<u-collapse-item :title="'第'+(index+1)+'位'"
v-for="(newUser,index) in newUsers"
:key="index">
<view style="border: 1rpx solid #595b5f;padding: 10rpx 20rpx;">
<u-form :model="newUser" :ref="'newUser-'+index"
label-width="300rpx"
label-position="top">
<u-form-item label="姓名">
<u-input :maxlength="-1" v-model="newUser.nickName" />
</u-form-item>
<u-form-item label="微信号">
<u-input :maxlength="-1" placeholder="不能与其他重复!" v-model="newUser.chatNumber" />
</u-form-item>
<u-form-item label="头像">
<u-input :maxlength="-1" type="textarea" v-model="newUser.avatar" />
</u-form-item>
<u-form-item label="地区(注意空格)">
<u-input :maxlength="-1" placeholder="注意格式,如: 广东 广州" v-model="newUser.address" />
</u-form-item>
<u-form-item label="个性签名">
<u-input :maxlength="-1" v-model="newUser.signature" />
</u-form-item>
<u-form-item label="给你打招呼的内容">
<u-input :maxlength="-1" type="textarea" v-model="newUser.userRemark" />
</u-form-item>
<u-form-item label="给你打招呼的时间">
<u-input :maxlength="-1" placeholder="注意格式,如: 22:30" v-model="newUser.createTime" />
</u-form-item>
<u-form-item label="你通过好友时间">
<u-input :maxlength="-1" placeholder="注意格式,如: 22:35" v-model="newUser.sendTimeStr" />
</u-form-item>
<u-form-item label="第一条朋友圈">
<u-input type="textarea" :maxlength="-1" v-model="newUser.friendCircle[0]" />
</u-form-item>
<u-form-item label="第二条朋友圈">
<u-input type="textarea" :maxlength="-1" v-model="newUser.friendCircle[1]" />
</u-form-item>
<u-form-item label="第三条朋友圈">
<u-input type="textarea" :maxlength="-1" v-model="newUser.friendCircle[2]" />
</u-form-item>
</u-form>
</view>
</u-collapse-item>
</u-collapse>
</view>
</scroll-view>
<view class="u-flex u-row-between u-col-center u-p-30">
<u-button @click="addNewUserItem()">新增</u-button>
<u-button v-if="newUsers.length>1" @click="delNewUser()">删除</u-button>
<u-button type="success" @click="submitAddUsers()">确定</u-button>
</view>
</u-popup>
<u-popup v-model="newUserPopFlag" mode="right"
safe-area-inset-bottom
:mask-close-able="false"
length="100%">
<view style="padding-top:15%;">
<view class="u-border-top">
<u-field v-model="count"
type="number"
label="本次录入数量" label-width="200rpx"
placeholder="录入数量,建议5-10个"></u-field>
</view>
<view v-if="count<1" class="u-p-30 u-font-32" style="font-weight: bold;">
<view>填入数量决定了下面录入的每项信息的数量 !!!</view>
<view class="u-m-t-10">比如5个,那么必须填入5个人的信息,每个用%%隔开 ! </view>
<view class="u-m-t-10">不能多! 也不能少! 没有信息的填0 !</view>
<view>地址为例: "广东 广州,0,湖北 武汉,四川 城都,福建 厦门" </view>
</view>
</view>
<view v-if="count>0">
<scroll-view class="u-p-30 u-p-t-80"
scroll-y="true" style="height:80%;width: 700rpx;">
<view style="padding:10rpx 20rpx;">
<u-form :model="propArrObj" :border-bottom="false"
label-width="300rpx"
label-position="top">
<u-form-item label="姓名">
<u-input placeholder="格式: (张三%%李四%%王五)"
type="textarea" :maxlength="-1"
:height="300" border
v-model="propArrObj.nickNameStr" />
</u-form-item>
<u-form-item label="微信号">
<u-input placeholder="格式: (666666%%aaaaaa%%zzzzzz)"
type="textarea" :maxlength="-1"
:height="200" border
v-model="propArrObj.chatNumberStr" />
</u-form-item>
<u-form-item label="头像">
<u-input placeholder="格式: (xxx.jpg%%yyy.jpg%%%zzz%%jpg)"
type="textarea" :maxlength="-1"
:height="300" border
v-model="propArrObj.avatarStr" />
</u-form-item>
<u-form-item label="个性签名">
<u-input placeholder="格式: (哈哈哈%%嘻嘻嘻%%哇哇哇)"
type="textarea" :maxlength="-1"
:height="200" border
v-model="propArrObj.signatureStr" />
</u-form-item>
<u-form-item label="打招呼的内容">
<u-input placeholder="格式: (哈喽%%你好%%hello)"
type="textarea" :maxlength="-1"
:height="200" border
v-model="propArrObj.userRemarkStr" />
</u-form-item>
<u-form-item label="地址">
<u-input placeholder="每个值用%%隔开!没有地址的用0代替"
type="textarea" :maxlength="-1"
:height="100" border
v-model="propArrObj.addressStr" />
</u-form-item>
<u-form-item label="打招呼时间">
<u-input placeholder="格式: (12:01%%12:02%%12:03)"
type="textarea" :maxlength="-1"
:height="100" border
v-model="propArrObj.createTimeStr" />
</u-form-item>
<u-form-item label="加好友时间">
<u-input placeholder="格式: (12:01%%12:02%%12:03)"
type="textarea" :maxlength="-1"
:height="100" border
v-model="propArrObj.sendTimeStr" />
</u-form-item>
<u-form-item label="朋友圈">
<u-input placeholder="格式: ([xxx.jpg%%yyy.jpg,zzz.jpg]%%[111.jpg,222.jpg]%%[]%%[333.jpg])"
type="textarea" :maxlength="-1"
:height="300" border
v-model="propArrObj.friendCircleStr" />
</u-form-item>
</u-form>
</view>
</scroll-view>
<view class="u-flex u-row-between u-col-center u-p-30">
<u-button @click="cancelAdd()">取消</u-button>
<u-button type="success" @click="submitAdd()">确定</u-button>
</view>
</view>
</u-popup>
</view>
</template>
<script>
export default {
data() {
return {
titleStyle:{
fontSize:"34rpx",
color:'#000000'
},
linkedUsers:[], //
addAndUpdateFlag:false, //
newUsers:[
{
id:new Date().getTime(),
avatar:'',
chatNumber:'',
nickName:'',
signature:'',
userRemark:'',
address:'',
sex:0,
friendPower:0,
forbidSelf:false,
forbidHis:false,
createTime:"",
sendTimeStr:"",
lastMsgContent:'',
delFlag:false,
friendCircle:["","",""],
messageList:[],
},
],
checked:true,
newLatestFlag:false,
lastestZhuanFaUsers:[
{
id:'1',
avatar:'/static/image/default/default-user/1.jpg',
chatNumber:'qwe',
nickName:'猫先生',
}
],
//--------------------------------------
newUserPopFlag:false,
count:5,
propArrObj:{
nickNameStr:"",
chatNumberStr:"",
avatarStr:"",
signatureStr:"",
userRemarkStr:"",
addressStr:"",
createTimeStr:"",
sendTimeStr:"",
friendCircleStr:"",
}
};
},
methods:{
toSetLatestUsers:function(){
if(this.vuex_lastestZhuanFaUsers.length>0){
this.lastestZhuanFaUsers=JSON.parse(JSON.stringify(this.vuex_lastestZhuanFaUsers));
}
this.newLatestFlag=true;
},
addNewLatestUserItem:function(){
let obj={
id:new Date().getTime(),
avatar:'',
chatNumber:'',
nickName:'',
};
this.lastestZhuanFaUsers.push(obj);
},
delNewLatestUser:function(){
this.lastestZhuanFaUsers.splice(this.lastestZhuanFaUsers.length-1,1);
},
submitAddLatestUsers:function(){
let that=this;
this.$u.vuex("vuex_lastestZhuanFaUsers",this.lastestZhuanFaUsers);
this.newLatestFlag=false;
},
addAndUpdate:function(){
let that=this;
that.addAndUpdateFlag=true
},
addNewUserItem:function(){
let oldObj= this.newUsers[this.newUsers.length-1];
let obj={
id:new Date().getTime(),
avatar:'',
chatNumber:'',
nickName:'',
signature:'',
userRemark:'',
address:'',
sex:0,
friendPower:0,
forbidSelf:false,
forbidHis:false,
createTime:oldObj.createTime,
sendTimeStr:oldObj.sendTimeStr,
lastMsgContent:'',
delFlag:false,
friendCircle:["","",""],
messageList:[],
};
//this.newUsers.push(obj);
this.newUsers.unshift(obj);
},
delNewUser:function(){
this.newUsers.splice(this.newUsers.length-1,1);
},
submitAddUsers:function(){
let that=this;
uni.showModal({
title: '确认提示',
content: '请认真检查各项填入内容!',
success: function (res) {
if (res.confirm) {
that.addAndUpdateFlag = false;
that.linkedUsers=that.newUsers.concat(that.linkedUsers);
that.newUsers=[
{
id:new Date().getTime(),
avatar:'',
chatNumber:'',
nickName:'',
signature:'',
userRemark:'',
address:'',
sex:0,
friendPower:0,
forbidSelf:false,
forbidHis:false,
createTime:'',
sendTimeStr:'',
lastMsgContent:'',
delFlag:false,
friendCircle:["","",""],
messageList:[],
},
]
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
},
toLinkedList:function(){
let arr=JSON.parse(JSON.stringify(this.vuex_linkedUsers));
let newLinkedUsers=this.linkedUsers.concat(arr);
console.log("去摇一摇设置",newLinkedUsers);
this.$u.vuex("vuex_linkedUsers",newLinkedUsers);
this.linkedUsers=[];
this.$u.route("/pages/tabbar/find/yaoyiyao/yaoyiyao-linked-list");
},
//------------------------------------------------------------------------------------------------
showNewUserPop:function(){
this.newUserPopFlag=true;
},
cancelAdd:function(){
this.newUserPopFlag=false;
},
submitAdd:function(){
let that=this;
let keys= Object.keys(that.propArrObj);
for (let i = 0; i < keys.length; i++) {
let key= keys[i];
let keyValue= that.propArrObj[key];
let valueArr= keyValue.split("%%");
if(valueArr.length!=that.count){
this.globalUtil.utilAlert("第"+(i+1)+"项录入信息有误!["+key+"]");
return;
}
}
that.muchCreateUser();
},
muchCreateUser(){
let that=this;
let arr=[];
for (var i = 0; i < this.count; i++) {
let item={
id:"u-"+i,
avatar:that.propArrObj.avatarStr.split("%%")[i],
chatNumber:that.propArrObj.chatNumberStr.split("%%")[i],
nickName:that.propArrObj.nickNameStr.split("%%")[i],
signature:that.propArrObj.signatureStr.split("%%")[i],
userRemark:that.propArrObj.userRemarkStr.split("%%")[i],
address:that.propArrObj.addressStr.split("%%")[i],
createTime:that.propArrObj.createTimeStr.split("%%")[i],
sendTimeStr:that.propArrObj.sendTimeStr.split("%%")[i],
friendCircle:that.propArrObj.friendCircleStr.split("%%")[i],
lastMsgContent:"",
messageList:[],
sex:0,
friendPower:0,
forbidSelf:false,
forbidHis:false,
delFlag:false,
};
arr.push(item);
}
console.log("====arr====",arr);
that.linkedUsers=arr.concat(that.linkedUsers);
this.count=0;
this.propArrObj={
avatarStr:"",
chatNumberStr:"",
nickNameStr:"",
signatureStr:"",
userRemarkStr:"",
addressStr:"",
createTimeStr:"",
sendTimeStr:"",
friendCircleStr:"",
};
that.newUserPopFlag=false;
}
}
}
</script>
<style lang="scss">
</style>
<style>
page{
background-color: #f0f0f0;
}
</style>

Some files were not shown because too many files have changed in this diff Show More