index.uvue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  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. <view class="menu-section">
  31. <view class="menu-item" @click="handlePassword()">
  32. <image class="menu-icon" src="/static/images/profile/7.png" mode="aspectFit"></image>
  33. <text class="menu-title">修改密码</text>
  34. <text class="menu-arrow">›</text>
  35. </view>
  36. <view class="menu-item">
  37. <image class="menu-icon" src="/static/images/profile/5.png" mode="aspectFit"></image>
  38. <text class="menu-title">版本:{{version}}</text>
  39. <text class="menu-title-download" v-if="isNewVersion" @click="handleVersionClick()">新版下载</text>
  40. </view>
  41. </view>
  42. <!-- 功能菜单 -->
  43. <!-- <view class="menu-section">
  44. <view v-for="(item, index) in menuList" :key="index" class="menu-item" @click="handleMenuClick(item)">
  45. <image class="menu-icon" src="/static/images/profile/7.png" mode="aspectFit"></image>
  46. <text class="menu-title">修改密码</text>
  47. <text class="menu-arrow">›</text>
  48. </view>
  49. </view> -->
  50. <!-- 退出 -->
  51. <view class="logout-wrapper">
  52. <text class="logout-btn" @click="handleLogout">退出</text>
  53. </view>
  54. </scroll-view>
  55. </view>
  56. </template>
  57. <script setup lang="uts">
  58. import { ref, onMounted } from 'vue'
  59. import { getUserInfo, getStoreIsKey } from '../../utils/storage'
  60. import { logout as logoutApi } from '../../api/auth/logout'
  61. import { useAuth } from '../../composables/useAuth'
  62. import { encryptAES, decryptAES } from '../../utils/crypto'
  63. import { getIsKey } from '../../api/auth/login'
  64. import { getConfigKey } from '../../api/system/config.uts'
  65. import { getBaseUrl } from '../../utils/request'
  66. // @ts-ignore
  67. import manifest from '@/manifest.json'
  68. // 用户信息
  69. const userName = ref<string>('')
  70. const userPhone = ref<string>('')
  71. const userDept = ref<string>('')
  72. const userRole = ref<string>('')
  73. const isNewVersion = ref<boolean>(false)
  74. // 版本信息
  75. const manifestVersion = manifest.versionName as string | null
  76. const version = ref<string>(manifestVersion != null ? manifestVersion : '1.0.0')
  77. // 认证管理
  78. const auth = useAuth()
  79. const clearAuth = auth.logout
  80. const getCurrentAccessToken = auth.getCurrentAccessToken
  81. // 菜单列表
  82. type MenuItem = {
  83. id: number
  84. title: string
  85. icon: string
  86. path?: string
  87. action?: string
  88. }
  89. const menuList = ref<MenuItem[]>([
  90. {
  91. id: 1,
  92. title: '修改密码',
  93. icon: '/static/images/profile/7.png',
  94. path: '/pages/profile/password/index'
  95. },
  96. {
  97. id: 2,
  98. title: '关于版本',
  99. icon: '/static/images/profile/5.png',
  100. action: 'version'
  101. },
  102. {
  103. id: 3,
  104. title: '关于我们',
  105. icon: '/static/images/profile/6.png',
  106. path: '/pages/profile/about/index'
  107. },
  108. {
  109. id: 4,
  110. title: '通用设置',
  111. icon: '/static/images/profile/8.png',
  112. path: '/pages/profile/settings/index'
  113. }
  114. ])
  115. // 通讯录点击
  116. const handleContactClick = (): void => {
  117. uni.showToast({
  118. title: '功能开发中',
  119. icon: 'none'
  120. })
  121. }
  122. const compareVersion = (newVersion: string, currentVersion: string): boolean => {
  123. const newVer = newVersion.split('.').map(item => parseInt(item))
  124. const currentVer = currentVersion.split('.').map(item => parseInt(item))
  125. const maxLength = Math.max(newVer.length, currentVer.length)
  126. for (let i = 0; i < maxLength; i++) {
  127. const newNum = i < newVer.length ? newVer[i] : 0
  128. const currentNum = i < currentVer.length ? currentVer[i] : 0
  129. if (newNum > currentNum) {
  130. return true
  131. } else if (newNum < currentNum) {
  132. return false
  133. }
  134. }
  135. return false
  136. }
  137. const checkVersion = async (): Promise<void> => {
  138. const versionJSON = await getConfigKey("gxt.android.version") as UTSJSONObject
  139. const versionServer = versionJSON['msg'] as string
  140. const hasNewVersion = compareVersion(versionServer, version.value) // true
  141. console.log("versionServer:"+versionServer)
  142. console.log("hasNewVersion:"+hasNewVersion)
  143. if(hasNewVersion){
  144. isNewVersion.value = true
  145. }
  146. }
  147. const handlePassword = (): void =>{
  148. uni.navigateTo({
  149. url:"/pages/profile/password/index",
  150. fail: (err: any) => {
  151. uni.showToast({
  152. title: '功能开发中',
  153. icon: 'none'
  154. })
  155. }
  156. })
  157. }
  158. // 菜单点击
  159. const handleMenuClick = (item: MenuItem): void => {
  160. if (item.action == 'version') {
  161. uni.showModal({
  162. title: '版本信息',
  163. content: `当前版本:v${version.value}`,
  164. showCancel: false
  165. })
  166. return
  167. }
  168. if (item.path != null && item.path.length > 0) {
  169. uni.navigateTo({
  170. url: item.path,
  171. fail: (err: any) => {
  172. uni.showToast({
  173. title: '功能开发中',
  174. icon: 'none'
  175. })
  176. }
  177. })
  178. }
  179. }
  180. // 执行退出登录(必须在 handleLogout 之前定义)
  181. const doLogout = async (): Promise<void> => {
  182. try {
  183. await logoutApi()
  184. } catch (e: any) {
  185. console.log('退出登录接口调用失败', e)
  186. } finally {
  187. uni.setStorageSync("login_key", "0")
  188. clearAuth()
  189. }
  190. }
  191. // 退出登录
  192. const handleLogout = (): void => {
  193. uni.showModal({
  194. title: '提示',
  195. content: '确定要退出登录吗?',
  196. success: (res) => {
  197. // 提取属性(any 类型不能直接访问属性)
  198. const confirm = res.confirm as boolean
  199. if (confirm) {
  200. // 使用异步函数包装
  201. doLogout()
  202. }
  203. }
  204. })
  205. }
  206. // 安装APK的单独函数
  207. const installApkFile = (filePath: string): void => {
  208. uni.installApk({
  209. filePath: filePath,
  210. success: () => {
  211. console.log('安装成功');
  212. uni.showToast({
  213. title: '安装成功',
  214. icon: 'success'
  215. });
  216. },
  217. fail: (error) => {
  218. console.error('安装失败:', error);
  219. uni.showToast({
  220. title: '安装失败',
  221. icon: 'none'
  222. });
  223. },
  224. complete: (res) => {
  225. console.log('安装完成:', res);
  226. }
  227. });
  228. }
  229. const installApkWithProgress = (): void => {
  230. // #ifdef APP-ANDROID
  231. // 显示下载进度
  232. uni.showLoading({
  233. title: '下载中...',
  234. mask: true
  235. });
  236. let donwloadUrl = '';
  237. donwloadUrl = getBaseUrl() + '/profile/app/gxt-release.apk'
  238. // 下载APK
  239. const downloadTask = uni.downloadFile({
  240. url: donwloadUrl,
  241. filePath: `${uni.env.USER_DATA_PATH}/${Date.now()}_test.apk`, // 使用时间戳防止重名
  242. success: (downloadRes) => {
  243. uni.hideLoading();
  244. if (downloadRes.statusCode == 200) {
  245. // 确认安装
  246. uni.showModal({
  247. title: '安装提示',
  248. content: '下载完成,是否立即安装?',
  249. success: (modalRes) => {
  250. if (modalRes.confirm) {
  251. installApkFile(downloadRes.tempFilePath);
  252. }
  253. }
  254. });
  255. } else {
  256. uni.showToast({
  257. title: '下载失败',
  258. icon: 'error'
  259. });
  260. }
  261. },
  262. fail: (error) => {
  263. uni.hideLoading();
  264. uni.showToast({
  265. title: '下载失败',
  266. icon: 'none'
  267. });
  268. console.error('下载失败:', error);
  269. }
  270. });
  271. // 监听下载进度
  272. downloadTask.onProgressUpdate((res) => {
  273. console.log('下载进度:', res.progress + '%');
  274. // 如果需要,可以更新UI显示进度
  275. uni.showLoading({
  276. title: `下载中...`,
  277. mask: true
  278. });
  279. });
  280. // #endif
  281. // #ifdef APP-HARMONY
  282. uni.showToast({
  283. title: '请登录PC端,扫描二维码下载并安装',
  284. icon: 'none'
  285. });
  286. // #endif
  287. // #ifdef APP-IOS
  288. uni.showToast({
  289. title: '请登录PC端,扫描二维码下载并安装',
  290. icon: 'none'
  291. });
  292. // #endif
  293. }
  294. // 菜单点击
  295. const handleVersionClick = (): void => {
  296. installApkWithProgress();
  297. }
  298. // 初始化
  299. onMounted(() => {
  300. checkVersion();
  301. const userInfo = getUserInfo()
  302. if (userInfo != null) {
  303. const nickName = userInfo['nickName'] as string | null
  304. const phone = userInfo['phone'] as string | null
  305. const isKey = getStoreIsKey();
  306. if(isKey == "1"){
  307. userName.value = decryptAES(nickName ?? '')
  308. if (phone != null && phone.length > 0) {
  309. userPhone.value = decryptAES(phone ?? '')
  310. }
  311. }else{
  312. userName.value = nickName ?? ''
  313. if (phone != null && phone.length > 0) {
  314. userPhone.value = phone
  315. }
  316. }
  317. const deptName = userInfo['deptName'] as string | null
  318. if (deptName != null && deptName.length > 0) {
  319. userDept.value = deptName
  320. }
  321. const roleNames = userInfo['roleNames'] as string | null
  322. if (roleNames != null && roleNames.length > 0) {
  323. userRole.value = roleNames
  324. }
  325. }
  326. })
  327. </script>
  328. <style lang="scss">
  329. .page-container {
  330. position: relative;
  331. flex: 1;
  332. background: #f5f8fb;
  333. padding-top: env(safe-area-inset-top);
  334. }
  335. .bg-image {
  336. position: absolute;
  337. top: 0;
  338. left: 0;
  339. width: 100%;
  340. height: 100%;
  341. z-index: 0;
  342. }
  343. .page-content {
  344. position: relative;
  345. flex: 1;
  346. padding-bottom: 100rpx;
  347. z-index: 1;
  348. }
  349. .header {
  350. padding: 80rpx 30rpx 40rpx;
  351. flex-direction: row;
  352. align-items: center;
  353. .avatar {
  354. width: 120rpx;
  355. height: 120rpx;
  356. border: 1rpx solid #ccc;
  357. border-radius: 60rpx;
  358. background-color: #fff;
  359. margin-right: 30rpx;
  360. }
  361. .user-name {
  362. font-size: 40rpx;
  363. color: #ffffff;
  364. font-weight: bold;
  365. }
  366. }
  367. .info-cards {
  368. padding: 0 30rpx;
  369. flex-direction: row;
  370. justify-content: space-between;
  371. margin-bottom: 30rpx;
  372. .info-card {
  373. width: 330rpx;
  374. background-color: #ffffff;
  375. border-radius: 24rpx;
  376. padding: 30rpx 24rpx;
  377. /* #ifndef APP-HARMONY */
  378. box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
  379. /* #endif */
  380. .card-icon {
  381. width: 48rpx;
  382. height: 48rpx;
  383. margin-bottom: 16rpx;
  384. }
  385. .card-label {
  386. font-size: 26rpx;
  387. color: #666666;
  388. margin-bottom: 12rpx;
  389. }
  390. .card-value {
  391. font-size: 32rpx;
  392. color: #333333;
  393. font-weight: bold;
  394. }
  395. }
  396. }
  397. .contact-item {
  398. margin: 0 30rpx 30rpx;
  399. padding: 30rpx;
  400. background-color: #ffffff;
  401. border-radius: 24rpx;
  402. flex-direction: row;
  403. align-items: center;
  404. /* #ifndef APP-HARMONY */
  405. box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
  406. /* #endif */
  407. .contact-icon {
  408. width: 48rpx;
  409. height: 48rpx;
  410. margin-right: 24rpx;
  411. }
  412. .contact-text {
  413. flex: 1;
  414. font-size: 32rpx;
  415. color: #333333;
  416. }
  417. .contact-arrow {
  418. font-size: 48rpx;
  419. color: #cccccc;
  420. line-height: 48rpx;
  421. }
  422. }
  423. .menu-section {
  424. margin: 0 30rpx 30rpx;
  425. background-color: #ffffff;
  426. border-radius: 24rpx;
  427. overflow: hidden;
  428. /* #ifndef APP-HARMONY */
  429. box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
  430. /* #endif */
  431. .menu-item {
  432. flex-direction: row;
  433. align-items: center;
  434. padding: 30rpx;
  435. border-bottom: 1rpx solid #f0f0f0;
  436. &:last-child {
  437. border-bottom: none;
  438. }
  439. .menu-icon {
  440. width: 48rpx;
  441. height: 48rpx;
  442. margin-right: 24rpx;
  443. }
  444. .menu-title {
  445. flex: 1;
  446. font-size: 32rpx;
  447. color: #333333;
  448. }
  449. .menu-title-download {
  450. font-size: 30rpx;
  451. color: #5500ff;
  452. }
  453. .menu-arrow {
  454. font-size: 48rpx;
  455. color: #cccccc;
  456. line-height: 48rpx;
  457. }
  458. }
  459. }
  460. .logout-wrapper {
  461. margin: 0 30rpx 30rpx;
  462. background-color: #ffffff;
  463. border-radius: 24rpx;
  464. overflow: hidden;
  465. align-items: center;
  466. justify-content: center;
  467. height: 100rpx;
  468. /* #ifndef APP-HARMONY */
  469. box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
  470. /* #endif */
  471. .logout-btn {
  472. font-size: 32rpx;
  473. color: #666666;
  474. padding: 20rpx 0;
  475. }
  476. }
  477. </style>