cwg-right-drawer.vue 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. <template>
  2. <view class="notice-container">
  3. <cwg-dropdown ref="dropdownRef" :menu-list="[]">
  4. <view class="pc-header-btn cursor-pointer" :data-tooltip="t('vu.tooltip.t100')">
  5. <cwg-icon name="icon_my" color="#97A1C0" @click="openNotice" />
  6. <cwg-match-media :min-width="791">
  7. <view>{{ name }}</view>
  8. </cwg-match-media>
  9. </view>
  10. <template #btn>
  11. <view class="dropdown-menu dropdown-menu-end w-225px mt-1 show">
  12. <view class="d-flex align-items-center p-2">
  13. <view class="avatar avatar-sm rounded-circle">
  14. <image class="avatar1" src="/static/images/vu/logo.png" mode="aspectFill" />
  15. </view>
  16. <view class="ms-2">
  17. <view class="fw-bold text-dark text-ellipsis mb-2">{{ _displayName }}</view>
  18. <view class="text-body d-block lh-sm text-ellipsis mb-2">{{ _email }}</view>
  19. <text class="cid">CID: <text class="cwg-cursor cursor-pointer cursor-pointer1" :data-tooltip="t('vu.tooltip.t102')" @click="copy(_displayCid)">{{ _displayCid
  20. }}</text></text>
  21. </view>
  22. </view>
  23. <view>
  24. <view class="dropdown-divider my-1"></view>
  25. </view>
  26. <view v-for="item in menuList" :key="item.id">
  27. <view class="dropdown-item user-menu-item d-flex align-items-center gap-2 cursor-pointer"
  28. @click="handleNavigate(item.path)">
  29. <cwg-icon :name="item.icon" :size="16" color="#000" />
  30. <text v-t="item.name"></text>
  31. </view>
  32. </view>
  33. <cwg-match-media :max-width="990" >
  34. <view>
  35. <view class="dropdown-divider my-1"></view>
  36. </view>
  37. <view>
  38. <view class="dropdown-item d-flex align-items-center gap-2 text-danger cursor-pointer"
  39. @click="handleLogout">
  40. <cwg-icon name="logout" :size="16" color="#FF401C" />
  41. <text v-t="'language.i6'"></text>
  42. </view>
  43. </view>
  44. </cwg-match-media>
  45. </view>
  46. </template>
  47. </cwg-dropdown>
  48. </view>
  49. <cwg-global-popup/>
  50. </template>
  51. <script setup lang="ts">
  52. import { ref, watch, onMounted, computed } from 'vue'
  53. import { onLoad, onShow, onLaunch } from '@dcloudio/uni-app'
  54. import useRoute from '@/hooks/useRoute'
  55. import useUserStore from '@/stores/use-user-store'
  56. import { userApi } from '@/api/user'
  57. import { useI18n } from "vue-i18n"
  58. import useRouter from "@/hooks/useRouter"
  59. import useGlobalStore from '@/stores/use-global-store'
  60. import { usePopup } from '@/hooks/usePopup'
  61. const globalStore = useGlobalStore()
  62. const isDark = computed(() => globalStore.theme === 'dark')
  63. const { t } = useI18n()
  64. const router = useRouter()
  65. const { confirm } = usePopup()
  66. const dropdownRef = ref(null)
  67. const userStore = useUserStore()
  68. const route = useRoute()
  69. // 强制用 ref 让插槽能渲染
  70. const menuList = ref([])
  71. const _displayName = ref('--')
  72. const _displayCid = ref('--')
  73. const _activePath = ref('')
  74. const _email = ref('--')
  75. const name = computed(() => {
  76. const info = userStore.userInfo?.customInfo || {}
  77. const firstName = info.firstName || ''
  78. const lastName = info.lastName || ''
  79. return (firstName + ' ' + lastName).trim() || info.name
  80. })
  81. // 复制文本
  82. const copy = (text: string) => {
  83. uni.setClipboardData({
  84. data: text,
  85. success: function () {
  86. uni.showToast({
  87. title: t('Btn.item8'),
  88. icon: 'none',
  89. duration: 2000
  90. });
  91. }
  92. });
  93. };
  94. // 初始化菜单
  95. function initMenu() {
  96. menuList.value = [
  97. { id: 1, path: '/pages/mine/info?type=1', name: 'PersonalManagement.Title.PersonalInformation', icon: 'crm-circle-user' },
  98. { id: 2, path: '/pages/mine/info?type=2', name: 'PersonalManagement.Title.BankInformation', icon: 'crm-building-columns' },
  99. { id: 3, path: '/pages/mine/info?type=3', name: 'PersonalManagement.Title.FileManagement', icon: 'crm-file' },
  100. { id: 4, path: '/pages/mine/info?type=4', name: 'PersonalManagement.Title.SecurityCenter', icon: 'crm-lock' },
  101. ]
  102. }
  103. // 强制同步用户信息
  104. function syncUserInfo() {
  105. const info = userStore.userInfo?.customInfo || {}
  106. const firstName = info.firstName || ''
  107. const lastName = info.lastName || ''
  108. _displayName.value = (firstName + ' ' + lastName).trim() || info.name || info.email || '--'
  109. _displayCid.value = info.cId || info.id || '--'
  110. _email.value = info.email || '--'
  111. }
  112. // 强制同步路径
  113. function syncPath() {
  114. _activePath.value = route.path + (route.query?.type ? `?type=${route.query.type}` : '')
  115. }
  116. onMounted(() => {
  117. initMenu()
  118. syncUserInfo()
  119. syncPath()
  120. })
  121. onShow(() => {
  122. initMenu()
  123. syncUserInfo()
  124. syncPath()
  125. })
  126. // 监听变化自动更新
  127. watch(() => userStore.userInfo, () => {
  128. syncUserInfo()
  129. }, { deep: true })
  130. watch(() => route, () => {
  131. syncPath()
  132. }, { deep: true })
  133. // 打开抽屉
  134. function openNotice() {
  135. // dropdownRef.value?.open()
  136. }
  137. // 关闭
  138. function close() {
  139. dropdownRef.value?.close()
  140. }
  141. // 跳转
  142. function handleNavigate(path) {
  143. router.push({ path })
  144. close()
  145. }
  146. // 登出
  147. async function handleLogout() {
  148. const res = await confirm({
  149. title: t('Msg.SystemPrompt'),
  150. content: t('mine.p'),
  151. showCancel: t('mine.b2'),
  152. confirmText: t('mine.b1'),
  153. })
  154. if (!res) return
  155. try {
  156. await userApi.logout()
  157. } catch (e) { }
  158. userStore.clearUserInfo()
  159. uni.setStorageSync('logoutToSystem', 1)
  160. // uni.$emit('updateSystemList')
  161. router.push('/pages/login/index')
  162. close()
  163. }
  164. defineExpose({ openNotice, close })
  165. </script>
  166. <style scoped lang="scss">
  167. @import "@/uni.scss";
  168. .notice-container {
  169. .text-ellipsis {
  170. display: block !important;
  171. white-space: normal !important;
  172. word-wrap: break-word !important;
  173. word-break: break-all !important;
  174. overflow: visible !important;
  175. text-overflow: unset !important;
  176. }
  177. :deep(.cwg-dropdown-menu-container) {
  178. left: px2rpx(-150) !important;
  179. right: px2rpx(0) !important;
  180. .menu {
  181. border: 0;
  182. overflow: visible;
  183. }
  184. }
  185. @media screen and (max-width: 991px) {
  186. :deep(.cwg-dropdown-menu-container) {
  187. left: px2rpx(-150) !important;
  188. max-width: px2rpx(400);
  189. }
  190. }
  191. @media screen and (max-width: 768px) {
  192. :deep(.cwg-dropdown-menu-container) {
  193. left: px2rpx(-186) !important;
  194. max-width: px2rpx(400);
  195. }
  196. }
  197. .pc-header-btn {
  198. width: auto;
  199. padding: 0 px2rpx(5);
  200. position: relative;
  201. }
  202. .user-menu-item {
  203. color: var(--bs-emphasis-color);
  204. text {
  205. color: var(--bs-emphasis-color);
  206. }
  207. }
  208. .right-drawer {
  209. width: px2rpx(300);
  210. background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important;
  211. display: flex;
  212. flex-direction: column;
  213. padding: px2rpx(20) px2rpx(16);
  214. box-sizing: border-box;
  215. }
  216. .drawer-header {
  217. display: flex;
  218. align-items: center;
  219. gap: px2rpx(12);
  220. padding: px2rpx(20);
  221. border-bottom: 1px solid #d9dde5;
  222. }
  223. .avatar1 {
  224. width: px2rpx(40);
  225. height: px2rpx(40);
  226. border-radius: 50%;
  227. // background: #fff;
  228. }
  229. .user-info {
  230. display: flex;
  231. flex-direction: column;
  232. gap: px2rpx(6);
  233. }
  234. .name {
  235. font-size: px2rpx(22);
  236. font-weight: 600;
  237. color: var(--bs-heading-color);
  238. }
  239. .cid {
  240. font-size: px2rpx(14);
  241. color: #ef4444;
  242. }
  243. .menu-list {
  244. padding: px2rpx(12) 0;
  245. }
  246. .menu-item {
  247. height: px2rpx(48);
  248. display: flex;
  249. align-items: center;
  250. gap: px2rpx(10);
  251. padding: 0 px2rpx(16);
  252. color: var(--bs-heading-color);
  253. font-size: px2rpx(16);
  254. font-weight: 600;
  255. &:hover {
  256. background-color: rgba(0, 0, 0, 0.05);
  257. }
  258. }
  259. .menu-item.active {
  260. background: rgba(108, 133, 149, 0.12) !important;
  261. border-radius: 0.125rem;
  262. }
  263. .logout-wrap {
  264. margin-top: auto;
  265. padding: px2rpx(20);
  266. margin-bottom: px2rpx(20);
  267. }
  268. .logout-btn {
  269. height: px2rpx(44);
  270. background-color: var(--bs-btn-bg);
  271. display: flex;
  272. align-items: center;
  273. justify-content: center;
  274. gap: px2rpx(8);
  275. color: #fff;
  276. font-weight: 600;
  277. font-size: px2rpx(16);
  278. cursor: pointer;
  279. }
  280. }
  281. </style>