cwg-right-drawer.vue 6.4 KB

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