index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. <template>
  2. <view class="user_card_container">
  3. <u-loading-page :loading="isLoading" loading-text="loading..."></u-loading-page>
  4. <custom-nav-bar title="" :route="!isScan" @leftClick="leftClick" />
  5. <u-empty style="padding-top: 20px" mode="data"
  6. icon="https://zkzh-2025.oss-cn-beijing.aliyuncs.com/fs/20240423/cf4a86b913a04341bb44e34bb4d37aa2.png" text="暂无数据"
  7. v-if="!isLoading && !sourceID"></u-empty>
  8. <view v-if="!isLoading && sourceID" style="flex: 1; display: flex; flex-direction: column">
  9. <view class="base_info">
  10. <my-avatar :desc="sourceUserInfo.remark || sourceUserInfo.nickname" :src="sourceUserInfo.faceURL"
  11. size="44">
  12. <image v-if="getRole()==1" class="taoj" src="/static/svg/doctor.svg"></image>
  13. <image v-if="getRole()==2" class="taoj" src="/static/svg/guanjia.svg"></image>
  14. </my-avatar>
  15. <view class="user_name">
  16. <text class="text">{{ getShowName }}</text>
  17. <text class="id" @click="copy(sourceUserInfo.userID)">{{ sourceUserInfo.userID }}</text>
  18. </view>
  19. <view class="add_btn" @click="toAddFriend" v-if="trySendRequest && !friend">
  20. <u-button type="primary" icon="man-add" text="添加"></u-button>
  21. </view>
  22. </view>
  23. <view v-if="memberInfo" class="info_row">
  24. <user-info-row-item lable="群昵称" :content="memberInfo.nickname" />
  25. <user-info-row-item lable="入群时间" :content="getJoinGroupTime" />
  26. <user-info-row-item lable="入群方式" :content="getJoinSource" />
  27. </view>
  28. <view v-if="!isSelf && (showSetRole || showSetMuteMember)" class="info_row">
  29. <user-info-row-item v-if="showSetRole" lable="设为管理员" arrow>
  30. <u-switch :loading="switchLoading" @change="changeMemberRole" :asyncChange="true" size="20"
  31. :value="isAdmin" />
  32. </user-info-row-item>
  33. <user-info-row-item v-if="showSetMuteMember" @click="toMuteMember" arrow lable="设置禁言">
  34. <view class="mute_right">
  35. <text>{{ getMuteTime }}</text>
  36. <u-icon name="arrow-right" color="#999" size="20"></u-icon>
  37. </view>
  38. </user-info-row-item>
  39. </view>
  40. <view v-if="organizationData.length > 0" class="company_row info_row">
  41. <text class="desc_title">组织信息</text>
  42. <view class="company_info">
  43. <user-info-row-item lable="企业/组织" :content="organizationData[0].companyName" />
  44. <user-info-row-item lable="部门" :content="organizationData[0].departmentName" />
  45. <user-info-row-item lable="职位" :content="organizationData[0].position || '-'" />
  46. <view v-if="organizationData.length > 1" @click="toFullOrganization" class="more_dep">
  47. <text>查看更多</text>
  48. <u-icon name="arrow-right" color="#1D6BED"></u-icon>
  49. </view>
  50. </view>
  51. </view>
  52. <view class="info_row">
  53. <user-info-row-item v-if="!isSelf" @click="toMoreInfo" lable="个人资料" arrow />
  54. <!-- <user-info-row-item @click="toPublisher" lable="查看动态" arrow /> -->
  55. </view>
  56. <view v-if="!isSelf || !trySendRequest || friend" class="action_row">
  57. <view v-if="!isBlack" @click="callUser" class="action_item">
  58. <image style="width: 40rpx;height: 40rpx;" src="../../../static/images/user_card_call.png" alt="" />
  59. <text>音视频通话</text>
  60. </view>
  61. <view @click="toDesignatedConversation" class="action_item">
  62. <image style="width: 40rpx;height: 40rpx;" src="../../../static/images/user_card_message.png"
  63. alt="" />
  64. <text>发消息</text>
  65. </view>
  66. </view>
  67. </view>
  68. </view>
  69. </template>
  70. <script>
  71. import {
  72. mapGetters
  73. } from 'vuex';
  74. import {
  75. CommonIsAllow
  76. } from '../../../constant';
  77. import {
  78. getUserInDepartment
  79. } from '../../../api/orgnizaition';
  80. import {
  81. navigateToDesignatedConversation,
  82. callingModule
  83. } from '../../../util/imCommon';
  84. import IMSDK, {
  85. GroupJoinSource,
  86. GroupMemberRole,
  87. SessionType
  88. } from 'openim-uniapp-polyfill';
  89. import MyAvatar from '../../../components/MyAvatar/index.vue';
  90. import CustomNavBar from '../../../components/CustomNavBar/index.vue';
  91. import UserInfoRowItem from './components/UserInfoRowItem.vue';
  92. import dayjs from 'dayjs';
  93. import {
  94. businessSearchUserInfo
  95. } from '../../../api/login';
  96. export default {
  97. components: {
  98. CustomNavBar,
  99. MyAvatar,
  100. UserInfoRowItem
  101. },
  102. data() {
  103. return {
  104. isLoading: true,
  105. sourceID: '',
  106. sourceUserInfo: {},
  107. organizationData: [],
  108. memberInfo: null,
  109. switchLoading: false,
  110. showSetRole: false,
  111. showSetMuteMember: false,
  112. disableAdd: false,
  113. isScan: false,
  114. friend: false,
  115. };
  116. },
  117. computed: {
  118. ...mapGetters(['storeFriendList', 'storeBlackList', 'storeCurrentMemberInGroup', 'storeCurrentUserID',
  119. 'storeSelfInfo', 'storeOrganizationData'
  120. ]),
  121. isFriend() {
  122. return this.storeFriendList.findIndex((friend) => friend.userID === this.sourceID) !== -1;
  123. },
  124. isBlack() {
  125. return this.storeBlackList.some((black) => black.userID === this.sourceID);
  126. },
  127. trySendRequest() {
  128. return !this.isFriend && !this.isSelf && !this.disableAdd && this.sourceUserInfo.allowAddFriend !== 2;
  129. },
  130. isSelf() {
  131. return this.sourceID === this.storeSelfInfo.userID;
  132. },
  133. isAdmin() {
  134. return this.memberInfo?.roleLevel === GroupMemberRole.Admin;
  135. },
  136. getJoinGroupTime() {
  137. return this.memberInfo ? dayjs(this.memberInfo.joinTime).format('YYYY-MM-DD') : '';
  138. },
  139. getJoinSource() {
  140. if (!this.memberInfo) {
  141. return '';
  142. }
  143. switch (this.memberInfo.joinSource) {
  144. case GroupJoinSource.Invitation:
  145. return '邀请进群';
  146. case GroupJoinSource.QrCode:
  147. return '扫码进群';
  148. case GroupJoinSource.Search:
  149. return '搜索进群';
  150. default:
  151. return '-';
  152. }
  153. },
  154. getShowName() {
  155. let suffix = '';
  156. if (this.sourceUserInfo.remark) {
  157. suffix = `(${this.sourceUserInfo.remark})`;
  158. }
  159. return this.sourceUserInfo.nickname + suffix;
  160. },
  161. getMuteTime() {
  162. if (!this.memberInfo.muteEndTime || this.memberInfo.muteEndTime < Date.now()) {
  163. return '';
  164. }
  165. return dayjs(this.memberInfo.muteEndTime).format('YYYY/MM/DD HH:mm');
  166. }
  167. },
  168. onLoad(options) {
  169. const {
  170. sourceID,
  171. sourceInfo,
  172. memberInfo,
  173. disableAdd,
  174. isScan
  175. } = options;
  176. this.friend = options.friend
  177. this.disableAdd = disableAdd ? JSON.parse(disableAdd) : false;
  178. this.isScan = isScan;
  179. if (sourceID) {
  180. this.sourceID = sourceID;
  181. } else {
  182. const info = JSON.parse(sourceInfo);
  183. this.sourceID = info.userID;
  184. }
  185. if (!this.sourceID) {
  186. this.isLoading = false;
  187. return
  188. }
  189. this.getSourceUserInfo();
  190. if (memberInfo) {
  191. this.memberInfo = JSON.parse(memberInfo);
  192. this.checkCurrentMember();
  193. }
  194. this.getOrganizationData();
  195. this.setIMListener();
  196. },
  197. onUnload() {
  198. this.disposeIMListener();
  199. },
  200. methods: {
  201. copy(userID) {
  202. uni.setClipboardData({
  203. showToast: false,
  204. data: userID,
  205. success: function() {
  206. uni.showToast({
  207. icon: 'none',
  208. title: '复制成功'
  209. });
  210. }
  211. });
  212. },
  213. toPublisher() {
  214. let baseUserInfo = JSON.stringify({
  215. userID: this.sourceUserInfo.userID,
  216. nickname: this.sourceUserInfo.nickname,
  217. faceURL: this.sourceUserInfo.faceURL
  218. });
  219. uni.navigateTo({
  220. url: `/pages_im/pages/moments/designatedMoments/index?baseUserInfo=${baseUserInfo}`
  221. });
  222. },
  223. async getSourceUserInfo() {
  224. let info = {};
  225. const friendInfo = this.storeFriendList.find((item) => item.userID === this.sourceID);
  226. if (friendInfo) {
  227. info = {
  228. ...friendInfo
  229. };
  230. } else {
  231. const {
  232. data
  233. } = await IMSDK.asyncApi(IMSDK.IMMethods.GetUsersInfo, IMSDK.uuid(), [this.sourceID]);
  234. info = {
  235. ...(data[0] ?? {})
  236. };
  237. }
  238. this.sourceUserInfo = {
  239. ...info
  240. };
  241. this.isLoading = false;
  242. try {
  243. const {
  244. total,
  245. users
  246. } = await businessSearchUserInfo(this.sourceID);
  247. if (total > 0) {
  248. info = {
  249. ...info,
  250. ...users[0]
  251. };
  252. }
  253. } catch (err) {
  254. console.log(err);
  255. }
  256. this.sourceUserInfo = {
  257. ...info
  258. };
  259. },
  260. getOrganizationData() {
  261. getUserInDepartment(this.sourceID).then((res) => {
  262. this.organizationData = res.users.map((item) => ({
  263. companyName: this.$store.getters.storeOrganizationData.current.name,
  264. departmentName: item.members[0].department.name,
  265. position: item.members[0].position
  266. }));
  267. });
  268. },
  269. async checkCurrentMember() {
  270. let role;
  271. if (this.storeCurrentMemberInGroup.groupID === this.memberInfo.groupID) {
  272. role = this.storeCurrentMemberInGroup.roleLevel;
  273. } else {
  274. try {
  275. const {
  276. data
  277. } = await IMSDK.asyncApi(IMSDK.IMMethods.GetSpecifiedGroupMembersInfo, IMSDK.uuid(), {
  278. groupID: this.memberInfo.groupID,
  279. userIDList: [this.storeCurrentUserID]
  280. });
  281. role = data[0]?.roleLevel;
  282. } catch (e) {}
  283. }
  284. this.showSetRole = role === GroupMemberRole.Owner;
  285. this.showSetMuteMember = role === GroupMemberRole.Owner || (role === GroupMemberRole.Admin && this
  286. .memberInfo.roleLevel === GroupMemberRole.Nomal);
  287. },
  288. toAddFriend() {
  289. console.log("qxj toAddFriend");
  290. uni.$u.route('/pages_im/pages/common/sendAddRequest/index', {
  291. isGroup: false,
  292. sourceID: this.sourceID,
  293. isScan: false,
  294. notNeedVerification: false
  295. });
  296. },
  297. callUser() {
  298. uni.showActionSheet({
  299. itemList: ['语音通话', '视频通话'],
  300. cancelText: '取消',
  301. success: ({
  302. tapIndex
  303. }) => {
  304. callingModule.startLiveChat(!!tapIndex, [this.sourceID], '', this.$store.getters
  305. .storeCurrentUserID);
  306. }
  307. });
  308. },
  309. toFullOrganization() {
  310. uni.$u.route('/pages_im/pages/common/fullOrganization/index', {
  311. organizationData: JSON.stringify(this.organizationData)
  312. });
  313. },
  314. toDesignatedConversation() {
  315. if (this.isScan) {
  316. navigateToDesignatedConversation(this.sourceID, SessionType.Single, this.memberInfo !== null, false)
  317. .catch(() => uni.$u.toast('获取会话信息失败'));
  318. } else {
  319. // uni.navigateBack({
  320. // delta: 2
  321. // });
  322. let url = `/pages_im/pages/conversation/chating/index?back2Tab=false`;
  323. uni.navigateTo({url});
  324. }
  325. },
  326. toMoreInfo() {
  327. uni.navigateTo({
  328. url: `/pages_im/pages/common/userCardMore/index?sourceInfo=${JSON.stringify(this.sourceUserInfo)}`
  329. });
  330. },
  331. changeMemberRole(flag) {
  332. this.switchLoading = true;
  333. const newRole = flag ? GroupMemberRole.Admin : GroupMemberRole.Nomal;
  334. IMSDK.asyncApi(IMSDK.IMMethods.SetGroupMemberInfo, IMSDK.uuid(), {
  335. groupID: this.memberInfo.groupID,
  336. userID: this.sourceUserInfo.userID,
  337. roleLevel: newRole
  338. }).catch(() => uni.$u.toast('设置失败'))
  339. .finally(() => (this.switchLoading = false));
  340. },
  341. toMuteMember() {
  342. uni.navigateTo({
  343. url: `/pages_im/pages/common/setMemberMute/index?sourceInfo=${JSON.stringify([this.memberInfo])}`
  344. });
  345. },
  346. friendInfoChangeHandler({
  347. data
  348. }) {
  349. if (data.userID === this.sourceUserInfo.userID) {
  350. this.sourceUserInfo = {
  351. ...this.sourceUserInfo,
  352. ...data
  353. };
  354. }
  355. },
  356. groupMemberInfoChangeHandler({
  357. data
  358. }) {
  359. if (data.userID === this.memberInfo.userID && data.groupID === this.memberInfo.groupID) {
  360. this.memberInfo = {
  361. ...this.memberInfo,
  362. ...data
  363. };
  364. }
  365. },
  366. setIMListener() {
  367. uni.$on(IMSDK.IMEvents.OnFriendInfoChanged, this.friendInfoChangeHandler);
  368. uni.$on(IMSDK.IMEvents.OnGroupMemberInfoChanged, this.groupMemberInfoChangeHandler);
  369. },
  370. disposeIMListener() {
  371. uni.$off(IMSDK.IMEvents.OnFriendInfoChanged, this.friendInfoChangeHandler);
  372. uni.$off(IMSDK.IMEvents.OnGroupMemberInfoChanged, this.groupMemberInfoChangeHandler);
  373. },
  374. getRole() {
  375. let userType = 0;
  376. let userId = this.sourceUserInfo.userID;
  377. if (userId != undefined && (userId != "" || userId.length > 0)) {
  378. if (userId.indexOf('U') !== -1) {
  379. userType = 0;
  380. }
  381. if (userId.indexOf('D') !== -1) {
  382. userType = 1;
  383. }
  384. if (userId.indexOf('C') !== -1) {
  385. userType = 2;
  386. }
  387. }
  388. return userType;
  389. },
  390. leftClick() {
  391. uni.switchTab({
  392. url: 'pages_im/pages/conversation/conversationList/index'
  393. });
  394. }
  395. },
  396. onBackPress() {
  397. uni.switchTab({
  398. url: 'pages_im/pages/conversation/conversationList/index'
  399. });
  400. return false;
  401. }
  402. };
  403. </script>
  404. <style lang="scss" scoped>
  405. .user_card_container {
  406. @include colBox(false);
  407. height: 100vh;
  408. background-color: #f6f6f6;
  409. overflow-y: auto;
  410. position: relative;
  411. .base_info {
  412. @include vCenterBox();
  413. background-color: #fff;
  414. padding: 44rpx;
  415. margin-bottom: 18rpx;
  416. .add_btn {
  417. width: 140rpx;
  418. height: 60rpx;
  419. margin-left: auto;
  420. .u-button {
  421. width: 140rpx;
  422. height: 60rpx;
  423. }
  424. }
  425. .u-avatar {
  426. margin-right: 24rpx;
  427. }
  428. .user_name {
  429. display: flex;
  430. flex-direction: column;
  431. justify-content: space-between;
  432. margin-bottom: 12rpx;
  433. height: 46px;
  434. margin-left: 20rpx;
  435. .text {
  436. @include nomalEllipsis();
  437. max-width: 300rpx;
  438. }
  439. }
  440. .company {
  441. font-size: 28rpx;
  442. color: $u-primary;
  443. }
  444. }
  445. .info_row {
  446. background-color: #fff;
  447. margin-bottom: 24rpx;
  448. }
  449. .mute_right {
  450. display: flex;
  451. align-items: center;
  452. }
  453. .company_row {
  454. padding: 20rpx 0;
  455. .desc_title {
  456. padding-left: 44rpx;
  457. }
  458. .company_info {
  459. .more_dep {
  460. display: flex;
  461. justify-content: center;
  462. color: #1d6bed;
  463. }
  464. }
  465. ::v-deep .title {
  466. width: 200rpx;
  467. color: #999 !important;
  468. }
  469. }
  470. .action_row {
  471. @include vCenterBox();
  472. align-items: flex-end;
  473. justify-content: space-around;
  474. margin: 44rpx;
  475. flex: 1;
  476. .action_item {
  477. width: 100%;
  478. @include colBox(true);
  479. flex-direction: row;
  480. align-items: center;
  481. justify-content: center;
  482. padding: 22rpx 0;
  483. background: $u-primary;
  484. color: #fff;
  485. border-radius: 6px;
  486. &:first-child {
  487. background-color: #fff;
  488. color: $uni-text-color;
  489. margin-right: 24rpx;
  490. }
  491. img {
  492. margin-right: 16rpx;
  493. width: 40rpx;
  494. height: 40rpx;
  495. }
  496. }
  497. }
  498. .id {
  499. font-size: 24rpx;
  500. color: #999;
  501. }
  502. .online_state {
  503. @include vCenterBox();
  504. margin-left: 24rpx;
  505. font-size: 24rpx;
  506. color: #999;
  507. .dot {
  508. background-color: #10cc64;
  509. width: 12rpx;
  510. height: 12rpx;
  511. border-radius: 50%;
  512. margin-right: 12rpx;
  513. }
  514. .online_str {
  515. @include nomalEllipsis();
  516. max-width: 280rpx;
  517. }
  518. }
  519. .taoj {
  520. position: absolute;
  521. left: 0px;
  522. top: 0px;
  523. right: 0px;
  524. bottom: 0px;
  525. width: 84rpx;
  526. height: 84rpx;
  527. }
  528. }
  529. </style>