index.uvue 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. <template>
  2. <view class="page-container">
  3. <!-- 背景图 -->
  4. <image class="bg-image" src="/static/images/profile/1.png" mode="widthFix"></image>
  5. <scroll-view class="page-content" style="flex: 1">
  6. <!-- 用户信息头部 -->
  7. <view class="header">
  8. <image class="avatar" src="/static/images/login/2.png" mode="aspectFill"></image>
  9. <text class="user-name">{{ userName }}</text>
  10. </view>
  11. <!-- 信息卡片区域 -->
  12. <view class="info-cards">
  13. <!-- 电话卡片 -->
  14. <view class="info-card">
  15. <image class="card-icon" src="/static/images/profile/2.png" mode="aspectFit"></image>
  16. <text class="card-label">电话</text>
  17. <text class="card-value">{{ userPhone }}</text>
  18. </view>
  19. <!-- 部门卡片 -->
  20. <view class="info-card">
  21. <image class="card-icon" src="/static/images/profile/3.png" mode="aspectFit"></image>
  22. <text class="card-label">部门</text>
  23. <text class="card-value">{{ userDept }}</text>
  24. </view>
  25. </view>
  26. <!-- 通讯录 -->
  27. <view class="contact-item">
  28. <text class="contact-text">{{userRole}}</text>
  29. </view>
  30. <!-- 功能菜单 -->
  31. <view class="menu-section">
  32. <view v-for="(item, index) in menuList" :key="index" class="menu-item" @click="handleMenuClick(item)">
  33. <image class="menu-icon" :src="item.icon" mode="aspectFit"></image>
  34. <text class="menu-title">{{ item.title }}</text>
  35. <text class="menu-arrow">›</text>
  36. </view>
  37. </view>
  38. <!-- 注销账号 -->
  39. <view class="logout-wrapper">
  40. <text class="logout-btn" @click="handleLogout">注销账号</text>
  41. </view>
  42. </scroll-view>
  43. </view>
  44. </template>
  45. <script setup lang="uts">
  46. import { ref, onMounted } from 'vue'
  47. import { getUserInfo } from '../../utils/storage'
  48. import { logout as logoutApi } from '../../api/auth/logout'
  49. import { useAuth } from '../../composables/useAuth'
  50. // @ts-ignore
  51. import manifest from '@/manifest.json'
  52. // 用户信息
  53. const userName = ref<string>('')
  54. const userPhone = ref<string>('')
  55. const userDept = ref<string>('')
  56. const userRole = ref<string>('')
  57. // 版本信息
  58. const manifestVersion = manifest.versionName as string | null
  59. const version = ref<string>(manifestVersion != null ? manifestVersion : '1.0.0')
  60. // 认证管理
  61. const auth = useAuth()
  62. const clearAuth = auth.logout
  63. const getCurrentAccessToken = auth.getCurrentAccessToken
  64. // 菜单列表
  65. type MenuItem = {
  66. id: number
  67. title: string
  68. icon: string
  69. path?: string
  70. action?: string
  71. }
  72. const menuList = ref<MenuItem[]>([
  73. {
  74. id: 1,
  75. title: '关于版本',
  76. icon: '/static/images/profile/5.png',
  77. action: 'version'
  78. },
  79. {
  80. id: 2,
  81. title: '关于我们',
  82. icon: '/static/images/profile/6.png',
  83. path: '/pages/profile/about/index'
  84. },
  85. {
  86. id: 3,
  87. title: '隐私条款',
  88. icon: '/static/images/profile/7.png',
  89. path: '/pages/profile/privacy/index'
  90. },
  91. {
  92. id: 4,
  93. title: '通用设置',
  94. icon: '/static/images/profile/8.png',
  95. path: '/pages/profile/settings/index'
  96. }
  97. ])
  98. // 通讯录点击
  99. const handleContactClick = (): void => {
  100. uni.showToast({
  101. title: '功能开发中',
  102. icon: 'none'
  103. })
  104. }
  105. // 菜单点击
  106. const handleMenuClick = (item: MenuItem): void => {
  107. if (item.action == 'version') {
  108. uni.showModal({
  109. title: '版本信息',
  110. content: `当前版本:v${version.value}`,
  111. showCancel: false
  112. })
  113. return
  114. }
  115. if (item.path != null && item.path.length > 0) {
  116. uni.navigateTo({
  117. url: item.path,
  118. fail: (err: any) => {
  119. uni.showToast({
  120. title: '功能开发中',
  121. icon: 'none'
  122. })
  123. }
  124. })
  125. }
  126. }
  127. // 执行退出登录(必须在 handleLogout 之前定义)
  128. const doLogout = async (): Promise<void> => {
  129. try {
  130. await logoutApi()
  131. } catch (e: any) {
  132. console.log('退出登录接口调用失败', e)
  133. } finally {
  134. clearAuth()
  135. }
  136. }
  137. // 退出登录
  138. const handleLogout = (): void => {
  139. uni.showModal({
  140. title: '提示',
  141. content: '确定要退出登录吗?',
  142. success: (res) => {
  143. // 提取属性(any 类型不能直接访问属性)
  144. const confirm = res.confirm as boolean
  145. if (confirm) {
  146. // 使用异步函数包装
  147. doLogout()
  148. }
  149. }
  150. })
  151. }
  152. // 初始化
  153. onMounted(() => {
  154. const userInfo = getUserInfo()
  155. if (userInfo != null) {
  156. const nickName = userInfo['nickName'] as string | null
  157. userName.value = nickName ?? ''
  158. const phone = userInfo['phone'] as string | null
  159. if (phone != null && phone.length > 0) {
  160. userPhone.value = phone
  161. }
  162. const deptName = userInfo['deptName'] as string | null
  163. if (deptName != null && deptName.length > 0) {
  164. userDept.value = deptName
  165. }
  166. const roleNames = userInfo['roleNames'] as string | null
  167. if (roleNames != null && roleNames.length > 0) {
  168. userRole.value = roleNames
  169. }
  170. }
  171. })
  172. </script>
  173. <style lang="scss">
  174. .page-container {
  175. position: relative;
  176. flex: 1;
  177. background: #f5f8fb;
  178. padding-top: env(safe-area-inset-top);
  179. }
  180. .bg-image {
  181. position: absolute;
  182. top: 0;
  183. left: 0;
  184. width: 100%;
  185. height: 100%;
  186. z-index: 0;
  187. }
  188. .page-content {
  189. position: relative;
  190. flex: 1;
  191. padding-bottom: 100rpx;
  192. z-index: 1;
  193. }
  194. .header {
  195. padding: 80rpx 30rpx 40rpx;
  196. flex-direction: row;
  197. align-items: center;
  198. .avatar {
  199. width: 120rpx;
  200. height: 120rpx;
  201. border: 1rpx solid #ccc;
  202. border-radius: 60rpx;
  203. background-color: #fff;
  204. margin-right: 30rpx;
  205. }
  206. .user-name {
  207. font-size: 40rpx;
  208. color: #ffffff;
  209. font-weight: bold;
  210. }
  211. }
  212. .info-cards {
  213. padding: 0 30rpx;
  214. flex-direction: row;
  215. justify-content: space-between;
  216. margin-bottom: 30rpx;
  217. .info-card {
  218. width: 330rpx;
  219. background-color: #ffffff;
  220. border-radius: 24rpx;
  221. padding: 30rpx 24rpx;
  222. /* #ifndef APP-HARMONY */
  223. box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
  224. /* #endif */
  225. .card-icon {
  226. width: 48rpx;
  227. height: 48rpx;
  228. margin-bottom: 16rpx;
  229. }
  230. .card-label {
  231. font-size: 26rpx;
  232. color: #666666;
  233. margin-bottom: 12rpx;
  234. }
  235. .card-value {
  236. font-size: 32rpx;
  237. color: #333333;
  238. font-weight: bold;
  239. }
  240. }
  241. }
  242. .contact-item {
  243. margin: 0 30rpx 30rpx;
  244. padding: 30rpx;
  245. background-color: #ffffff;
  246. border-radius: 24rpx;
  247. flex-direction: row;
  248. align-items: center;
  249. /* #ifndef APP-HARMONY */
  250. box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
  251. /* #endif */
  252. .contact-icon {
  253. width: 48rpx;
  254. height: 48rpx;
  255. margin-right: 24rpx;
  256. }
  257. .contact-text {
  258. flex: 1;
  259. font-size: 32rpx;
  260. color: #333333;
  261. }
  262. .contact-arrow {
  263. font-size: 48rpx;
  264. color: #cccccc;
  265. line-height: 48rpx;
  266. }
  267. }
  268. .menu-section {
  269. margin: 0 30rpx 30rpx;
  270. background-color: #ffffff;
  271. border-radius: 24rpx;
  272. overflow: hidden;
  273. /* #ifndef APP-HARMONY */
  274. box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
  275. /* #endif */
  276. .menu-item {
  277. flex-direction: row;
  278. align-items: center;
  279. padding: 30rpx;
  280. border-bottom: 1rpx solid #f0f0f0;
  281. &:last-child {
  282. border-bottom: none;
  283. }
  284. .menu-icon {
  285. width: 48rpx;
  286. height: 48rpx;
  287. margin-right: 24rpx;
  288. }
  289. .menu-title {
  290. flex: 1;
  291. font-size: 32rpx;
  292. color: #333333;
  293. }
  294. .menu-arrow {
  295. font-size: 48rpx;
  296. color: #cccccc;
  297. line-height: 48rpx;
  298. }
  299. }
  300. }
  301. .logout-wrapper {
  302. margin: 0 30rpx 30rpx;
  303. background-color: #ffffff;
  304. border-radius: 24rpx;
  305. overflow: hidden;
  306. align-items: center;
  307. justify-content: center;
  308. height: 100rpx;
  309. /* #ifndef APP-HARMONY */
  310. box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
  311. /* #endif */
  312. .logout-btn {
  313. font-size: 32rpx;
  314. color: #666666;
  315. padding: 20rpx 0;
  316. }
  317. }
  318. </style>