cwg-right-drawer.vue 6.6 KB

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