Compare commits

...
28 Commits
Author SHA1 Message Date
weiwei 77a7d38fc1 2025-04-28 10:33:11 +08:00
weiwei e796c6f911 1 2025-04-28 10:32:28 +08:00
weiwei 933a5abd3a 调整 2025-04-25 16:41:57 +08:00
weiwei 618ac4cdfb 2025-04-25 16:25:00 +08:00
weiwei 498914d8ad 修改 2025-04-25 16:22:32 +08:00
weiwei 4f480a9927 登录调整 2025-04-25 12:31:49 +08:00
weiwei 6d56a5a8c7 request 2025-04-25 10:53:39 +08:00
zjf e1197a3f43 env 2025-04-25 10:46:40 +08:00
weiwei 23ec18a8b1 修改登录 2025-04-25 10:38:02 +08:00
weiwei 3287ba950f 修改 2025-04-24 19:23:11 +08:00
weiwei e44cf79082 修改 2025-04-24 16:13:36 +08:00
weiwei e871efadd9 登录修改 2025-04-24 14:28:11 +08:00
weiwei 70a9be238f 1 2025-04-23 21:07:55 +08:00
weiwei cfdfeb4537 微信登录页面 2025-04-23 20:20:35 +08:00
weiwei 446e545f2e Merge branch 'jxfk' of http://gitea.zianxin.com/zianxin-tec/biz-mobile-react-cloud into jxfk 2025-04-23 19:16:11 +08:00
weiwei 4fd68cf89d 路由调整 2025-04-23 19:07:02 +08:00
zjf b9345a58f5 request.ts 2025-04-22 11:55:20 +08:00
zjf fcb4e0d890 env 2025-04-22 11:08:57 +08:00
zjf 32c485e539 env 2025-04-21 20:57:35 +08:00
zjf d0f5ef2bdd env 2025-04-21 20:56:39 +08:00
zjf f526b50557 访客页面 2025-04-18 18:58:01 +08:00
zjf a5d0312a19 访客页面 2025-04-18 18:49:59 +08:00
zjf d3cde0e24a 访客页面 2025-04-18 18:31:21 +08:00
zjf 6110d8de16 访客页面 2025-04-18 18:23:46 +08:00
zjf e640856538 访客页面 2025-04-18 17:06:38 +08:00
zjf 4d6a5d5e0a 访客页面 2025-04-18 17:02:21 +08:00
zjf 0a952f01c1 访客页面 2025-04-18 16:50:01 +08:00
zjf 1c6c0a0f7b 访客页面 2025-04-18 15:44:20 +08:00
27 changed files with 3424 additions and 25916 deletions

No files matched your search

+16 -20
View File
@@ -2,7 +2,7 @@
VITE_IS_TOC=true VITE_IS_TOC=true
#是否是H5模式 #是否是H5模式
VITE_IS_H5=true VITE_IS_H5=false
#是否是微服务架构 #是否是微服务架构
VITE_IS_MICRO=true VITE_IS_MICRO=true
@@ -17,31 +17,27 @@ VITE_PWD_ENC_KEY='pigxpigxpigxpigx'
VITE_OAUTH2_APP_CLIENT='mini:mini' VITE_OAUTH2_APP_CLIENT='mini:mini'
# 图片访问基础地址 # 图片访问基础地址
VITE_IMAGE_BASE_PATH = http://localhost:9999 VITE_IMAGE_BASE_PATH = http://localhost:9999
# VITE_IMAGE_BASE_PATH = http://192.168.101.64:9999 # 邓-本地 # VITE_IMAGE_BASE_PATH = http://192.168.101.123:9999 # 邓-本地
# VITE_IMAGE_BASE_PATH = https://123.156.230.15:8089/api # 线上 # VITE_IMAGE_BASE_PATH = https://123.156.230.15:8089/api # 线上
# VITE_IMAGE_BASE_PATH = http://220.191.229.171:8090/api # 线上 # VITE_IMAGE_BASE_PATH = http://220.191.229.171:8090/api # 线上
#VITE_IMAGE_BASE_PATH = https://stgl.pj.gov.cn/api # 线上 # VITE_IMAGE_BASE_PATH = https://stgl.pj.gov.cn/api # 线上
; # VITE_IMAGE_BASE_PATH = http://pjcloud.center.takin.cc # 张 # VITE_IMAGE_BASE_PATH = http://pjcloud.center.takin.cc # 张
#VITE_IMAGE_BASE_PATH = http://59.202.170.152:81/api # 信创线上 # VITE_IMAGE_BASE_PATH = http://59.202.170.152:81/api # 信创线上
# ******************** start 开发环境 start ******************** # ******************** start 开发环境 start ********************
#VITE_APP_DEV_SERVICE = http://59.202.170.152:81/api # 信创线上 # VITE_APP_DEV_SERVICE = http://59.202.170.152:81/api # 信创线上
#VITE_APP_DEV_SERVICE = https://stgl.pj.gov.cn/api # VITE_APP_DEV_SERVICE = https://stgl.pj.gov.cn/api
# VITE_APP_DEV_SERVICE = http://192.168.101.64:9999 # 邓-本地 # VITE_APP_DEV_SERVICE = http://192.168.101.123:9999 # 邓-本地
VITE_APP_DEV_SERVICE = http://127.0.0.1:9999 # VITE_APP_DEV_SERVICE = http://127.0.0.1:9999
# VITE_APP_DEV_SERVICE = http://123.156.230.15:8089/api/ # 线上 VITE_APP_DEV_SERVICE = http://123.156.230.15:8089/api # 访客线上
# VITE_APP_DEV_SERVICE = http://220.191.229.171:8090/api # 线上 # VITE_APP_DEV_SERVICE = http://220.191.229.171:8090/api # 线上
# VITE_APP_DEV_SERVICE = http://localhost:9999
# ******************** end 开发环境 end ******************** # ******************** end 开发环境 end ********************
# ******************** start 生产环境 start **************** # ******************** start 生产环境 start ****************
#VITE_APP_PROD_SERVICE = http://123.156.228.134:48080/app-api # VITE_APP_PROD_SERVICE = http://123.156.228.134:48080/app-api
; #VITE_APP_PROD_SERVICE = http://59.202.170.152:81/api # VITE_APP_PROD_SERVICE = http://59.202.170.152:81/api
#VITE_APP_PROD_SERVICE = https://stgl.pj.gov.cn/api # VITE_APP_PROD_SERVICE = https://stgl.pj.gov.cn/api
VITE_APP_PROD_SERVICE = http://60.12.184.86:8089/api VITE_APP_PROD_SERVICE = http://123.156.230.15:8089/api
#VITE_APP_PROD_SERVICE = http://59.202.170.152:81/api # 信创线上 # VITE_APP_PROD_SERVICE = http://59.202.170.152:81/api # 信创线上
# ******************** end 生产环境 end ******************** # ******************** end 生产环境 end ********************
VITE_DEV_ENV = dev
# VITE_DEV_ENV = prod
-21474
View File
File diff suppressed because it is too large. Load diff
-1
View File
@@ -67,7 +67,6 @@
"luch-request": "^3.1.1", "luch-request": "^3.1.1",
"pinia": "^2.2.6", "pinia": "^2.2.6",
"qrcode": "^1.5.4", "qrcode": "^1.5.4",
"qs": "^6.13.1",
"radash": "^12.1.0", "radash": "^12.1.0",
"vue": "^3.3.11", "vue": "^3.3.11",
"vue-i18n": "^9.1.9", "vue-i18n": "^9.1.9",
+104 -10
View File
@@ -53,6 +53,9 @@ importers:
'@dcloudio/uni-quickapp-webview': '@dcloudio/uni-quickapp-webview':
specifier: 3.0.0-4020920240930001 specifier: 3.0.0-4020920240930001
version: 3.0.0-4020920240930001(postcss@8.5.1)(rollup@4.34.2)(vue@3.5.13(typescript@4.9.5)) version: 3.0.0-4020920240930001(postcss@8.5.1)(rollup@4.34.2)(vue@3.5.13(typescript@4.9.5))
'@vant/weapp':
specifier: ^1.11.7
version: 1.11.7
'@vue/compiler-core': '@vue/compiler-core':
specifier: ^3.5.11 specifier: ^3.5.11
version: 3.5.13 version: 3.5.13
@@ -74,9 +77,9 @@ importers:
pinia: pinia:
specifier: ^2.2.6 specifier: ^2.2.6
version: 2.3.1(typescript@4.9.5)(vue@3.5.13(typescript@4.9.5)) version: 2.3.1(typescript@4.9.5)(vue@3.5.13(typescript@4.9.5))
qs: qrcode:
specifier: ^6.13.1 specifier: ^1.5.4
version: 6.14.0 version: 1.5.4
radash: radash:
specifier: ^12.1.0 specifier: ^12.1.0
version: 12.1.0 version: 12.1.0
@@ -1565,6 +1568,9 @@ packages:
peerDependencies: peerDependencies:
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0
'@vant/weapp@1.11.7':
resolution: {integrity: sha512-Rwn9BBnb4kHSV4XmvBicwtd42J+amEUfnFDcXJsGNPNX4a9c/DoT6YLsm4X1wB2+sQbdiQsbFBLAvGRBxCkD8g==}
'@vitejs/plugin-legacy@5.3.2': '@vitejs/plugin-legacy@5.3.2':
resolution: {integrity: sha512-8moCOrIMaZ/Rjln0Q6GsH6s8fAt1JOI3k8nmfX4tXUxE5KAExVctSyOBk+A25GClsdSWqIk2yaUthH3KJ2X4tg==} resolution: {integrity: sha512-8moCOrIMaZ/Rjln0Q6GsH6s8fAt1JOI3k8nmfX4tXUxE5KAExVctSyOBk+A25GClsdSWqIk2yaUthH3KJ2X4tg==}
engines: {node: ^18.0.0 || >=20.0.0} engines: {node: ^18.0.0 || >=20.0.0}
@@ -2001,6 +2007,9 @@ packages:
cjs-module-lexer@1.4.3: cjs-module-lexer@1.4.3:
resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==}
cliui@6.0.0:
resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
cliui@7.0.4: cliui@7.0.4:
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
@@ -2150,6 +2159,10 @@ packages:
supports-color: supports-color:
optional: true optional: true
decamelize@1.2.0:
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
engines: {node: '>=0.10.0'}
decimal.js@10.5.0: decimal.js@10.5.0:
resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==} resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==}
@@ -2190,6 +2203,9 @@ packages:
resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
dijkstrajs@1.0.3:
resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
dom-walk@0.1.2: dom-walk@0.1.2:
resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==} resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==}
@@ -3205,6 +3221,10 @@ packages:
resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==} resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==}
engines: {node: '>=4.0.0'} engines: {node: '>=4.0.0'}
pngjs@5.0.0:
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
engines: {node: '>=10.13.0'}
postcss-import@14.1.0: postcss-import@14.1.0:
resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
@@ -3297,14 +3317,15 @@ packages:
resolution: {integrity: sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==} resolution: {integrity: sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==}
hasBin: true hasBin: true
qrcode@1.5.4:
resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==}
engines: {node: '>=10.13.0'}
hasBin: true
qs@6.13.0: qs@6.13.0:
resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
engines: {node: '>=0.6'} engines: {node: '>=0.6'}
qs@6.14.0:
resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
engines: {node: '>=0.6'}
querystringify@2.2.0: querystringify@2.2.0:
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
@@ -3371,6 +3392,9 @@ packages:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
require-main-filename@2.0.0:
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
requires-port@1.0.0: requires-port@1.0.0:
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
@@ -3476,6 +3500,9 @@ packages:
resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
set-blocking@2.0.0:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
setprototypeof@1.2.0: setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
@@ -3919,11 +3946,18 @@ packages:
resolution: {integrity: sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==} resolution: {integrity: sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==}
engines: {node: '>=10'} engines: {node: '>=10'}
which-module@2.0.1:
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
which@2.0.2: which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
hasBin: true hasBin: true
wrap-ansi@6.2.0:
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
engines: {node: '>=8'}
wrap-ansi@7.0.0: wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -3989,6 +4023,9 @@ packages:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'} engines: {node: '>=0.4'}
y18n@4.0.3:
resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
y18n@5.0.8: y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -4000,10 +4037,18 @@ packages:
resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
engines: {node: '>= 6'} engines: {node: '>= 6'}
yargs-parser@18.1.3:
resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
engines: {node: '>=6'}
yargs-parser@20.2.9: yargs-parser@20.2.9:
resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
engines: {node: '>=10'} engines: {node: '>=10'}
yargs@15.4.1:
resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
engines: {node: '>=8'}
yargs@16.2.0: yargs@16.2.0:
resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -6268,6 +6313,8 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- rollup - rollup
'@vant/weapp@1.11.7': {}
'@vitejs/plugin-legacy@5.3.2(terser@5.37.0)(vite@5.2.8(@types/node@18.19.75)(sass@1.71.1)(terser@5.37.0))': '@vitejs/plugin-legacy@5.3.2(terser@5.37.0)(vite@5.2.8(@types/node@18.19.75)(sass@1.71.1)(terser@5.37.0))':
dependencies: dependencies:
'@babel/core': 7.26.7 '@babel/core': 7.26.7
@@ -6833,6 +6880,12 @@ snapshots:
cjs-module-lexer@1.4.3: {} cjs-module-lexer@1.4.3: {}
cliui@6.0.0:
dependencies:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 6.2.0
cliui@7.0.4: cliui@7.0.4:
dependencies: dependencies:
string-width: 4.2.3 string-width: 4.2.3
@@ -6946,6 +6999,8 @@ snapshots:
dependencies: dependencies:
ms: 2.1.3 ms: 2.1.3
decamelize@1.2.0: {}
decimal.js@10.5.0: {} decimal.js@10.5.0: {}
dedent@0.7.0: {} dedent@0.7.0: {}
@@ -6970,6 +7025,8 @@ snapshots:
diff-sequences@27.5.1: {} diff-sequences@27.5.1: {}
dijkstrajs@1.0.3: {}
dom-walk@0.1.2: {} dom-walk@0.1.2: {}
domexception@2.0.1: domexception@2.0.1:
@@ -8176,6 +8233,8 @@ snapshots:
pngjs@3.4.0: {} pngjs@3.4.0: {}
pngjs@5.0.0: {}
postcss-import@14.1.0(postcss@8.5.1): postcss-import@14.1.0(postcss@8.5.1):
dependencies: dependencies:
postcss: 8.5.1 postcss: 8.5.1
@@ -8269,11 +8328,13 @@ snapshots:
qrcode-terminal@0.12.0: {} qrcode-terminal@0.12.0: {}
qs@6.13.0: qrcode@1.5.4:
dependencies: dependencies:
side-channel: 1.1.0 dijkstrajs: 1.0.3
pngjs: 5.0.0
yargs: 15.4.1
qs@6.14.0: qs@6.13.0:
dependencies: dependencies:
side-channel: 1.1.0 side-channel: 1.1.0
@@ -8339,6 +8400,8 @@ snapshots:
require-from-string@2.0.2: {} require-from-string@2.0.2: {}
require-main-filename@2.0.0: {}
requires-port@1.0.0: {} requires-port@1.0.0: {}
resolve-cwd@3.0.0: resolve-cwd@3.0.0:
@@ -8469,6 +8532,8 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
set-blocking@2.0.0: {}
setprototypeof@1.2.0: {} setprototypeof@1.2.0: {}
shebang-command@2.0.0: shebang-command@2.0.0:
@@ -8935,10 +9000,18 @@ snapshots:
tr46: 2.1.0 tr46: 2.1.0
webidl-conversions: 6.1.0 webidl-conversions: 6.1.0
which-module@2.0.1: {}
which@2.0.2: which@2.0.2:
dependencies: dependencies:
isexe: 2.0.0 isexe: 2.0.0
wrap-ansi@6.2.0:
dependencies:
ansi-styles: 4.3.0
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi@7.0.0: wrap-ansi@7.0.0:
dependencies: dependencies:
ansi-styles: 4.3.0 ansi-styles: 4.3.0
@@ -8984,14 +9057,35 @@ snapshots:
xtend@4.0.2: {} xtend@4.0.2: {}
y18n@4.0.3: {}
y18n@5.0.8: {} y18n@5.0.8: {}
yallist@3.1.1: {} yallist@3.1.1: {}
yaml@1.10.2: {} yaml@1.10.2: {}
yargs-parser@18.1.3:
dependencies:
camelcase: 5.3.1
decamelize: 1.2.0
yargs-parser@20.2.9: {} yargs-parser@20.2.9: {}
yargs@15.4.1:
dependencies:
cliui: 6.0.0
decamelize: 1.2.0
find-up: 4.1.0
get-caller-file: 2.0.5
require-directory: 2.1.1
require-main-filename: 2.0.0
set-blocking: 2.0.0
string-width: 4.2.3
which-module: 2.0.1
y18n: 4.0.3
yargs-parser: 18.1.3
yargs@16.2.0: yargs@16.2.0:
dependencies: dependencies:
cliui: 7.0.4 cliui: 7.0.4
+59 -251
View File
@@ -1,251 +1,59 @@
<script lang="ts"> <script lang="ts">
import isDevEnv from "./sheep/utils/isDevEnv" // import { onLaunch, onShow, onHide } from "@dcloudio/uni-app"
// onLaunch(() => {})
// import { onLaunch, onShow, onHide } from "@dcloudio/uni-app" // onShow(() => {})
// onLaunch(() => {}) // onHide(() => {})
// onShow(() => {})
// onHide(() => {}) export default {
onLaunch() {
interface TabbarItem { // 检查登录状态
pagePath: string const token = uni.getStorageSync("token")
text: string if (!token) {
iconPath: string uni.navigateTo({
selectedIconPath: string url: "/pages/test/login",
launchParam: string[] })
auth: string[] return
} }
},
const tabbar: TabbarItem[] = [ onShow() {},
// { onHide() {},
// pagePath: "pages/tabbar/canteen",
// text: "食堂", methods: {
// iconPath: "/static/tabbar/canteen.png", async _initTabbar() {},
// selectedIconPath: "/static/tabbar/canteen-select.png", },
// launchParam: ["canteen"], }
// auth: [], </script>
// },
// { <style lang="scss">
// pagePath: "pages/tabbar/selfMerchant", @import "@/sheep/scss/global.scss";
// text: "自营商家", @import url(@/uni_modules/tm-ui/css/remixicon.min.css);
// iconPath: "/static/tabbar/selfMerchant.png",
// selectedIconPath: "/static/tabbar/selfMerchant-select.png", $theme-green: #06be36;
// launchParam: ["selfMerchant"], $theme-blue: #0094ff;
// auth: [], $theme-gray: #999;
// },
// { .botder_btn {
// pagePath: "pages/tabbar/coopMerchant", border: 1px solid #999;
// text: "合作商家", color: #999;
// iconPath: "/static/tabbar/coopMerchant.png", font-weight: 500;
// selectedIconPath: "/static/tabbar/coopMerchant-select.png", font-size: 28rpx;
// launchParam: ["selfMerchant"], border-radius: 10rpx;
// auth: ["user"], padding: 8rpx 15rpx;
// }, }
// {
// pagePath: "pages/tabbar/order", .prefix_title {
// text: "订单", font-size: 32rpx;
// iconPath: "/static/tabbar/order.png", font-weight: 500;
// selectedIconPath: "/static/tabbar/order-select.png", color: #333;
// launchParam: ["canteen", "selfMerchant"],
// auth: ["user"], &::before {
// }, content: "";
// { display: inline-block;
// pagePath: "pages/tabbar/flowStatistics", width: 8rpx;
// text: "流水统计", height: 24rpx;
// iconPath: "/static/tabbar/order.png", background: $theme-blue;
// selectedIconPath: "/static/tabbar/order-select.png", border-radius: 4rpx;
// launchParam: [], margin-right: 10rpx;
// auth: ["merchant"], }
// }, }
// { </style>
// pagePath: "pages/tabbar/individualOrder",
// text: "订单",
// iconPath: "/static/tabbar/order.png",
// selectedIconPath: "/static/tabbar/order-select.png",
// launchParam: [],
// auth: ["merchant"],
// },
{
pagePath: "pages/integrated/visitor/index",
text: "访客申请",
iconPath: "/static/tabbar/my.png",
selectedIconPath: "/static/tabbar/my-select.png",
launchParam: ["canteen", "selfMerchant"],
auth: ["user",],
},
{
pagePath: "pages/integrated/visitor/myvisitor",
text: "申请记录",
iconPath: "/static/tabbar/my.png",
selectedIconPath: "/static/tabbar/my-select.png",
launchParam: ["canteen", "selfMerchant"],
auth: ["user",],
},
{
pagePath: "pages/integrated/visitor/myyyvisitor",
text: "主动邀约",
iconPath: "/static/tabbar/my.png",
selectedIconPath: "/static/tabbar/my-select.png",
launchParam: ["canteen", "selfMerchant"],
auth: ["user",],
},
{
pagePath: "pages/integrated/visitor/myyyvisitorlist",
text: "邀约记录",
iconPath: "/static/tabbar/my.png",
selectedIconPath: "/static/tabbar/my-select.png",
launchParam: ["canteen", "selfMerchant"],
auth: ["user",],
},
{
pagePath: "pages/tabbar/my",
text: "我的",
iconPath: "/static/tabbar/my.png",
selectedIconPath: "/static/tabbar/my-select.png",
launchParam: ["canteen", "selfMerchant"],
auth: ["user", "merchant"],
},
]
// 获取启动参数
const getLaunchParam = () => {
return new URL(window.location.href).searchParams.get("launchParam")
}
// 设置tabbar项目
const setTabbarItem = (index: number, item: TabbarItem, visible: boolean) => {
uni.setTabBarItem({
index,
pagePath: item.pagePath,
text: item.text,
iconPath: item.iconPath,
selectedIconPath: item.selectedIconPath,
visible,
})
}
// 根据启动参数设置tabbar项目
const setTabbarByLaunchParam = (launchParam: string) => {
tabbar.forEach((item, index) => {
const visible = item.launchParam.includes(launchParam)
setTabbarItem(index, item, visible)
})
}
// 根据用户角色设置tabbar项目
const setTabbarByUserRole = (role: string) => {
tabbar.forEach((item, index) => {
const visible = item.auth.includes(role)
setTabbarItem(index, item, visible)
})
}
export default {
async onLaunch() {
if (isDevEnv()) return
uni.setNavigationBarTitle({ title: "" })
// 环境判断
const res = await (window as any).CP2.getAppInfo()
if (res.code == 0) {
// 不是诗画环境
const launchParam = getLaunchParam()
console.log("app launch 启动路径参数", launchParam)
switch (launchParam) {
case "canteen":
uni.switchTab({ url: "/pages/tabbar/canteen" })
break
case "selfMerchant":
uni.switchTab({ url: "/pages/tabbar/selfMerchant" })
break
default:
break
}
} else {
// 是诗画环境
const userRole = uni.getStorageSync("userRole")
if (!userRole) return uni.redirectTo({ url: "/pages/user/userRole" })
if (userRole === "user") return uni.switchTab({ url: "/pages/tabbar/coopMerchant" })
if (userRole === "merchant") return uni.switchTab({ url: "/pages/tabbar/individualOrder" })
}
},
onShow() {},
onHide() {},
methods: {
async _initTabbar() {
if (isDevEnv()) {
tabbar.forEach((item, index) => {
setTabbarItem(index, item, true)
})
return
}
const launchParam = getLaunchParam()
console.log("tabbar launch 启动路径参数", launchParam)
const res = await (window as any).CP2.getAppInfo()
if (res.code == 0) {
// 不是诗画环境
if (launchParam) {
setTabbarByLaunchParam(launchParam)
} else {
uni.hideTabBar()
uni.showToast({ title: "启动参数不合法", icon: "none" })
}
} else {
// 是诗画环境
const userRole = uni.getStorageSync("userRole")
switch (userRole) {
case "user":
setTabbarByUserRole("user")
break
case "merchant":
setTabbarByUserRole("merchant")
break
default:
uni.hideTabBar()
uni.showToast({ title: "用户身份未确认", icon: "none" })
break
}
}
},
},
}
</script>
<style lang="scss">
@import "@/sheep/scss/global.scss";
@import url(@/uni_modules/tm-ui/css/remixicon.min.css);
$theme-green: #06be36;
$theme-blue: #0094ff;
$theme-gray: #999;
.botder_btn {
border: 1px solid #999;
color: #999;
font-weight: 500;
font-size: 28rpx;
border-radius: 10rpx;
padding: 8rpx 15rpx;
}
.prefix_title {
font-size: 32rpx;
font-weight: 500;
color: #333;
&::before {
content: "";
display: inline-block;
width: 8rpx;
height: 24rpx;
background: $theme-blue;
border-radius: 4rpx;
margin-right: 10rpx;
}
}
</style>
+89 -89
View File
@@ -1,91 +1,91 @@
{ {
"name": "", "name" : "",
"appid": "", "appid" : "",
"description": "浦江县机关事务智慧后勤一体化平台", "description" : "浦江县机关事务智慧后勤一体化平台",
"versionName": "1.0.0", "versionName" : "1.0.0",
"versionCode": "100", "versionCode" : "100",
"transformPx": false, "transformPx" : false,
/* 5+App特有相关 */ /* 5+App特有相关 */
"app-plus": { "app-plus" : {
"usingComponents": true, "usingComponents" : true,
"nvueCompiler": "uni-app", "nvueCompiler" : "uni-app",
"renderer": "native", "renderer" : "native",
"nvueStyleCompiler": "uni-app", "nvueStyleCompiler" : "uni-app",
"compilerVersion": 3, "compilerVersion" : 3,
"splashscreen": { "splashscreen" : {
"alwaysShowBeforeRender": true, "alwaysShowBeforeRender" : true,
"waiting": true, "waiting" : true,
"autoclose": true, "autoclose" : true,
"delay": 0 "delay" : 0
}, },
/* 模块配置 */ /* 模块配置 */
"modules": {}, "modules" : {},
/* 应用发布信息 */ /* 应用发布信息 */
"distribute": { "distribute" : {
/* android打包配置 */ /* android打包配置 */
"android": { "android" : {
"permissions": [ "permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>", "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>", "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>", "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>", "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>", "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>", "<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>", "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>", "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>", "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>", "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>", "<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>", "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>" "<uses-permission android:name=\"android.permission.CALL_PHONE\"/>"
] ]
}, },
/* ios打包配置 */ /* ios打包配置 */
"ios": {}, "ios" : {},
/* SDK配置 */ /* SDK配置 */
"sdkConfigs": {} "sdkConfigs" : {}
} }
}, },
/* 快应用特有相关 */ /* 快应用特有相关 */
"quickapp": {}, "quickapp" : {},
/* 小程序特有相关 */ /* 小程序特有相关 */
"mp-weixin": { "mp-weixin" : {
"appid": "", "appid" : "wxe860b6ed8d1ceabe",
"setting": { "setting" : {
"urlCheck": false, "urlCheck" : false,
"es6": true, "es6" : true,
"postcss": true, "postcss" : true,
"minified": true "minified" : true
}, },
"usingComponents": true, "usingComponents" : true,
"requiredPrivateInfos": ["getLocation"], "requiredPrivateInfos" : [ "getLocation" ],
"permission": { "permission" : {
"scope.userLocation": { "scope.userLocation" : {
"desc": "你的位置信息将用于小程序位置接口的效果展示" "desc" : "你的位置信息将用于小程序位置接口的效果展示"
} }
} }
}, },
"mp-alipay": { "mp-alipay" : {
"usingComponents": true "usingComponents" : true
}, },
"mp-baidu": { "mp-baidu" : {
"usingComponents": true "usingComponents" : true
}, },
"mp-toutiao": { "mp-toutiao" : {
"usingComponents": true "usingComponents" : true
}, },
"uniStatistics": { "uniStatistics" : {
"enable": false "enable" : false
}, },
"vueVersion": "3", "vueVersion" : "3",
"h5": { "h5" : {
"router": { "router" : {
"base": "/h5/", "base" : "/h5/",
"mode": "hash" "mode" : "hash"
}, },
"title": "浦江县机关事务智慧后勤一体化平台" "title" : "浦江县机关事务智慧后勤一体化平台"
} }
} }
+108 -276
View File
@@ -9,55 +9,6 @@
} }
}, },
"pages": [ "pages": [
// {
// "path": "pages/tabbar/canteen",
// "style": {
// "navigationBarTitleText": "食堂",
// "disableScroll": true
// }
// },
// {
// "path": "pages/tabbar/selfMerchant",
// "style": {
// "navigationBarTitleText": "自营商家",
// "disableScroll": true
// }
// },
// {
// "path": "pages/tabbar/coopMerchant",
// "style": {
// "navigationBarTitleText": "合作商家",
// "disableScroll": true
// }
// },
// {
// "path": "pages/tabbar/order",
// "style": {
// "navigationBarTitleText": "订单",
// "disableScroll": true
// }
// },
// {
// "path": "pages/tabbar/flowStatistics",
// "style": {
// "navigationBarTitleText": "流水统计",
// "disableScroll": true
// }
// },
// {
// "path": "pages/tabbar/individualOrder",
// "style": {
// "navigationBarTitleText": "订单",
// "disableScroll": true
// }
// },
// {
// "path": "pages/integrated/visitor/myvisitor",
// "style": {
// "navigationBarTitleText": "访客",
// "disableScroll": true
// }
// },
{ {
"path": "pages/tabbar/my", "path": "pages/tabbar/my",
"style": { "style": {
@@ -65,10 +16,34 @@
"disableScroll": true "disableScroll": true
} }
}, },
{
"path": "pages/integrated/visitor/index",
"style": {
"navigationBarTitleText": "访客申请"
}
},
{
"path": "pages/integrated/visitor/myvisitor",
"style": {
"navigationBarTitleText": "申请记录"
}
},
{
"path": "pages/integrated/visitor/myyyvisitor",
"style": {
"navigationBarTitleText": "主动邀约"
}
},
{
"path": "pages/integrated/visitor/myyyvisitorlist",
"style": {
"navigationBarTitleText": "邀约记录"
}
},
{ {
"path": "pages/integrated/visitor/verify", "path": "pages/integrated/visitor/verify",
"style": { "style": {
"navigationStyle": "custom" "navigationBarTitleText": "访客核销"
} }
} }
], ],
@@ -189,14 +164,14 @@
"disableScroll": true "disableScroll": true
} }
}, },
{ // {
"path": "food", // "path": "food",
"style": { // "style": {
"navigationBarTitleText": "菜谱", // "navigationBarTitleText": "菜谱",
"navigationBarBackgroundColor": "#1777FF", // "navigationBarBackgroundColor": "#1777FF",
"disableScroll": true // "disableScroll": true
} // }
}, // },
{ {
"path": "product-detail", "path": "product-detail",
"style": { "style": {
@@ -243,158 +218,7 @@
{ {
"path": "login", "path": "login",
"style": { "style": {
"navigationBarTitleText": "", "navigationBarTitleText": "登录"
"disableScroll": true
}
}
]
},
{
"root": "pages/wallet",
"pages": [
{
"path": "list",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "detail",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "recharge",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/human-face",
"pages": [
{
"path": "list",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "apply",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/user",
"pages": [
{
"path": "userRole",
"style": {
"navigationBarTitleText": "用户身份",
"disableScroll": true
}
}
]
},
{
"root": "pages/pay",
"pages": [
{
"path": "scanPay",
"style": {
"navigationBarTitleText": "支付",
"disableScroll": true
}
},
{
"path": "payComplete",
"style": {
"navigationBarTitleText": "支付完成",
"disableScroll": true
}
},
{
"path": "transferMoney",
"style": {
"navigationBarTitleText": "转账",
"disableScroll": true
}
}
]
},
{
"root": "pages/integrated",
"pages": [
{
"path": "feedback/index",
"style": {
"navigationBarTitleText": "意见反馈"
}
}
]
},
{
"root": "pages/integrated",
"pages": [
{
"path": "visitor/index",
"style": {
"navigationBarTitleText": "访客"
}
}
]
},
{
"root": "pages/integrated",
"pages": [
{
"path": "visitor/myvisitor",
"style": {
"navigationBarTitleText": "访客记录"
}
}
]
},
{
"root": "pages/integrated",
"pages": [
{
"path": "visitor/myyyvisitor",
"style": {
"navigationBarTitleText": "邀约访客"
}
}
]
},
{
"root": "pages/integrated",
"pages": [
{
"path": "visitor/myyyvisitorlist",
"style": {
"navigationBarTitleText": "邀约记录"
}
}
]
},
{
"root": "pages/integrated",
"pages": [
{
"path": "visitor/visitorDetail",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "记录详情"
} }
} }
] ]
@@ -402,85 +226,94 @@
], ],
"globalStyle": { "globalStyle": {
"navigationBarTextStyle": "black", "navigationBarTextStyle": "black",
"navigationBarTitleText": "浦江小程序", "navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#1777FF", "navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#ecf4f9", "backgroundColor": "#F8F8F8"
"navigationStyle": "custom"
}, },
"tabBar": { "tabBar": {
"color": "#7A7E83",
"selectedColor": "#3cc51f",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [ "list": [
{ {
"pagePath": "pages/integrated/visitor/index", "pagePath": "pages/integrated/visitor/index",
"text": "访客申请", "text": "访客申请",
"iconPath": "", "iconPath": "/static/tabbar/my.png",
"selectedIconPath": "" "selectedIconPath": "/static/tabbar/my-select.png"
}, },
{ {
"pagePath": "pages/integrated/visitor/myvisitor", "pagePath": "pages/integrated/visitor/myvisitor",
"text": "申请记录", "text": "申请记录",
"iconPath": "", "iconPath": "/static/tabbar/my.png",
"selectedIconPath": "" "selectedIconPath": "/static/tabbar/my-select.png"
}, },
{ {
"pagePath": "pages/integrated/visitor/myyyvisitor", "pagePath": "pages/integrated/visitor/myyyvisitor",
"text": "访客邀约", "text": "访客邀约",
"iconPath": "", "iconPath": "/static/tabbar/my.png",
"selectedIconPath": "" "selectedIconPath": "/static/tabbar/my-select.png"
}, },
{ {
"pagePath": "pages/integrated/visitor/myyyvisitorlist", "pagePath": "pages/integrated/visitor/myyyvisitorlist",
"text": "邀约记录", "text": "邀约记录",
"iconPath": "", "iconPath": "/static/tabbar/my.png",
"selectedIconPath": "" "selectedIconPath": "/static/tabbar/my-select.png"
}, },
// {
// "pagePath": "pages/integrated/visitor/verify",
// "text": "访客核销",
// "iconPath": "/static/tabbar/my.png",
// "selectedIconPath": "/static/tabbar/my-select.png"
// },
{ {
"pagePath": "pages/integrated/visitor/verify", "pagePath": "pages/tabbar/my",
"text": "核销", "text": "我的",
"iconPath": "", "iconPath": "/static/tabbar/my.png",
"selectedIconPath": "" "selectedIconPath": "/static/tabbar/my-select.png"
}, }
// { // {
// "pagePath": "pages/tabbar/canteen", // "pagePath": "pages/tabbar/canteen",
// "text": "", // "text": "",
// "iconPath": "", // "iconPath": "",
// "selectedIconPath": "" // "selectedIconPath": ""
// }, // },
// { // {
// "pagePath": "pages/tabbar/selfMerchant", // "pagePath": "pages/tabbar/selfMerchant",
// "text": "", // "text": "",
// "iconPath": "", // "iconPath": "",
// "selectedIconPath": "" // "selectedIconPath": ""
// }, // },
// { // {
// "pagePath": "pages/tabbar/coopMerchant", // "pagePath": "pages/tabbar/coopMerchant",
// "text": "", // "text": "",
// "iconPath": "", // "iconPath": "",
// "selectedIconPath": "" // "selectedIconPath": ""
// }, // },
// { // {
// "pagePath": "pages/tabbar/order", // "pagePath": "pages/tabbar/order",
// "text": "", // "text": "",
// "iconPath": "", // "iconPath": "",
// "selectedIconPath": "" // "selectedIconPath": ""
// }, // },
// { // {
// "pagePath": "pages/tabbar/flowStatistics", // "pagePath": "pages/tabbar/flowStatistics",
// "text": "", // "text": "",
// "iconPath": "", // "iconPath": "",
// "selectedIconPath": "" // "selectedIconPath": ""
// }, // },
// { // {
// "pagePath": "pages/tabbar/individualOrder", // "pagePath": "pages/tabbar/individualOrder",
// "text": "", // "text": "",
// "iconPath": "", // "iconPath": "",
// "selectedIconPath": "" // "selectedIconPath": ""
// }, // },
// { // {
// "pagePath": "pages/tabbar/my", // "pagePath": "pages/tabbar/my",
// "text": "", // "text": "",
// "iconPath": "", // "iconPath": "",
// "selectedIconPath": "" // "selectedIconPath": ""
// } // }
// { // {
// "pagePath": "pages/tabbar/canteen", // "pagePath": "pages/tabbar/canteen",
// "text": "食堂", // "text": "食堂",
@@ -517,13 +350,12 @@
// "iconPath": "static/tabbar/order.png", // "iconPath": "static/tabbar/order.png",
// "selectedIconPath": "static/tabbar/order-select.png" // "selectedIconPath": "static/tabbar/order-select.png"
// }, // },
{ // {
"pagePath": "pages/tabbar/my", // "pagePath": "pages/tabbar/my",
"text": "我的", // "text": "我的",
"iconPath": "static/tabbar/my.png", // "iconPath": "static/tabbar/my.png",
"selectedIconPath": "static/tabbar/my-select.png" // "selectedIconPath": "static/tabbar/my-select.png"
} // }
] ]
} }
} }
+115 -106
View File
@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive } from "vue"; import { ref, reactive } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app"; import { onLoad, onShow } from "@dcloudio/uni-app"
import { isEmpty } from "radash" import { isEmpty } from "radash"
import dayjs from "dayjs" import dayjs from "dayjs"
@@ -8,129 +8,138 @@ import { hooks, router } from "@@/index"
import { applyApi } from "@@/api" import { applyApi } from "@@/api"
const state = reactive<any>({ const state = reactive<any>({
detailType: "add", detailType: "add",
detailId: null, detailId: null,
detail: null, detail: null,
}) })
const applyForm = reactive<any>({ let applyForm = reactive<any>({
displayTimeRange: "", displayTimeRange: "",
displayTimeRangeStr: "", displayTimeRangeStr: "",
count: 1, count: 1,
remark: "", remark: "",
}) })
const applyFormRules = reactive<any>({ const applyFormRules = reactive<any>({
displayTimeRangeStr: [ displayTimeRangeStr: [{ required: true, message: "请选择使用时间" }],
{ required: true, message: '请选择使用时间' } count: [
], { required: true, message: "请输入申请数量" },
count: [ { required: true, min: 1, message: "最少申请1张" },
{ required: true, message: '请输入申请数量' }, { required: true, max: 20, message: "最多申请20张" },
{ required: true, min: 1, message: '最少申请1张' }, ],
{ required: true, max: 20, message: '最多申请20张' },
]
}) })
onLoad((options: any) => { onLoad((options: any) => {
const { type, id } = options const { type, id } = options
if (!isEmpty(type)) state.detailType = type if (!isEmpty(type)) state.detailType = type
if (!isEmpty(id)) state.detailId = id if (!isEmpty(id)) state.detailId = id
if (type === 'view') { if (type === "view") {
getDetail() getDetail()
} }
});
onShow(() => {
}) })
onShow(() => {})
const getDetail = async () => { const getDetail = async () => {
if (isEmpty(state.detailId)) return if (isEmpty(state.detailId)) return
const _pa = { id: state.detailId } const _pa = { id: state.detailId }
const { data } = await applyApi.foreignDetail(_pa) const { data } = await applyApi.foreignDetail(_pa)
if (!isEmpty(data)) { if (!isEmpty(data)) {
state.detail = data state.detail = data
const startTime = hooks.useTime(data.applyStartTime, "{y}-{m}-{d}") const startTime = hooks.useTime(data.applyStartTime, "{y}-{m}-{d}")
const endTime = hooks.useTime(data.applyEndTime, "{y}-{m}-{d}") const endTime = hooks.useTime(data.applyEndTime, "{y}-{m}-{d}")
applyForm.displayTimeRangeStr = startTime + "~" + endTime applyForm.displayTimeRangeStr = startTime + "~" + endTime
applyForm.count = data.count applyForm.count = data.count
if (isEmpty(data.remark)) { if (isEmpty(data.remark)) {
applyForm.remark = "无" applyForm.remark = "无"
} else { } else {
applyForm.remark = data.remark applyForm.remark = data.remark
} }
} }
} }
const applyFormRef = ref<any>(null) const applyFormRef = ref<any>(null)
const doSubmit = async () => { const doSubmit = async () => {
applyFormRef.value.submit() applyFormRef.value.submit()
const { isPass } = applyFormRef.value.validate() const { isPass } = applyFormRef.value.validate()
if (!isPass) return if (!isPass) return
const range = applyForm.displayTimeRange const range = applyForm.displayTimeRange
console.log(range); console.log(range)
try { try {
uni.showLoading({ title: '申请中...', mask: false }); uni.showLoading({ title: "申请中...", mask: false })
const _pa = { const _pa = {
applyStartTime: hooks.useTime(range[0], "{y}-{m}-{d}"), applyStartTime: hooks.useTime(range[0], "{y}-{m}-{d}"),
applyEndTime: hooks.useTime(range[1], "{y}-{m}-{d}"), applyEndTime: hooks.useTime(range[1], "{y}-{m}-{d}"),
count: applyForm.count, count: applyForm.count,
remark: applyForm.remark, remark: applyForm.remark,
} }
const { data, code }: any = await applyApi.foreignApply(_pa) const { data, code }: any = await applyApi.foreignApply(_pa)
if (code != 0) throw new Error() if (code != 0) throw new Error()
uni.hideLoading() uni.hideLoading()
uni.showToast({ title: '申请成功', icon: "none" }); uni.showToast({ title: "申请成功", icon: "none" })
router.back() router.back()
} catch (error) { } catch (error) {
console.error(error); console.error(error)
uni.hideLoading() uni.hideLoading()
} }
} }
</script> </script>
<template> <template>
<crud-layout title="外来人员就餐" bg="/static/page/blue-bg.png"> <crud-layout title="外来人员就餐" bg="/static/page/blue-bg.png">
<view class="h-30rpx" /> <view class="h-30rpx" />
<view class="card p-x-30rpx p-y-40rpx"> <view class="card p-x-30rpx p-y-40rpx">
<tm-form ref="applyFormRef" v-model="applyForm" :rules="applyFormRules" direction="vertical"> <tm-form ref="applyFormRef" v-model="applyForm" :rules="applyFormRules" direction="vertical">
<tm-form-item name="displayTimeRangeStr" label="使用时间" :showBottom="false"> <tm-form-item name="displayTimeRangeStr" label="使用时间" :showBottom="false">
<tm-between-time start="2020-1-1" end="2030-1-1" v-model="applyForm.displayTimeRange" <tm-between-time
v-model:model-str="applyForm.displayTimeRangeStr" format="YYYY-MM-DD"> start="2020-1-1"
<view :style="{ pointerEvents: state.detailType == 'view' ? 'auto' : 'none' }"> end="2030-1-1"
<tm-input v-model="applyForm.displayTimeRangeStr" disabled placeholder="请选择使用时间.."> v-model="applyForm.displayTimeRange"
<template v-slot:rightIcon> v-model:model-str="applyForm.displayTimeRangeStr"
<text v-if="state.detailType != 'view'" format="YYYY-MM-DD"
class="p-0.5rem color-#b1b1b1 iconfont icon-xiangxiajiantouxiao"></text> >
</template> <view :style="{ pointerEvents: state.detailType == 'view' ? 'auto' : 'none' }">
</tm-input> <tm-input v-model="applyForm.displayTimeRangeStr" disabled placeholder="请选择使用时间..">
</view> <template v-slot:rightIcon>
</tm-between-time> <text v-if="state.detailType != 'view'" class="p-0.5rem color-#b1b1b1 iconfont icon-xiangxiajiantouxiao"></text>
</tm-form-item> </template>
<view class="h-30rpx" /> </tm-input>
<tm-form-item name="count" label="申请数量" :showBottom="false"> </view>
<tm-input v-model="applyForm.count" type="number" :disabled="state.detailType == 'view'" </tm-between-time>
:showClear="state.detailType != 'view'" placeholder="请输入申请数量.." /> </tm-form-item>
</tm-form-item> <view class="h-30rpx" />
<view class="h-30rpx" /> <tm-form-item name="count" label="申请数量" :showBottom="false">
<tm-form-item name="remark" label="备注" :required="false" :showBottom="false"> <tm-input
<tm-input v-model="applyForm.remark" type="textarea" :disabled="state.detailType == 'view'" v-model="applyForm.count"
:showClear="state.detailType != 'view'" type="number"
:placeholder="state.detailType == 'view' ? '' : '请输入备注..'"></tm-input> :disabled="state.detailType == 'view'"
</tm-form-item> :showClear="state.detailType != 'view'"
</tm-form> placeholder="请输入申请数量.."
</view> />
<view v-if="state.detailType != 'view'" </tm-form-item>
class="bg-#0094ff p-y-20rpx text-center rounded-40rpx m-x-30rpx m-t-60rpx sub-text-30 color-white!" <view class="h-30rpx" />
@tap="doSubmit"> <tm-form-item name="remark" label="备注" :required="false" :showBottom="false">
提交 <tm-input
</view> v-model="applyForm.remark"
</crud-layout> type="textarea"
:disabled="state.detailType == 'view'"
:showClear="state.detailType != 'view'"
:placeholder="state.detailType == 'view' ? '' : '请输入备注..'"
></tm-input>
</tm-form-item>
</tm-form>
</view>
<view
v-if="state.detailType != 'view'"
class="bg-#0094ff p-y-20rpx text-center rounded-40rpx m-x-30rpx m-t-60rpx sub-text-30 color-white!"
@tap="doSubmit"
>
提交
</view>
</crud-layout>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
:deep() { :deep() {
.tmFormItemLabel { .tmFormItemLabel {
padding-bottom: 10rpx !important; padding-bottom: 10rpx !important;
} }
} }
</style> </style>
+89 -83
View File
@@ -1,111 +1,117 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive } from "vue"; import { ref, reactive } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app"; import { onLoad, onShow } from "@dcloudio/uni-app"
import { isEmpty } from "radash" import { isEmpty } from "radash"
import { hooks, router } from "@@/index" import { router } from "@@/index"
import { applyApi } from "@@/api" import { applyApi } from "@@/api"
const state = reactive<any>({ const state = reactive<any>({
detailType: "add", detailType: "add",
detailId: null, detailId: null,
detail: null, detail: null,
}) })
const applyForm = reactive<any>({ let applyForm = reactive<any>({
count: 1, count: 1,
remark: "", remark: "",
}) })
const applyFormRules = reactive<any>({ const applyFormRules = reactive<any>({
count: [ count: [
{ required: true, message: '请输入申请数量' }, { required: true, message: "请输入申请数量" },
{ required: true, min: 1, message: '最少申请1张' }, { required: true, min: 1, message: "最少申请1张" },
{ required: true, max: 20, message: '最多申请20张' }, { required: true, max: 20, message: "最多申请20张" },
] ],
}) })
onLoad((options: any) => { onLoad((options: any) => {
const { type, id } = options const { type, id } = options
if (!isEmpty(type)) state.detailType = type if (!isEmpty(type)) state.detailType = type
if (!isEmpty(id)) state.detailId = id if (!isEmpty(id)) state.detailId = id
if (type === 'view') { if (type === "view") {
getDetail() getDetail()
} }
});
onShow(() => {
}) })
onShow(() => {})
const getDetail = async () => { const getDetail = async () => {
if (isEmpty(state.detailId)) return if (isEmpty(state.detailId)) return
const _pa = { id: state.detailId } const _pa = { id: state.detailId }
const { data } = await applyApi.strideDetail(_pa) const { data } = await applyApi.strideDetail(_pa)
if (!isEmpty(data)) { if (!isEmpty(data)) {
state.detail = data state.detail = data
applyForm.count = data.count applyForm.count = data.count
if (isEmpty(data.remark)) { if (isEmpty(data.remark)) {
applyForm.remark = "无" applyForm.remark = "无"
} else { } else {
applyForm.remark = data.remark applyForm.remark = data.remark
} }
} }
} }
const applyFormRef = ref<any>(null) const applyFormRef = ref<any>(null)
const doSubmit = async () => { const doSubmit = async () => {
applyFormRef.value.submit() applyFormRef.value.submit()
const { isPass } = applyFormRef.value.validate() const { isPass } = applyFormRef.value.validate()
if (!isPass) return if (!isPass) return
const range = applyForm.displayTimeRange const range = applyForm.displayTimeRange
try { try {
uni.showLoading({ title: '申请中...', mask: false }); uni.showLoading({ title: "申请中...", mask: false })
const _pa = { const _pa = {
count: applyForm.count, count: applyForm.count,
remark: applyForm.remark, remark: applyForm.remark,
} }
const { data, code }: any = await applyApi.strideApply(_pa) const { data, code }: any = await applyApi.strideApply(_pa)
if (code != 0) throw new Error() if (code != 0) throw new Error()
uni.hideLoading() uni.hideLoading()
uni.showToast({ title: '申请成功', icon: "none" }); uni.showToast({ title: "申请成功", icon: "none" })
router.back() router.back()
} catch (error) { } catch (error) {
console.error(error); console.error(error)
uni.hideLoading() uni.hideLoading()
} }
} }
</script> </script>
<template> <template>
<crud-layout title="外来人员就餐" bg="/static/page/blue-bg.png"> <crud-layout title="外来人员就餐" bg="/static/page/blue-bg.png">
<view class="h-30rpx" /> <view class="h-30rpx" />
<view class="card p-x-30rpx p-y-40rpx"> <view class="card p-x-30rpx p-y-40rpx">
<tm-form ref="applyFormRef" v-model="applyForm" :rules="applyFormRules" direction="vertical"> <tm-form ref="applyFormRef" v-model="applyForm" :rules="applyFormRules" direction="vertical">
<tm-form-item name="count" label="申请数量" :showBottom="false"> <tm-form-item name="count" label="申请数量" :showBottom="false">
<tm-input v-model="applyForm.count" type="number" :disabled="state.detailType == 'view'" <tm-input
:showClear="state.detailType != 'view'" placeholder="请输入申请数量.." /> v-model="applyForm.count"
</tm-form-item> type="number"
<view class="h-30rpx" /> :disabled="state.detailType == 'view'"
<tm-form-item name="remark" label="备注" :required="false" :showBottom="false"> :showClear="state.detailType != 'view'"
<tm-input v-model="applyForm.remark" type="textarea" :disabled="state.detailType == 'view'" placeholder="请输入申请数量.."
:showClear="state.detailType != 'view'" />
:placeholder="state.detailType == 'view' ? '' : '请输入备注..'"></tm-input> </tm-form-item>
</tm-form-item> <view class="h-30rpx" />
</tm-form> <tm-form-item name="remark" label="备注" :required="false" :showBottom="false">
</view> <tm-input
<view v-if="state.detailType != 'view'" v-model="applyForm.remark"
class="bg-#0094ff p-y-20rpx text-center rounded-40rpx m-x-30rpx m-t-60rpx sub-text-30 color-white!" type="textarea"
@tap="doSubmit"> :disabled="state.detailType == 'view'"
提交 :showClear="state.detailType != 'view'"
</view> :placeholder="state.detailType == 'view' ? '' : '请输入备注..'"
</crud-layout> ></tm-input>
</tm-form-item>
</tm-form>
</view>
<view
v-if="state.detailType != 'view'"
class="bg-#0094ff p-y-20rpx text-center rounded-40rpx m-x-30rpx m-t-60rpx sub-text-30 color-white!"
@tap="doSubmit"
>
提交
</view>
</crud-layout>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
:deep() { :deep() {
.tmFormItemLabel { .tmFormItemLabel {
padding-bottom: 10rpx !important; padding-bottom: 10rpx !important;
} }
} }
</style> </style>
+1 -1
View File
@@ -19,7 +19,7 @@ const state = reactive<any>({
end: "", end: "",
}, },
}) })
const applyForm = reactive<any>({ let applyForm = reactive<any>({
displayTimeRange: "", displayTimeRange: "",
displayTimeRangeStr: "", displayTimeRangeStr: "",
bxDate: null, bxDate: null,
File diff suppressed because it is too large. Load diff
+364 -393
View File
@@ -1,93 +1,72 @@
<template> <template>
<view class="visitor-list"> <view class="visitor-list">
<!-- 顶部标签页 --> <!-- 顶部标签页 -->
<view class="tabs"> <view class="tabs">
<view <view v-for="(tab, index) in tabs" :key="index" class="tab-item" :class="{ active: currentTab === index }" @tap="switchTab(index)">
v-for="(tab, index) in tabs" <text>{{ tab }}</text>
:key="index" </view>
class="tab-item" </view>
:class="{ active: currentTab === index }"
@tap="switchTab(index)"
>
<text>{{ tab }}</text>
</view>
</view>
<!-- 访客记录列表 --> <!-- 访客记录列表 -->
<scroll-view <scroll-view
scroll-y scroll-y
class="list-content" class="list-content"
:refresher-enabled="true" :refresher-enabled="true"
:refresher-triggered="refreshing" :refresher-triggered="refreshing"
@refresherrefresh="onRefresh" @refresherrefresh="onRefresh"
@scrolltolower="loadMore" @scrolltolower="loadMore"
> >
<view <view v-for="item in visitorList" :key="item.id" class="visitor-item" @tap="goToDetail(item)">
v-for="item in visitorList" <view class="item-header">
:key="item.id" <text class="visit-time">{{ formatTime(item.vstartDate, item.vendDate) }}</text>
class="visitor-item" <text :class="['status-tag', getStatusClass(item.checkStatus)]">
@tap="goToDetail(item)" {{ getStatusText(item.checkStatus) }}
> </text>
<view class="item-header"> </view>
<text class="visit-time">{{ formatTime(item.vstartDate, item.vendDate) }}</text> <view class="item-content">
<text :class="['status-tag', getStatusClass(item.checkStatus)]"> <view class="info-row">
{{ getStatusText(item.checkStatus) }} <text class="label">接待人:</text>
</text> <text class="value">{{ item.userName }}</text>
</view> </view>
<view class="item-content"> <view class="info-row">
<view class="info-row"> <text class="label">联系方式:</text>
<text class="label">接待人:</text> <text class="value">{{ item.mobile }}</text>
<text class="value">{{ item.userName }}</text> </view>
</view> <view class="info-row">
<view class="info-row"> <text class="label">预约时间:</text>
<text class="label">联系方式:</text> <text class="value">{{ formatCreateTime(item.createTime) }}</text>
<text class="value">{{ item.mobile }}</text> </view>
</view> <view v-if="item.description" class="info-row">
<view class="info-row"> <text class="label">备注:</text>
<text class="label">预约时间:</text> <text class="value">{{ item.description }}</text>
<text class="value">{{ formatCreateTime(item.createTime) }}</text> </view>
</view> <view v-if="item.checkReason" class="info-row check-reason">
<view v-if="item.description" class="info-row"> <text class="label">审核意见:</text>
<text class="label">备注:</text> <text class="value">{{ item.checkReason }}</text>
<text class="value">{{ item.description }}</text> </view>
</view> </view>
<view v-if="item.checkReason" class="info-row check-reason"> <view class="item-footer" v-if="canCancel(item.checkStatus) || canVisit(item.checkStatus)">
<text class="label">审核意见:</text> <view class="btn-group">
<text class="value">{{ item.checkReason }}</text> <view v-if="canVisit(item.checkStatus)" class="visit-btn" @tap="handleVisit(item)">已到访</view>
</view> <view v-if="canCancel(item.checkStatus)" class="cancel-btn" @tap="handleCancel(item)">取消</view>
</view> </view>
<view class="item-footer" v-if="canCancel(item.checkStatus) || canVisit(item.checkStatus)"> </view>
<view class="btn-group"> </view>
<view
v-if="canVisit(item.checkStatus)"
class="visit-btn"
@tap="handleVisit(item)"
>已到访</view>
<view
v-if="canCancel(item.checkStatus)"
class="cancel-btn"
@tap="handleCancel(item)"
>取消</view>
</view>
</view>
</view>
<!-- 加载状态 --> <!-- 加载状态 -->
<view v-if="loading && !refreshing" class="loading">加载中...</view> <view v-if="loading && !refreshing" class="loading">加载中...</view>
<view v-if="!loading && !hasMore" class="no-more">没有更多数据了</view> <view v-if="!loading && !hasMore" class="no-more">没有更多数据了</view>
<view v-if="!loading && visitorList.length === 0" class="empty"> <view v-if="!loading && visitorList.length === 0" class="empty">暂无访客记录</view>
暂无访客记录 </scroll-view>
</view> </view>
</scroll-view>
</view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, onMounted } from "vue"
import visitorApi from '@/sheep/api/integrated/visitors' import visitorApi from "@/sheep/api/integrated/visitors"
// 标签页配置 // 标签页配置
const tabs = ['全部', '待审核', '待到访', '已到访', '已取消'] const tabs = ["全部", "待审核", "待到访", "已到访", "已取消"]
const currentTab = ref(0) const currentTab = ref(0)
// 列表数据 // 列表数据
@@ -100,391 +79,383 @@ const size = 10
// 格式化访问时间 // 格式化访问时间
const formatTime = (start: string, end: string) => { const formatTime = (start: string, end: string) => {
if (!start || !end) return '' if (!start || !end) return ""
const startDate = new Date(start) const startDate = new Date(start)
const endDate = new Date(end) const endDate = new Date(end)
return `${startDate.getMonth() + 1}/${startDate.getDate()} ${startDate.getHours()}:00-${endDate.getHours()}:00` return `${startDate.getMonth() + 1}/${startDate.getDate()} ${startDate.getHours()}:00-${endDate.getHours()}:00`
} }
// 格式化创建时间 // 格式化创建时间
const formatCreateTime = (time: string) => { const formatCreateTime = (time: string) => {
if (!time) return '' if (!time) return ""
const date = new Date(time) const date = new Date(time)
return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${date.getMinutes()}` return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${date.getMinutes()}`
} }
// 获取状态文本 // 获取状态文本
const getStatusText = (status: string) => { const getStatusText = (status: string) => {
const statusMap: Record<string, string> = { const statusMap: Record<string, string> = {
'1': '待审核', "1": "待审核",
'2': '待到访', "2": "待到访",
'0': '已到访', "0": "已到访",
'5': '已取消' "5": "已取消",
} }
return statusMap[status] || status return statusMap[status] || status
} }
// 获取状态样式 // 获取状态样式
const getStatusClass = (status: string) => { const getStatusClass = (status: string) => {
const statusMap: Record<string, string> = { const statusMap: Record<string, string> = {
'1': 'status-pending', "1": "status-pending",
'2': 'status-waiting', "2": "status-waiting",
'0': 'status-visited', "0": "status-visited",
'5': 'status-cancelled' "5": "status-cancelled",
} }
return statusMap[status] || '' return statusMap[status] || ""
} }
// 切换标签页 // 切换标签页
const switchTab = (index: number) => { const switchTab = (index: number) => {
if (currentTab.value === index) return if (currentTab.value === index) return
currentTab.value = index currentTab.value = index
resetList() resetList()
getVisitorList() getVisitorList()
} }
// 重置列表状态 // 重置列表状态
const resetList = () => { const resetList = () => {
current.value = 1 current.value = 1
visitorList.value = [] visitorList.value = []
hasMore.value = true hasMore.value = true
} }
// 获取访客记录列表 // 获取访客记录列表
const getVisitorList = async () => { const getVisitorList = async () => {
if (loading.value || (!hasMore.value && !refreshing.value)) return if (loading.value || (!hasMore.value && !refreshing.value)) return
loading.value = true loading.value = true
try {
const params: any = {
current: current.value,
size
}
// 添加状态筛选
if (currentTab.value > 0) {
const statusMap = ['', '1', '2', '0', '5']
params.checkStatus = statusMap[currentTab.value]
}
const { code, data } = await visitorApi.getVisitorList(params) try {
const params: any = {
if (code === 0 && data) { current: current.value,
if (refreshing.value || current.value === 1) { size,
visitorList.value = data.records || [] }
} else {
visitorList.value.push(...(data.records || [])) // 添加状态筛选
} if (currentTab.value > 0) {
const statusMap = ["", "1", "2", "0", "5"]
hasMore.value = (data.records || []).length === size params.checkStatus = statusMap[currentTab.value]
if (hasMore.value) { }
current.value++
} const { code, data } = await visitorApi.getVisitorList(params)
}
} catch (error) { if (code === 0 && data) {
console.error('获取访客记录失败:', error) if (refreshing.value || current.value === 1) {
uni.showToast({ visitorList.value = data.records || []
title: '获取数据失败', } else {
icon: 'none' visitorList.value.push(...(data.records || []))
}) }
} finally {
loading.value = false hasMore.value = (data.records || []).length === size
refreshing.value = false if (hasMore.value) {
} current.value++
}
}
} catch (error) {
console.error("获取访客记录失败:", error)
uni.showToast({
title: "获取数据失败",
icon: "none",
})
} finally {
loading.value = false
refreshing.value = false
}
} }
// 下拉刷新 // 下拉刷新
const onRefresh = async () => { const onRefresh = async () => {
refreshing.value = true refreshing.value = true
resetList() resetList()
await getVisitorList() await getVisitorList()
} }
// 上拉加载更多 // 上拉加载更多
const loadMore = () => { const loadMore = () => {
if (!loading.value && hasMore.value) { if (!loading.value && hasMore.value) {
getVisitorList() getVisitorList()
} }
} }
// 判断是否可以取消 // 判断是否可以取消
const canCancel = (status: string) => { const canCancel = (status: string) => {
return status === '1' || status === '2' || status === '5' return status === "1" || status === "2" || status === "5"
} }
// 判断是否可以标记到访 // 判断是否可以标记到访
const canVisit = (status: string) => { const canVisit = (status: string) => {
return status === '2' return status === "2"
} }
// 修改取消预约方法 // 修改取消预约方法
const handleCancel = (item: any) => { const handleCancel = (item: any) => {
if (!item.id) return if (!item.id) return
uni.showModal({ uni.showModal({
title: '提示', title: "提示",
content: '确定要取消此次预约吗?', content: "确定要取消此次预约吗?",
success: async (res) => { success: async (res) => {
if (res.confirm) { if (res.confirm) {
try { try {
uni.showLoading({ uni.showLoading({
title: '取消中...', title: "取消中...",
mask: true mask: true,
}) })
const { code, msg } = await visitorApi.cancelVisitor(item.id) const { code, msg } = await visitorApi.cancelVisitor(item.id)
uni.hideLoading() uni.hideLoading()
if (code === 0) { if (code === 0) {
uni.showToast({ uni.showToast({
title: '取消成功', title: "取消成功",
icon: 'success' icon: "success",
}) })
// 刷新列表 // 刷新列表
resetList() resetList()
getVisitorList() getVisitorList()
} else { } else {
uni.showToast({ uni.showToast({
title: msg || '取消失败', title: msg || "取消失败",
icon: 'none' icon: "none",
}) })
} }
} catch (error) { } catch (error) {
uni.hideLoading() uni.hideLoading()
uni.showToast({ uni.showToast({
title: '取消失败', title: "取消失败",
icon: 'none' icon: "none",
}) })
} }
} }
} },
}) })
} }
// 处理到访 // 处理到访
const handleVisit = (item: any) => { const handleVisit = (item: any) => {
if (!item.id) return if (!item.id) return
uni.showModal({ uni.showModal({
title: '提示', title: "提示",
content: '确认访客已到访?', content: "确认访客已到访?",
success: async (res) => { success: async (res) => {
if (res.confirm) { if (res.confirm) {
try { try {
uni.showLoading({ uni.showLoading({
title: '处理中...', title: "处理中...",
mask: true mask: true,
}) })
const { code, msg } = await visitorApi.haveVisitor(item.id) const { code, msg } = await visitorApi.haveVisitor(item.id)
uni.hideLoading() uni.hideLoading()
if (code === 0) { if (code === 0) {
uni.showToast({ uni.showToast({
title: '操作成功', title: "操作成功",
icon: 'success' icon: "success",
}) })
// 刷新列表 // 刷新列表
resetList() resetList()
getVisitorList() getVisitorList()
} else { } else {
uni.showToast({ uni.showToast({
title: msg || '操作失败', title: msg || "操作失败",
icon: 'none' icon: "none",
}) })
} }
} catch (error) { } catch (error) {
uni.hideLoading() uni.hideLoading()
uni.showToast({ uni.showToast({
title: '操作失败', title: "操作失败",
icon: 'none' icon: "none",
}) })
} }
} }
} },
}) })
} }
// 跳转到详情页 // 跳转到详情页
const goToDetail = (item: any) => { const goToDetail = (item: any) => {
uni.navigateTo({ uni.navigateTo({
url: `/pages/integrated/visitor/visitorDetail?id=${item.id}&token=${item.token}` url: `/pages/integrated/visitor/visitorDetail?id=${item.id}&token=${item.token}`,
}) })
} }
onMounted(() => { onMounted(() => {
getVisitorList() getVisitorList()
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.visitor-list { .visitor-list {
min-height: 100vh; display: flex;
background: #F7F8FA; flex-direction: column;
} height: 100vh;
background-color: #f5f5f5;
.tabs { .tabs {
display: flex; display: flex;
background: #fff; background-color: #fff;
padding: 0 32rpx; padding: 20rpx 0;
position: fixed; position: sticky;
top: 0; top: 0;
left: 0; z-index: 100;
right: 0; border-bottom: 1rpx solid #eee;
z-index: 1;
}
.tab-item { .tab-item {
flex: 1; flex: 1;
height: 88rpx; text-align: center;
display: flex; padding: 20rpx 0;
align-items: center; font-size: 28rpx;
justify-content: center; color: #666;
font-size: 28rpx; position: relative;
color: #666;
position: relative;
&.active {
color: #1989FA;
font-weight: 500;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 40rpx;
height: 4rpx;
background: #1989FA;
border-radius: 2rpx;
}
}
}
.list-content { &.active {
padding: 108rpx 32rpx 32rpx; color: #1777ff;
} font-weight: bold;
.visitor-item { &::after {
background: #fff; content: "";
border-radius: 12rpx; position: absolute;
padding: 24rpx; bottom: 0;
margin-bottom: 20rpx; left: 50%;
&:active { transform: translateX(-50%);
opacity: 0.8; width: 40rpx;
} height: 4rpx;
} background-color: #1777ff;
border-radius: 2rpx;
}
}
}
}
.item-header { .list-content {
display: flex; flex: 1;
justify-content: space-between; height: calc(100vh - 100rpx);
align-items: center; padding: 20rpx;
margin-bottom: 20rpx; box-sizing: border-box;
} }
.visit-time { .visitor-item {
font-size: 28rpx; background-color: #fff;
color: #333; border-radius: 12rpx;
font-weight: 500; padding: 20rpx;
} margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
.status-tag { .item-header {
font-size: 24rpx; display: flex;
padding: 4rpx 12rpx; justify-content: space-between;
border-radius: 4rpx; align-items: center;
margin-bottom: 20rpx;
&.status-pending {
background: #FFF3E0;
color: #FF9800;
}
&.status-waiting {
background: #E3F2FD;
color: #2196F3;
}
&.status-visited {
background: #E8F5E9;
color: #4CAF50;
}
&.status-cancelled {
background: #EEEEEE;
color: #9E9E9E;
}
}
.info-row { .visit-time {
display: flex; font-size: 28rpx;
margin-bottom: 16rpx; color: #333;
font-weight: bold;
&:last-child { }
margin-bottom: 0;
}
.label {
font-size: 26rpx;
color: #666;
width: 140rpx;
flex-shrink: 0;
}
.value {
font-size: 26rpx;
color: #333;
flex: 1;
}
&.check-reason { .status-tag {
margin-top: 16rpx; padding: 4rpx 16rpx;
padding-top: 16rpx; border-radius: 20rpx;
border-top: 1rpx solid #F5F5F5; font-size: 24rpx;
.value {
color: #FF4D4F;
}
}
}
.item-footer { &.status-pending {
margin-top: 20rpx; background-color: #fff7e6;
padding-top: 20rpx; color: #fa8c16;
border-top: 1rpx solid #F5F5F5; }
}
.btn-group { &.status-waiting {
display: flex; background-color: #e6f7ff;
justify-content: flex-end; color: #1890ff;
gap: 20rpx; }
}
.cancel-btn, .visit-btn { &.status-visited {
padding: 12rpx 32rpx; background-color: #f6ffed;
border-radius: 32rpx; color: #52c41a;
font-size: 26rpx; }
&:active {
opacity: 0.8;
}
}
.cancel-btn { &.status-cancelled {
border: 1rpx solid #DCDEE0; background-color: #fff1f0;
color: #666; color: #f5222d;
} }
}
}
.visit-btn { .item-content {
background: #1989FA; .info-row {
color: #fff; display: flex;
} margin-bottom: 12rpx;
font-size: 26rpx;
.loading, .no-more, .empty { .label {
text-align: center; color: #999;
font-size: 24rpx; width: 140rpx;
color: #999; }
padding: 20rpx 0;
.value {
color: #333;
flex: 1;
}
&.check-reason {
.value {
color: #f5222d;
}
}
}
}
.item-footer {
margin-top: 20rpx;
padding-top: 20rpx;
border-top: 1rpx solid #eee;
.btn-group {
display: flex;
justify-content: flex-end;
gap: 20rpx;
.visit-btn,
.cancel-btn {
padding: 10rpx 30rpx;
border-radius: 30rpx;
font-size: 26rpx;
}
.visit-btn {
background-color: #1777ff;
color: #fff;
}
.cancel-btn {
background-color: #fff;
color: #666;
border: 1rpx solid #ddd;
}
}
}
}
.loading,
.no-more,
.empty {
text-align: center;
padding: 30rpx;
color: #999;
font-size: 26rpx;
}
} }
</style> </style>
File diff suppressed because it is too large. Load diff
+366 -429
View File
@@ -1,105 +1,76 @@
<template> <template>
<view class="page-container"> <view class="page-container">
<!-- 顶部状态切换 --> <!-- 顶部状态切换 -->
<view class="status-tabs"> <view class="status-tabs">
<view <view
v-for="(tab, index) in tabs" v-for="(tab, index) in tabs"
:key="index" :key="index"
:class="['tab-item', { active: currentTab === tab.value }]" :class="['tab-item', { active: currentTab === tab.value }]"
@tap="handleTabChange(tab.value)" @tap="handleTabChange(tab.value)"
> >
{{ tab.label }} {{ tab.label }}
</view> </view>
</view> </view>
<view class="visitor-list"> <view class="visitor-list">
<view <view v-for="(item, index) in visitorList" :key="item.id" class="visitor-item" @tap="goToDetail(item)">
v-for="(item, index) in visitorList" <view class="visitor-header">
:key="item.id" <text class="visit-time">{{ formatDateTime(item.vStartDate) }}</text>
class="visitor-item" <text :class="['status-tag', getStatusClass(item.checkStatus)]">{{ getStatusText(item.checkStatus) }}</text>
@tap="goToDetail(item)" </view>
> <view class="visitor-content">
<view class="visitor-header"> <view class="info-row">
<text class="visit-time">{{ formatDateTime(item.vStartDate) }}</text> <text class="label">访客姓名:</text>
<text :class="['status-tag', getStatusClass(item.checkStatus)]">{{ getStatusText(item.checkStatus) }}</text> <text class="value">{{ item.name }}</text>
</view> </view>
<view class="visitor-content"> <view class="info-row">
<view class="info-row"> <text class="label">手机号码:</text>
<text class="label">访客姓名:</text> <text class="value">{{ item.mobile }}</text>
<text class="value">{{ item.name }}</text> </view>
</view> <view class="info-row">
<view class="info-row"> <text class="label">拜访部门:</text>
<text class="label">手机号码:</text> <text class="value">{{ item.deptName }}</text>
<text class="value">{{ item.mobile }}</text> </view>
</view> <view class="info-row" v-if="item.companyName">
<view class="info-row"> <text class="label">来访公司:</text>
<text class="label">拜访部门:</text> <text class="value">{{ item.companyName }}</text>
<text class="value">{{ item.deptName }}</text> </view>
</view> <view class="info-row">
<view class="info-row" v-if="item.companyName"> <text class="label">拜访时间:</text>
<text class="label">来访公司:</text> <text class="value">{{ formatDateTime(item.vstartDate) }} - {{ formatDateTime(item.vendDate) }}</text>
<text class="value">{{ item.companyName }}</text> </view>
</view> <view class="info-row">
<view class="info-row"> <text class="label">拜访原因:</text>
<text class="label">拜访时间:</text> <text class="value">{{ getReasonText(item.reasonValue) }}</text>
<text class="value">{{ formatDateTime(item.vstartDate) }} - {{ formatDateTime(item.vendDate) }}</text> </view>
</view> </view>
<view class="info-row"> <view class="visitor-footer">
<text class="label">拜访原因:</text> <template v-if="item.checkStatus === '1'">
<text class="value">{{ getReasonText(item.description) }}</text> <view class="action-btn reject-btn" @tap="handleReject(item)">拒绝</view>
</view> <view class="action-btn accept-btn" @tap="handleAccept(item)">接受</view>
</view> </template>
<view class="visitor-footer"> <view v-else-if="canCancel(item.checkStatus)" class="action-btn cancel-btn" @tap="handleCancel(item)">取消邀约</view>
<template v-if="item.checkStatus === '1'"> </view>
<view class="action-btn reject-btn" @tap="handleReject(item)">拒绝</view> </view>
<view class="action-btn accept-btn" @tap="handleAccept(item)">接受</view> </view>
</template>
<view v-else-if="canCancel(item.checkStatus)" class="action-btn cancel-btn" @tap="handleCancel(item)">取消邀约</view>
</view>
</view>
</view>
<!-- 下拉刷新和上拉加载的提示 --> <!-- 下拉刷新和上拉加载的提示 -->
<view class="loading-more" v-if="loading"> <view class="loading-more" v-if="loading">
<text>加载中...</text> <text>加载中...</text>
</view> </view>
<view class="no-more" v-if="!hasMore && visitorList.length > 0"> <view class="no-more" v-if="!hasMore && visitorList.length > 0">
<text>没有更多了</text> <text>没有更多了</text>
</view> </view>
<view class="empty" v-if="!loading && visitorList.length === 0"> <view class="empty" v-if="!loading && visitorList.length === 0">
<text>暂无数据</text> <text>暂无数据</text>
</view> </view>
</view>
<!-- 底部固定导航 -->
<view class="bottom-nav">
<view class="nav-item">
<image src="/static/images/visitor/visitor-apply.png" mode="aspectFit" class="nav-icon"></image>
<text class="nav-text active">访客申请</text>
</view>
<view class="nav-item">
<image src="/static/images/visitor/visitor-record.png" mode="aspectFit" class="nav-icon"></image>
<text class="nav-text">申请记录</text>
</view>
<view class="nav-item">
<image src="/static/images/visitor/visitor-invite.png" mode="aspectFit" class="nav-icon"></image>
<text class="nav-text">主动邀约</text>
</view>
<view class="nav-item">
<image src="/static/images/visitor/visitor-invited.png" mode="aspectFit" class="nav-icon"></image>
<text class="nav-text">邀约记录</text>
</view>
<view class="nav-item">
<image src="/static/images/visitor/visitor-my.png" mode="aspectFit" class="nav-icon"></image>
<text class="nav-text">我的</text>
</view>
</view>
</view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, onMounted } from "vue"
import visitorApi from '@/sheep/api/integrated/visitors' import visitorApi from "@/sheep/api/integrated/visitors"
import dayjs from 'dayjs' import dayjs from "dayjs"
// 定义响应式变量 // 定义响应式变量
const visitorList = ref<any[]>([]) const visitorList = ref<any[]>([])
@@ -107,439 +78,405 @@ const loading = ref(false)
const hasMore = ref(true) const hasMore = ref(true)
const current = ref(1) const current = ref(1)
const size = ref(10) const size = ref(10)
const currentTab = ref('') const currentTab = ref("")
const reasonDict = ref<any[]>([]) const reasonDict = ref<any[]>([])
const tabs = [ const tabs = [
{ label: '全部', value: '' }, { label: "全部", value: "" },
{ label: '待审核', value: '1' }, { label: "待审核", value: "1" },
{ label: '待到访', value: '2' }, { label: "待到访", value: "2" },
{ label: '已取消', value: '5' } { label: "已取消", value: "5" },
] ]
// 获取拜访原因字典 // 获取拜访原因字典
const getReasonDict = async () => { const getReasonDict = async () => {
try { try {
const { code, data } = await visitorApi.getDictListByType(1) const { code, data } = await visitorApi.getDictListByType(1)
if (code === 0 && data) { if (code === 0 && data) {
reasonDict.value = data reasonDict.value = data
} }
} catch (error) { } catch (error) {
console.error('获取拜访原因字典失败:', error) console.error("获取拜访原因字典失败:", error)
} }
} }
// 获取拜访原因文本 // 获取拜访原因文本
const getReasonText = (value: string) => { const getReasonText = (value: string) => {
const reason = reasonDict.value.find(item => item.value === value) const reason = reasonDict.value.find((item) => item.value === value)
return reason ? reason.label : value return reason ? reason.label : value
} }
// 格式化日期时间 // 格式化日期时间
const formatDateTime = (dateStr: string) => { const formatDateTime = (dateStr: string) => {
if (!dateStr) return '' if (!dateStr) return ""
return dayjs(dateStr).format('YYYY/MM/DD HH:mm') return dayjs(dateStr).format("YYYY/MM/DD HH:mm")
} }
// 获取状态文本 // 获取状态文本
const getStatusText = (status: string) => { const getStatusText = (status: string) => {
const statusMap: { [key: string]: string } = { const statusMap: { [key: string]: string } = {
'1': '待审核', "1": "待审核",
'2': '待到访', "2": "待到访",
'5': '已取消', "5": "已取消",
'0': '已到访' "0": "已到访",
} }
return statusMap[status] || '' return statusMap[status] || ""
} }
// 获取状态样式类 // 获取状态样式类
const getStatusClass = (status: string) => { const getStatusClass = (status: string) => {
const statusClassMap: { [key: string]: string } = { const statusClassMap: { [key: string]: string } = {
'1': 'status-pending', "1": "status-pending",
'2': 'status-waiting', "2": "status-waiting",
'5': 'status-cancelled', "5": "status-cancelled",
'0': 'status-visited' "0": "status-visited",
} }
return statusClassMap[status] || '' return statusClassMap[status] || ""
} }
// 判断是否可以取消 // 判断是否可以取消
const canCancel = (status: string) => { const canCancel = (status: string) => {
return ['1', '2'].includes(status) return ["1", "2"].includes(status)
} }
// 处理标签切换 // 处理标签切换
const handleTabChange = (value: string) => { const handleTabChange = (value: string) => {
if (currentTab.value === value) return if (currentTab.value === value) return
currentTab.value = value currentTab.value = value
getVisitorList(true) getVisitorList(true)
} }
// 获取访客列表 // 获取访客列表
const getVisitorList = async (isRefresh = false) => { const getVisitorList = async (isRefresh = false) => {
if (isRefresh) { if (isRefresh) {
current.value = 1 current.value = 1
hasMore.value = true hasMore.value = true
visitorList.value = [] visitorList.value = []
} }
if (!hasMore.value || loading.value) return if (!hasMore.value || loading.value) return
loading.value = true loading.value = true
try { try {
// 确保先获取拜访原因字典 // 确保先获取拜访原因字典
if (reasonDict.value.length === 0) { if (reasonDict.value.length === 0) {
await getReasonDict() await getReasonDict()
} }
const { code, data } = await visitorApi.getVisitorYYList({ const { code, data } = await visitorApi.getVisitorYYList({
current: current.value, current: current.value,
size: size.value, size: size.value,
checkStatus: currentTab.value || undefined checkStatus: currentTab.value || undefined,
}) })
if (code === 0 && data) { if (code === 0 && data) {
if (isRefresh) { if (isRefresh) {
visitorList.value = data.records || [] visitorList.value = data.records || []
} else { } else {
visitorList.value = [...visitorList.value, ...(data.records || [])] visitorList.value = [...visitorList.value, ...(data.records || [])]
} }
hasMore.value = data.records.length === size.value hasMore.value = data.records.length === size.value
if (hasMore.value) { if (hasMore.value) {
current.value++ current.value++
} }
} }
} catch (error) { } catch (error) {
console.error('获取访客列表失败:', error) console.error("获取访客列表失败:", error)
} finally { } finally {
loading.value = false loading.value = false
} }
} }
// 处理取消邀约 // 处理取消邀约
const handleCancel = async (item: any) => { const handleCancel = async (item: any) => {
uni.showModal({ uni.showModal({
title: '提示', title: "提示",
content: '确定要取消该邀约吗?', content: "确定要取消该邀约吗?",
success: async (res) => { success: async (res) => {
if (res.confirm) { if (res.confirm) {
try { try {
const { code, msg } = await visitorApi.cancelVisitor(item.id) const { code, msg } = await visitorApi.cancelVisitor(item.id)
if (code === 0) { if (code === 0) {
uni.showToast({ uni.showToast({
title: '取消成功', title: "取消成功",
icon: 'success' icon: "success",
}) })
// 刷新列表 // 刷新列表
getVisitorList(true) getVisitorList(true)
} else { } else {
uni.showToast({ uni.showToast({
title: msg || '取消失败', title: msg || "取消失败",
icon: 'none' icon: "none",
}) })
} }
} catch (error) { } catch (error) {
console.error('取消邀约失败:', error) console.error("取消邀约失败:", error)
uni.showToast({ uni.showToast({
title: '取消失败', title: "取消失败",
icon: 'none' icon: "none",
}) })
} }
} }
} },
}) })
} }
// 处理接受邀约 // 处理接受邀约
const handleAccept = async (item: any) => { const handleAccept = async (item: any) => {
uni.showModal({ uni.showModal({
title: '提示', title: "提示",
content: '确定接受该邀约吗?', content: "确定接受该邀约吗?",
success: async (res) => { success: async (res) => {
if (res.confirm) { if (res.confirm) {
try { try {
const { code, msg } = await visitorApi.updateCheckStatus({ const { code, msg } = await visitorApi.updateCheckStatus({
id: item.id, id: item.id,
checkStatus: '2' checkStatus: "2",
}) })
if (code === 0) { if (code === 0) {
uni.showToast({ uni.showToast({
title: '已接受', title: "已接受",
icon: 'success' icon: "success",
}) })
// 刷新列表 // 刷新列表
getVisitorList(true) getVisitorList(true)
} else { } else {
uni.showToast({ uni.showToast({
title: msg || '操作失败', title: msg || "操作失败",
icon: 'none' icon: "none",
}) })
} }
} catch (error) { } catch (error) {
console.error('接受邀约失败:', error) console.error("接受邀约失败:", error)
uni.showToast({ uni.showToast({
title: '操作失败', title: "操作失败",
icon: 'none' icon: "none",
}) })
} }
} }
} },
}) })
} }
// 处理拒绝邀约 // 处理拒绝邀约
const handleReject = async (item: any) => { const handleReject = async (item: any) => {
uni.showModal({ uni.showModal({
title: '提示', title: "提示",
content: '确定拒绝该邀约吗?', content: "确定拒绝该邀约吗?",
success: async (res) => { success: async (res) => {
if (res.confirm) { if (res.confirm) {
try { try {
const { code, msg } = await visitorApi.updateCheckStatus({ const { code, msg } = await visitorApi.updateCheckStatus({
id: item.id, id: item.id,
checkStatus: '3' checkStatus: "3",
}) })
if (code === 0) { if (code === 0) {
uni.showToast({ uni.showToast({
title: '已拒绝', title: "已拒绝",
icon: 'success' icon: "success",
}) })
// 刷新列表 // 刷新列表
getVisitorList(true) getVisitorList(true)
} else { } else {
uni.showToast({ uni.showToast({
title: msg || '操作失败', title: msg || "操作失败",
icon: 'none' icon: "none",
}) })
} }
} catch (error) { } catch (error) {
console.error('拒绝邀约失败:', error) console.error("拒绝邀约失败:", error)
uni.showToast({ uni.showToast({
title: '操作失败', title: "操作失败",
icon: 'none' icon: "none",
}) })
} }
} }
} },
}) })
} }
// 跳转到详情页 // 跳转到详情页
const goToDetail = (item: any) => { const goToDetail = (item: any) => {
uni.navigateTo({ uni.navigateTo({
url: `/pages/integrated/visitor/visitorDetail?token=${item.token}` url: `/pages/integrated/visitor/visitorDetail?token=${item.token}`,
}) })
} }
// 页面生命周期钩子 // 页面生命周期钩子
onMounted(() => { onMounted(() => {
getReasonDict() getReasonDict()
getVisitorList(true) getVisitorList(true)
}) })
// 页面配置 // 页面配置
defineOptions({ defineOptions({
navigationBarTitleText: '我的邀约', navigationBarTitleText: "我的邀约",
enablePullDownRefresh: true, enablePullDownRefresh: true,
backgroundColor: '#f5f5f5' backgroundColor: "#f5f5f5",
}) })
// 下拉刷新 // 下拉刷新
defineExpose({ defineExpose({
onPullDownRefresh() { onPullDownRefresh() {
getVisitorList(true).then(() => { getVisitorList(true).then(() => {
uni.stopPullDownRefresh() uni.stopPullDownRefresh()
}) })
}, },
onReachBottom() { onReachBottom() {
getVisitorList() getVisitorList()
} },
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.page-container { .page-container {
min-height: 100vh; min-height: 100vh;
background-color: #f5f5f5; background-color: #f5f5f5;
padding-bottom: 120rpx; // 为底部导航留出空间 padding-bottom: 120rpx; // 为底部导航留出空间
} }
.status-tabs { .status-tabs {
display: flex; display: flex;
background-color: #fff; background-color: #fff;
padding: 20rpx 30rpx; padding: 20rpx 30rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
.tab-item { .tab-item {
flex: 1; flex: 1;
text-align: center; text-align: center;
font-size: 28rpx; font-size: 28rpx;
color: #666; color: #666;
position: relative; position: relative;
padding: 20rpx 0; padding: 20rpx 0;
&.active { &.active {
color: #1777FF; color: #1777ff;
font-weight: 500; font-weight: 500;
&::after { &::after {
content: ''; content: "";
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
width: 40rpx; width: 40rpx;
height: 4rpx; height: 4rpx;
background-color: #1777FF; background-color: #1777ff;
border-radius: 2rpx; border-radius: 2rpx;
} }
} }
} }
} }
.visitor-list { .visitor-list {
padding: 0 20rpx; padding: 0 20rpx;
padding-bottom: 20rpx; // 为底部添加一些间距 padding-bottom: 20rpx; // 为底部添加一些间距
.visitor-item { .visitor-item {
background-color: #fff; background-color: #fff;
border-radius: 12rpx; border-radius: 12rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
padding: 24rpx; padding: 24rpx;
.visitor-header { .visitor-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 20rpx; margin-bottom: 20rpx;
.visit-time { .visit-time {
font-size: 28rpx; font-size: 28rpx;
color: #333; color: #333;
} }
.status-tag { .status-tag {
padding: 4rpx 16rpx; padding: 4rpx 16rpx;
border-radius: 4rpx; border-radius: 4rpx;
font-size: 24rpx; font-size: 24rpx;
&.status-pending { &.status-pending {
background-color: #fff7e6; background-color: #fff7e6;
color: #ffa940; color: #ffa940;
} }
&.status-waiting { &.status-waiting {
background-color: #e6f7ff; background-color: #e6f7ff;
color: #1890ff; color: #1890ff;
} }
&.status-visited { &.status-visited {
background-color: #f6ffed; background-color: #f6ffed;
color: #52c41a; color: #52c41a;
} }
&.status-cancelled { &.status-cancelled {
background-color: #f5f5f5; background-color: #f5f5f5;
color: #999; color: #999;
} }
} }
} }
.visitor-content { .visitor-content {
.info-row { .info-row {
display: flex; display: flex;
margin-bottom: 16rpx; margin-bottom: 16rpx;
.label { .label {
width: 160rpx; width: 160rpx;
color: #666; color: #666;
font-size: 28rpx; font-size: 28rpx;
} }
.value { .value {
flex: 1; flex: 1;
color: #333; color: #333;
font-size: 28rpx; font-size: 28rpx;
} }
} }
} }
.visitor-footer { .visitor-footer {
margin-top: 20rpx; margin-top: 20rpx;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
gap: 20rpx; gap: 20rpx;
.action-btn { .action-btn {
padding: 12rpx 24rpx; padding: 12rpx 24rpx;
border: 1rpx solid #ddd; border: 1rpx solid #ddd;
border-radius: 4rpx; border-radius: 4rpx;
font-size: 26rpx; font-size: 26rpx;
&:active { &:active {
opacity: 0.8; opacity: 0.8;
} }
} }
.cancel-btn { .cancel-btn {
color: #666; color: #666;
} }
.reject-btn { .reject-btn {
color: #ff4d4f; color: #ff4d4f;
border-color: #ff4d4f; border-color: #ff4d4f;
} }
.accept-btn { .accept-btn {
color: #fff; color: #fff;
background-color: #1777FF; background-color: #1777ff;
border-color: #1777FF; border-color: #1777ff;
} }
} }
} }
} }
.loading-more, .no-more, .empty { .loading-more,
text-align: center; .no-more,
padding: 30rpx; .empty {
color: #999; text-align: center;
font-size: 26rpx; padding: 30rpx;
} color: #999;
font-size: 26rpx;
.bottom-nav {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 100rpx;
background-color: #fff;
display: flex;
justify-content: space-around;
align-items: center;
border-top: 1rpx solid #eee;
padding-bottom: env(safe-area-inset-bottom); // 适配iPhone底部安全区
.nav-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 10rpx 0;
.nav-icon {
width: 44rpx;
height: 44rpx;
margin-bottom: 4rpx;
}
.nav-text {
font-size: 20rpx;
color: #999;
&.active {
color: #1777FF;
}
}
}
} }
</style> </style>
+138 -184
View File
@@ -1,226 +1,180 @@
<template> <template>
<view class="verify-page"> <view class="verify-page">
<!-- 顶部导航 --> <!-- 扫码按钮 -->
<view class="header"> <view class="scan-section">
<view class="back-btn" @tap="goBack"> <view class="scan-btn" @tap="handleScan">
<text class="back-icon">〈</text> <text class="scan-icon">📷</text>
</view> <text class="scan-text">扫描访客二维码</text>
<view class="title">访客核销</view> </view>
<view class="right-placeholder"></view> </view>
</view>
<!-- 扫码按钮 --> <!-- 最近核销记录 -->
<view class="scan-section"> <view class="verify-history" v-if="recentVerifyList.length > 0">
<view class="scan-btn" @tap="handleScan"> <view class="section-title">最近核销记录</view>
<text class="scan-icon">📷</text> <view class="history-list">
<text class="scan-text">扫描访客二维码</text> <view v-for="(item, index) in recentVerifyList" :key="index" class="history-item">
</view> <view class="visitor-info">
</view> <text class="name">{{ item.name }}</text>
<text class="time">{{ formatDateTime(item.verifyTime) }}</text>
<!-- 最近核销记录 --> </view>
<view class="verify-history" v-if="recentVerifyList.length > 0"> <view class="visit-info">
<view class="section-title">最近核销记录</view> <text class="dept">{{ item.deptName }}</text>
<view class="history-list"> <text class="status success">已核销</text>
<view v-for="(item, index) in recentVerifyList" :key="index" class="history-item"> </view>
<view class="visitor-info"> </view>
<text class="name">{{ item.name }}</text> </view>
<text class="time">{{ formatDateTime(item.verifyTime) }}</text> </view>
</view> </view>
<view class="visit-info">
<text class="dept">{{ item.deptName }}</text>
<text class="status success">已核销</text>
</view>
</view>
</view>
</view>
</view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from "vue"
import visitorApi from '@/sheep/api/integrated/visitors' import visitorApi from "@/sheep/api/integrated/visitors"
import dayjs from 'dayjs' import dayjs from "dayjs"
const recentVerifyList = ref<any[]>([]) const recentVerifyList = ref<any[]>([])
// 格式化时间 // 格式化时间
const formatDateTime = (time: string) => { const formatDateTime = (time: string) => {
if (!time) return '' if (!time) return ""
return dayjs(time).format('YYYY-MM-DD HH:mm') return dayjs(time).format("YYYY-MM-DD HH:mm")
} }
// 处理扫码 // 处理扫码
const handleScan = () => { const handleScan = () => {
uni.scanCode({ uni.scanCode({
scanType: ['qrCode'], scanType: ["qrCode"],
success: async (res) => { success: async (res) => {
try { try {
// 假设扫描结果就是token // 假设扫描结果就是token
const token = res.result const token = res.result
const { code, msg } = await visitorApi.verifyVisitor({ token }) const { code, msg }: any = await visitorApi.verifyVisitor({ token })
if (code === 0) {
uni.showToast({
title: '核销成功',
icon: 'success'
})
// 刷新最近核销记录
// getRecentVerifyList()
} else {
uni.showToast({
title: msg || '核销失败',
icon: 'none'
})
}
} catch (error) {
uni.showToast({
title: '核销失败',
icon: 'none'
})
}
},
fail: () => {
uni.showToast({
title: '扫码失败',
icon: 'none'
})
}
})
}
// 返回上一页 if (code === 0) {
const goBack = () => { uni.showToast({
uni.navigateBack() title: "核销成功",
icon: "success",
})
// 刷新最近核销记录
// getRecentVerifyList()
} else {
uni.showToast({
title: msg || "核销失败",
icon: "none",
})
}
} catch (error) {
uni.showToast({
title: "核销失败",
icon: "none",
})
}
},
fail: () => {
uni.showToast({
title: "扫码失败",
icon: "none",
})
},
})
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.verify-page { .verify-page {
min-height: 100vh; min-height: 100vh;
background: #f5f5f5; background: #f5f5f5;
}
.header {
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
height: 90rpx;
padding: 0 30rpx;
border-bottom: 1rpx solid #f5f5f5;
.back-btn {
width: 60rpx;
height: 90rpx;
display: flex;
align-items: center;
}
.back-icon {
font-size: 38rpx;
color: #323233;
}
.title {
font-size: 36rpx;
color: #323233;
font-weight: 500;
}
.right-placeholder {
width: 60rpx;
}
} }
.scan-section { .scan-section {
padding: 60rpx 30rpx; padding: 60rpx 30rpx;
.scan-btn { .scan-btn {
background: #fff; background: #fff;
height: 200rpx; height: 200rpx;
border-radius: 16rpx; border-radius: 16rpx;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05); box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
&:active { &:active {
opacity: 0.8; opacity: 0.8;
} }
.scan-icon { .scan-icon {
font-size: 60rpx; font-size: 60rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
} }
.scan-text { .scan-text {
font-size: 32rpx; font-size: 32rpx;
color: #323233; color: #323233;
} }
} }
} }
.verify-history { .verify-history {
padding: 0 30rpx; padding: 0 30rpx;
.section-title { .section-title {
font-size: 32rpx; font-size: 32rpx;
color: #323233; color: #323233;
font-weight: 500; font-weight: 500;
margin-bottom: 20rpx; margin-bottom: 20rpx;
} }
.history-list { .history-list {
background: #fff; background: #fff;
border-radius: 16rpx; border-radius: 16rpx;
padding: 0 20rpx; padding: 0 20rpx;
} }
.history-item { .history-item {
padding: 20rpx 0; padding: 20rpx 0;
border-bottom: 1rpx solid #f5f5f5; border-bottom: 1rpx solid #f5f5f5;
&:last-child { &:last-child {
border-bottom: none; border-bottom: none;
} }
.visitor-info { .visitor-info {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 12rpx; margin-bottom: 12rpx;
.name { .name {
font-size: 30rpx; font-size: 30rpx;
color: #323233; color: #323233;
font-weight: 500; font-weight: 500;
} }
.time { .time {
font-size: 26rpx; font-size: 26rpx;
color: #969799; color: #969799;
} }
} }
.visit-info { .visit-info {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
.dept { .dept {
font-size: 28rpx; font-size: 28rpx;
color: #666; color: #666;
} }
.status { .status {
font-size: 26rpx; font-size: 26rpx;
&.success { &.success {
color: #07C160; color: #07c160;
} }
} }
} }
} }
} }
</style> </style>
+379 -379
View File
@@ -1,486 +1,486 @@
<template> <template>
<view class="detail-page"> <view class="detail-page">
<!-- 顶部导航 --> <!-- 顶部导航 -->
<view class="header"> <view class="header">
<view class="back-btn" @tap="goBack"> <view class="back-btn" @tap="goBack">
<text class="back-icon">〈</text> <text class="back-icon">〈</text>
</view> </view>
<view class="title">详情</view> <view class="title">详情</view>
<view class="right-placeholder"></view> <view class="right-placeholder"></view>
</view> </view>
<!-- 访客卡片 --> <!-- 访客卡片 -->
<view class="visit-card"> <view class="visit-card">
<!-- 公司Logo --> <!-- 公司Logo -->
<view class="company-logo"> <view class="company-logo">
<image src="/static/images/company-logo.png" mode="aspectFit"></image> <image src="/static/images/company-logo.png" mode="aspectFit"></image>
</view> </view>
<!-- 访问信息 --> <!-- 访问信息 -->
<view class="visit-info"> <view class="visit-info">
<view class="visit-time">{{ formatDate(detail.vstartDate) }} {{ formatTime(detail.vstartDate) }}</view> <view class="visit-time">{{ formatDate(detail.vstartDate) }} {{ formatTime(detail.vstartDate) }}</view>
<view class="visit-location">{{ detail.deptName }}</view> <view class="visit-location">{{ detail.deptName }}</view>
<view class="visit-address"></view> <view class="visit-address"></view>
</view> </view>
<!-- 访客信息 --> <!-- 访客信息 -->
<view class="visitor-info"> <view class="visitor-info">
<view class="info-row"> <view class="info-row">
<text class="label">邀请人</text> <text class="label">邀请人</text>
<text class="value">{{ detail.name }}</text> <text class="value">{{ detail.name }}</text>
</view> </view>
<view class="info-row"> <view class="info-row">
<text class="label">访客</text> <text class="label">访客</text>
<text class="value">{{ detail.userName }}</text> <text class="value">{{ detail.userName }}</text>
</view> </view>
</view> </view>
<!-- 二维码 --> <!-- 二维码 -->
<view class="qr-code"> <view class="qr-code">
<canvas type="2d" id="qrcode" canvas-id="qrcode" class="qr-canvas"></canvas> <canvas type="2d" id="qrcode" canvas-id="qrcode" class="qr-canvas"></canvas>
<text class="qr-tip">请于门卫或前台进行预访登记</text> <text class="qr-tip">请于门卫或前台进行预访登记</text>
</view> </view>
</view> </view>
<!-- 展开更多按钮 --> <!-- 展开更多按钮 -->
<view class="expand-btn" @tap="toggleExpand"> <view class="expand-btn" @tap="toggleExpand">
<text>{{ isExpanded ? '收起' : '查看详细信息' }}</text> <text>{{ isExpanded ? "收起" : "查看详细信息" }}</text>
<text :class="['expand-icon', { 'expanded': isExpanded }]">{{ isExpanded ? '∧' : '∨' }}</text> <text :class="['expand-icon', { expanded: isExpanded }]">{{ isExpanded ? "∧" : "∨" }}</text>
</view> </view>
<!-- 详细信息 --> <!-- 详细信息 -->
<view class="detail-content" v-show="isExpanded"> <view class="detail-content" v-show="isExpanded">
<!-- 原有的详细信息内容 --> <!-- 原有的详细信息内容 -->
<view class="detail-section"> <view class="detail-section">
<view class="section-title">基本信息</view> <view class="section-title">基本信息</view>
<view class="section-item"> <view class="section-item">
<text class="label">拜访时间</text> <text class="label">拜访时间</text>
<text class="value">{{ formatDateTime(detail.vstartDate) }} - {{ formatDateTime(detail.vendDate, true) }}</text> <text class="value">{{ formatDateTime(detail.vstartDate) }} - {{ formatDateTime(detail.vendDate, true) }}</text>
</view> </view>
<view class="section-item"> <view class="section-item">
<text class="label">访客姓名</text> <text class="label">访客姓名</text>
<text class="value">{{ detail.name }}</text> <text class="value">{{ detail.name }}</text>
</view> </view>
<view class="section-item"> <view class="section-item">
<text class="label">联系方式</text> <text class="label">联系方式</text>
<text class="value">{{ detail.mobile }}</text> <text class="value">{{ detail.mobile }}</text>
</view> </view>
<view class="section-item"> <view class="section-item">
<text class="label">所属公司</text> <text class="label">所属公司</text>
<text class="value">{{ detail.companyName }}</text> <text class="value">{{ detail.companyName }}</text>
</view> </view>
<view class="section-item"> <view class="section-item">
<text class="label">拜访对象</text> <text class="label">拜访对象</text>
<text class="value">{{ detail.userName }}</text> <text class="value">{{ detail.userName }}</text>
</view> </view>
</view> </view>
<!-- 随行人员信息 --> <!-- 随行人员信息 -->
<view class="detail-section" v-if="detail.isOtherPeople === '1' && detail.visitorsPeopleAppVos?.length"> <view class="detail-section" v-if="detail.isOtherPeople === '1' && detail.visitorsPeopleAppVos?.length">
<view class="section-title">随行人员信息</view> <view class="section-title">随行人员信息</view>
<view v-for="(person, index) in detail.visitorsPeopleAppVos" :key="index" class="companion-item"> <view v-for="(person, index) in detail.visitorsPeopleAppVos" :key="index" class="companion-item">
<view class="section-item"> <view class="section-item">
<text class="label">访客姓名</text> <text class="label">访客姓名</text>
<text class="value">{{ person.name }}</text> <text class="value">{{ person.name }}</text>
</view> </view>
<view class="section-item"> <view class="section-item">
<text class="label">手机号码</text> <text class="label">手机号码</text>
<text class="value">{{ person.mobile }}</text> <text class="value">{{ person.mobile }}</text>
</view> </view>
</view> </view>
</view> </view>
<!-- 车辆信息 --> <!-- 车辆信息 -->
<view class="detail-section" v-if="detail.isDrive === '1' && detail.visitorsCarAppVos?.length"> <view class="detail-section" v-if="detail.isDrive === '1' && detail.visitorsCarAppVos?.length">
<view class="section-title">车辆信息</view> <view class="section-title">车辆信息</view>
<view v-for="(car, index) in detail.visitorsCarAppVos" :key="index" class="section-item"> <view v-for="(car, index) in detail.visitorsCarAppVos" :key="index" class="section-item">
<text class="label">车牌号</text> <text class="label">车牌号</text>
<text class="value">{{ car.carNumber }}</text> <text class="value">{{ car.carNumber }}</text>
</view> </view>
</view> </view>
<!-- 审核信息 --> <!-- 审核信息 -->
<view class="detail-section" v-if="detail.checkStatus === '1' && detail.checkReason"> <view class="detail-section" v-if="detail.checkStatus === '1' && detail.checkReason">
<view class="section-title">审核信息</view> <view class="section-title">审核信息</view>
<view class="section-item"> <view class="section-item">
<text class="label">审核意见</text> <text class="label">审核意见</text>
<text class="value">{{ detail.checkReason }}</text> <text class="value">{{ detail.checkReason }}</text>
</view> </view>
</view> </view>
<!-- 提交信息 --> <!-- 提交信息 -->
<view class="detail-section"> <view class="detail-section">
<view class="section-item"> <view class="section-item">
<text class="label">提交时间</text> <text class="label">提交时间</text>
<text class="value">{{ formatDateTime(detail.createTime) }}</text> <text class="value">{{ formatDateTime(detail.createTime) }}</text>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, nextTick } from 'vue' import { ref, onMounted, nextTick } from "vue"
import visitorApi from '@/sheep/api/integrated/visitors' import visitorApi from "@/sheep/api/integrated/visitors"
import dayjs from 'dayjs' import dayjs from "dayjs"
import QRCode from 'qrcode' import QRCode from "qrcode"
const detail = ref<any>({}) const detail = ref<any>({})
const isExpanded = ref(false) const isExpanded = ref(false)
// 格式化日期(年月日) // 格式化日期(年月日)
const formatDate = (time: string) => { const formatDate = (time: string) => {
if (!time) return '' if (!time) return ""
const weekMap: { [key: string]: string } = { const weekMap: { [key: string]: string } = {
'Mon': '一', Mon: "一",
'Tue': '二', Tue: "二",
'Wed': '三', Wed: "三",
'Thu': '四', Thu: "四",
'Fri': '五', Fri: "五",
'Sat': '六', Sat: "六",
'Sun': '日' Sun: "日",
} }
const date = dayjs(time) const date = dayjs(time)
const weekEn = date.format('ddd') const weekEn = date.format("ddd")
const weekCn = weekMap[weekEn] || weekEn const weekCn = weekMap[weekEn] || weekEn
return `${date.format('M月D日')} 周${weekCn}` return `${date.format("M月D日")} 周${weekCn}`
} }
// 格式化时间(时分) // 格式化时间(时分)
const formatTime = (time: string) => { const formatTime = (time: string) => {
if (!time) return '' if (!time) return ""
return dayjs(time).format('HH:mm') return dayjs(time).format("HH:mm")
} }
// 格式化完整时间 // 格式化完整时间
const formatDateTime = (time: string, onlyTime = false) => { const formatDateTime = (time: string, onlyTime = false) => {
if (!time) return '' if (!time) return ""
return onlyTime ? dayjs(time).format('HH:mm') : dayjs(time).format('YYYY-MM-DD HH:mm') return onlyTime ? dayjs(time).format("HH:mm") : dayjs(time).format("YYYY-MM-DD HH:mm")
} }
// 生成二维码 // 生成二维码
const generateQRCode = async (token: string) => { const generateQRCode = async (token: string) => {
try { try {
// #ifdef H5 // #ifdef H5
const base64 = await QRCode.toDataURL(token, { const base64 = await QRCode.toDataURL(token, {
width: 200, width: 200,
margin: 2, margin: 2,
color: { color: {
dark: '#000000', dark: "#000000",
light: '#ffffff' light: "#ffffff",
} },
}) })
if (base64) { if (base64) {
const img = new Image() const img = new Image()
img.src = base64 img.src = base64
img.style.width = '400rpx' img.style.width = "400rpx"
img.style.height = '400rpx' img.style.height = "400rpx"
const container = document.querySelector('.qr-code') const container = document.querySelector(".qr-code")
const canvas = document.querySelector('#qrcode') const canvas = document.querySelector("#qrcode")
if (container && canvas) { if (container && canvas) {
const div = document.createElement('div') const div = document.createElement("div")
div.style.width = '400rpx' div.style.width = "400rpx"
div.style.height = '400rpx' div.style.height = "400rpx"
div.style.marginBottom = '20rpx' div.style.marginBottom = "20rpx"
div.appendChild(img) div.appendChild(img)
container.replaceChild(div, canvas) container.replaceChild(div, canvas)
} }
} }
// #endif // #endif
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
const query = uni.createSelectorQuery() const query = uni.createSelectorQuery()
query query
.select('#qrcode') .select("#qrcode")
.fields({ node: true, size: true }) .fields({ node: true, size: true })
.exec((res) => { .exec((res) => {
if (res[0] && res[0].node) { if (res[0] && res[0].node) {
const canvas = res[0].node const canvas = res[0].node
const ctx = canvas.getContext('2d') const ctx = canvas.getContext("2d")
// 设置canvas大小 // 设置canvas大小
const dpr = uni.getSystemInfoSync().pixelRatio const dpr = uni.getSystemInfoSync().pixelRatio
canvas.width = 200 * dpr canvas.width = 200 * dpr
canvas.height = 200 * dpr canvas.height = 200 * dpr
ctx.scale(dpr, dpr) ctx.scale(dpr, dpr)
// 生成二维码 // 生成二维码
QRCode.toCanvas(canvas, token, { QRCode.toCanvas(canvas, token, {
width: 200, width: 200,
margin: 2, margin: 2,
color: { color: {
dark: '#000000', dark: "#000000",
light: '#ffffff' light: "#ffffff",
} },
}) })
} }
}) })
// #endif // #endif
} catch (error) { } catch (error) {
console.error('生成二维码失败:', error) console.error("生成二维码失败:", error)
} }
} }
// 展开/收起详情 // 展开/收起详情
const toggleExpand = () => { const toggleExpand = () => {
isExpanded.value = !isExpanded.value isExpanded.value = !isExpanded.value
} }
// 获取详情数据 // 获取详情数据
const getDetail = async () => { const getDetail = async () => {
try { try {
const pages = getCurrentPages() const pages = getCurrentPages()
const currentPage = pages[pages.length - 1] const currentPage = pages[pages.length - 1]
const { token } = currentPage.$page?.options || {} const { token } = currentPage.$page?.options || {}
if (!token) { if (!token) {
uni.showToast({ uni.showToast({
title: '参数错误', title: "参数错误",
icon: 'none' icon: "none",
}) })
return return
} }
const { code, data } = await visitorApi.getVisitorDetail({ token }) const { code, data } = await visitorApi.getVisitorDetail({ token })
if (code === 0 && data) { if (code === 0 && data) {
detail.value = data detail.value = data
// 延迟生成二维码,确保canvas已经渲染 // 延迟生成二维码,确保canvas已经渲染
// #ifdef H5 // #ifdef H5
nextTick(() => { nextTick(() => {
generateQRCode(token) generateQRCode(token)
}) })
// #endif // #endif
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
setTimeout(() => { setTimeout(() => {
generateQRCode(token) generateQRCode(token)
}, 300) }, 300)
// #endif // #endif
} }
} catch (error) { } catch (error) {
console.error('获取详情失败:', error) console.error("获取详情失败:", error)
uni.showToast({ uni.showToast({
title: '获取详情失败', title: "获取详情失败",
icon: 'none' icon: "none",
}) })
} }
} }
// 返回上一页 // 返回上一页
const goBack = () => { const goBack = () => {
uni.navigateBack() uni.navigateBack()
} }
onMounted(() => { onMounted(() => {
getDetail() getDetail()
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.detail-page { .detail-page {
min-height: 100vh; min-height: 100vh;
background: #f5f5f5; background: #f5f5f5;
} }
.header { .header {
background: #fff; background: #fff;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
height: 90rpx; height: 90rpx;
padding: 0 30rpx; padding: 0 30rpx;
border-bottom: 1rpx solid #f5f5f5; border-bottom: 1rpx solid #f5f5f5;
.back-btn { .back-btn {
width: 60rpx; width: 60rpx;
height: 90rpx; height: 90rpx;
display: flex; display: flex;
align-items: center; align-items: center;
} }
.back-icon { .back-icon {
font-size: 38rpx; font-size: 38rpx;
color: #323233; color: #323233;
} }
.title { .title {
font-size: 36rpx; font-size: 36rpx;
color: #323233; color: #323233;
font-weight: 500; font-weight: 500;
} }
.right-placeholder { .right-placeholder {
width: 60rpx; width: 60rpx;
} }
} }
.visit-card { .visit-card {
margin: 30rpx; margin: 30rpx;
padding: 40rpx; padding: 40rpx;
background: #fff; background: #fff;
border-radius: 16rpx; border-radius: 16rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05); box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
.company-logo { .company-logo {
width: 120rpx; width: 120rpx;
height: 120rpx; height: 120rpx;
margin: 0 auto 30rpx; margin: 0 auto 30rpx;
background: #f5f5f5; background: #f5f5f5;
border-radius: 8rpx; border-radius: 8rpx;
overflow: hidden; overflow: hidden;
image { image {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
} }
.visit-info { .visit-info {
text-align: center; text-align: center;
margin-bottom: 40rpx; margin-bottom: 40rpx;
.visit-time { .visit-time {
font-size: 36rpx; font-size: 36rpx;
color: #323233; color: #323233;
font-weight: 500; font-weight: 500;
margin-bottom: 12rpx; margin-bottom: 12rpx;
} }
.visit-location { .visit-location {
font-size: 32rpx; font-size: 32rpx;
color: #323233; color: #323233;
margin-bottom: 8rpx; margin-bottom: 8rpx;
} }
.visit-address { .visit-address {
font-size: 28rpx; font-size: 28rpx;
color: #969799; color: #969799;
} }
} }
.visitor-info { .visitor-info {
margin-bottom: 40rpx; margin-bottom: 40rpx;
.info-row { .info-row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 20rpx 0; padding: 20rpx 0;
border-bottom: 1rpx solid #f5f5f5; border-bottom: 1rpx solid #f5f5f5;
&:last-child { &:last-child {
border-bottom: none; border-bottom: none;
} }
.label { .label {
font-size: 28rpx; font-size: 28rpx;
color: #969799; color: #969799;
} }
.value { .value {
font-size: 28rpx; font-size: 28rpx;
color: #323233; color: #323233;
} }
} }
} }
.qr-code { .qr-code {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
margin: 40rpx 0; margin: 40rpx 0;
.qr-canvas { .qr-canvas {
width: 400rpx !important; width: 400rpx !important;
height: 400rpx !important; height: 400rpx !important;
margin-bottom: 20rpx; margin-bottom: 20rpx;
} }
.qr-tip { .qr-tip {
font-size: 24rpx; font-size: 24rpx;
color: #969799; color: #969799;
} }
} }
} }
.expand-btn { .expand-btn {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 20rpx; padding: 20rpx;
margin: 0 30rpx; margin: 0 30rpx;
background: #fff; background: #fff;
border-radius: 8rpx; border-radius: 8rpx;
font-size: 28rpx; font-size: 28rpx;
color: #1989FA; color: #1989fa;
.expand-icon { .expand-icon {
margin-left: 8rpx; margin-left: 8rpx;
transition: transform 0.3s; transition: transform 0.3s;
&.expanded { &.expanded {
transform: rotate(180deg); transform: rotate(180deg);
} }
} }
} }
.detail-content { .detail-content {
padding: 20rpx 30rpx; padding: 20rpx 30rpx;
} }
.detail-section { .detail-section {
margin-bottom: 20rpx; margin-bottom: 20rpx;
padding: 20rpx; padding: 20rpx;
background: #fff; background: #fff;
border-radius: 12rpx; border-radius: 12rpx;
.section-title { .section-title {
font-size: 30rpx; font-size: 30rpx;
color: #323233; color: #323233;
font-weight: 500; font-weight: 500;
margin-bottom: 20rpx; margin-bottom: 20rpx;
padding-left: 20rpx; padding-left: 20rpx;
border-left: 4rpx solid #1989FA; border-left: 4rpx solid #1989fa;
} }
} }
.section-item { .section-item {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 24rpx 0; padding: 24rpx 0;
border-bottom: 1rpx solid #f5f5f5; border-bottom: 1rpx solid #f5f5f5;
&:last-child { &:last-child {
border-bottom: none; border-bottom: none;
} }
.label { .label {
font-size: 28rpx; font-size: 28rpx;
color: #969799; color: #969799;
} }
.value { .value {
font-size: 28rpx; font-size: 28rpx;
color: #323233; color: #323233;
text-align: right; text-align: right;
flex: 1; flex: 1;
margin-left: 20rpx; margin-left: 20rpx;
} }
} }
.companion-item { .companion-item {
margin-bottom: 20rpx; margin-bottom: 20rpx;
padding: 20rpx; padding: 20rpx;
background: #f7f8fa; background: #f7f8fa;
border-radius: 8rpx; border-radius: 8rpx;
&:last-child { &:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
.section-item { .section-item {
padding: 12rpx 0; padding: 12rpx 0;
} }
} }
</style> </style>
-201
View File
@@ -1,201 +0,0 @@
<script setup lang="ts">
import { ref, reactive, computed } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app"
import { isEmpty } from "radash"
import dayjs from "dayjs"
import { hooks, router } from "@@/index"
import { foodApi } from "@@/api"
import zTabs from "@@/components/other/z-tabs/z-tabs.vue"
import { LazyImg, Waterfall } from "vue-waterfall-plugin-next"
import "vue-waterfall-plugin-next/dist/style.css"
// import CustomWaterfallsFlow from "@@/components/other/custom-waterfalls-flow/custom-waterfalls-flow.vue"
import { filePath } from "@@/utils/file"
const menus = {
tabs: [
// { id: null, name: "全部", disabled: false },
{ id: 1, name: "早餐", disabled: false },
{ id: 2, name: "午餐", disabled: false },
{ id: 3, name: "晚餐", disabled: false },
],
weekDays: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"],
}
const state = reactive<any>({
dateList: [],
currentDateIndex: 0,
pa: {
merchantId: null,
startTime: null,
hourType: menus.tabs[0].id,
},
})
onLoad((options: any) => {
initDate()
if (!isEmpty(options.merchantId)) {
state.pa.merchantId = options.merchantId
}
})
onShow(() => {
if (pageRef.value && pageRef.value.reload) {
pageRef.value.reload(false)
}
})
const pageRef = ref<any>(null)
const pageHook = hooks.usePage(pageRef, foodApi.page, null, "data.records")
const query = (page: number, pagesize: number) => {
pageHook.startPage(page, pagesize, false, state.pa)
}
// 重新设置图片路径
const foodList = computed(() => {
return pageHook.list.map((item: any) => {
return { ...item, picUrl: filePath(item.picUrl) }
})
})
const initDate = () => {
const list: any = []
const now = new Date()
for (let i = 0; i < 10; i++) {
const date = dayjs(now).add(i, "d")
const weekDayStr = date.isSame(now, "day") ? "今天" : menus.weekDays[date.day()]
list.push({
weekDayStr,
date: date.toDate(),
dayTime: date.format("MM-DD"),
dateTime: date.format("YYYY-MM-DD"),
})
}
state.dateList = list
state.pa.startTime = state.dateList[0].dateTime
pageRef.value?.reload(false)
}
const doDateItem = (v: any, i: any) => {
state.currentDateIndex = i
state.pa.startTime = state.dateList[i].dateTime
pageHook.list = []
pageRef.value.reload(true)
}
const doTab = (e: any) => {
const target = menus.tabs[e]
state.pa.hourType = target.id
pageHook.list = []
pageRef.value.reload(true)
}
const doProduct = (v: any) => {
// router.jump("/pages/merchant/product-detail", { spuId: v.spuId })
}
</script>
<template>
<crud-layout title="每日菜谱" navBgColor="#1777FF" navBgTitleColor="white" bg="/static/page/blue-bg.png">
<z-paging
ref="pageRef"
height="100%"
:fixed="false"
v-model="pageHook.list"
:default-page-size="8"
safe-area-inset-bottom
:loading-more-enabled="true"
:show-scrollbar="false"
@query="query"
:auto-clean-list-when-reload="false"
:auto-scroll-to-top-when-reload="false"
@scroll="pageHook.scroll"
>
<template #top>
<view class="bg-#1777FF p-15rpx">
<scroll-view scroll-x class="whitespace-nowrap">
<view class="flex-row items-center space-x-15rpx">
<view
:class="{ active_date: state.currentDateIndex == i }"
class="flex-col-center flex-shrink-0 size-95rpx color-white rounded-8rpx font-size-24rpx font-700"
v-for="(v, i) in state.dateList"
:key="i"
@tap="doDateItem(v, i)"
>
<view>{{ v.dayTime }}</view>
<view>{{ v.weekDayStr }}</view>
</view>
</view>
</scroll-view>
</view>
<view class="bg-white rounded-16rpx px-20rpx m-x-10rpx m-t-15rpx">
<z-tabs :list="menus.tabs" @change="doTab" />
</view>
</template>
<view class="h-20rpx" />
<view class="m-x-20rpx">
<Waterfall
v-if="foodList.length"
:list="foodList"
:gutter="10"
:breakpoints="{
1800: { rowPerView: 4 },
1400: { rowPerView: 3 },
975: { rowPerView: 2 },
}"
:hasAroundGutter="false"
backgroundColor="#ffffff00"
:crossOrigin="false"
:animationDuration="100"
>
<template #default="{ item, url, index }">
<div class="card">
<LazyImg class="rounded-2" :url="item.picUrl" />
<view class="p-x-15rpx p-y-25rpx flex items-center justify-between">
<view class="sub-text-28 show-line">{{ item.name }}</view>
<!-- <view class="m-t-10rpx flex-row items-center"> -->
<view class="second-text-32 color-#f63a36!"> ¥{{ hooks.useMoney(item.price) }} </view>
<!-- <view class="m-l-20rpx mt-1 second-text-24 line-through">¥{{ hooks.useMoney(item.marketPrice) }}</view> -->
<!-- </view> -->
<!-- <view class="flex-col items-start"> -->
<!-- <view class="second-text-24">{{ hooks.useFormatNum("库存", "", item.stock) }}</view> -->
<!-- <view class="w-1px h-20rpx m-x-5rpx bg-#999" /> -->
<!-- <view class="second-text-24">{{ hooks.useFormatNum("销量", "", item.salesCount) }}</view> -->
<!-- </view> -->
</view>
</div>
</template>
</Waterfall>
<!-- <custom-waterfalls-flow v-model="foodList" imageKey="picUrl" :columnSpace="1" @wapperClick="doProduct" @imageClick="doProduct">
<template v-slot:default="v">
<view class="p-x-15rpx p-y-25rpx">
<view class="sub-text-28 show-line">{{ v.name }}</view>
<view class="m-t-10rpx flex-row items-center">
<view class="second-text-32 color-#f63a36!"> ¥{{ hooks.useMoney(v.price) }} </view>
<view class="m-l-20rpx second-text-24 line-through"> ¥{{ hooks.useMoney(v.marketPrice) }} </view>
</view>
<view class="flex-row items-center MT 10rpx">
<view class="second-text-24">{{ hooks.useFormatNum("库存", "", v.stock) }}</view>
<view class="w-1px h-20rpx m-x-5rpx bg-#999" />
<view class="second-text-24">{{ hooks.useFormatNum("销量", "", v.salesCount) }}</view>
</view>
</view>
</template>
</custom-waterfalls-flow> -->
</view>
</z-paging>
</crud-layout>
</template>
<style lang="scss" scoped>
:deep() {
.layout_main > scroll-view {
padding: 0 !important;
}
}
.active_date {
background-color: white;
color: #1679ff !important;
}
</style>
+41 -371
View File
@@ -1,20 +1,12 @@
<template> <template>
<crud-layout bg="/static/page/blue-bg.png"> <view class="bg-#F8F8F8 w-full h-screen overflow-hidden">
<view class="h-20rpx" /> <view class="card p-30rpx mx-4 flex items-center">
<view class="flex-row items-center"> <image v-if="userInfo?.avatar" class="w-100rpx h-100rpx rounded-full" :src="userInfo.avatarUrl" mode="aspectFill" />
<image
v-if="state.userInfo?.sysUser?.avatar"
class="w-100rpx h-100rpx rounded-full"
:src="filePath(state.userInfo.sysUser.avatar)"
mode="aspectFill"
/>
<image v-else class="w-100rpx h-100rpx rounded-full" src="/static/icon/default-avatar.png" mode="aspectFill" /> <image v-else class="w-100rpx h-100rpx rounded-full" src="/static/icon/default-avatar.png" mode="aspectFill" />
<view class="m-l-30rpx" v-if="state.userInfo?.sysUser?.userId"> <view class="m-l-30rpx" v-if="token">
<view class="flex-row items-center"> <view class="flex-row items-center">
<text class="sub-text-30">{{ state.userInfo?.sysUser?.name }}</text> <text class="sub-text-30">{{ userInfo.nickName }}</text>
<!-- <text class="iconfont icon-youjiantou color-#666666 m-l-20rpx"></text> -->
</view> </view>
<text class="sub-text-26">ID {{ state.userInfo?.sysUser?.userId }}</text>
</view> </view>
<view v-else class="flex-row items-center m-l-30rpx" @tap="onTabLogin"> <view v-else class="flex-row items-center m-l-30rpx" @tap="onTabLogin">
<text class="sub-text-32">请先登录</text> <text class="sub-text-32">请先登录</text>
@@ -22,375 +14,53 @@
</view> </view>
</view> </view>
<view class="m-t-20rpx rounded-20rpx shadow-main p-30rpx p-b-40rpx bg-white!"> <view class="card p-30rpx m-4 active:bg-#F8F8F8!">
<view class="service_item flex-row items-center justify-between" v-for="(v, i) in state.menuList" :key="i" @tap="onTabMenu(v, i)"> <view class="flex items-center justify-between" @tap="onTabMenu">
<view class="flex-row-center"> <text class="sub-text-36 m-l-20rpx">核销</text>
<image class="w-40rpx h-40rpx" :src="v.icon" mode="aspectFill" />
<text class="sub-text-28 m-l-20rpx">{{ v.text }}</text>
</view>
<view class="iconfont icon-youjiantou" /> <view class="iconfont icon-youjiantou" />
</view> </view>
</view> </view>
</view>
<view v-if="isDevEnv()" class="flex-col-center m-t-40rpx m-x-30rpx">
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff" @tap="doLogout">
退出登录
</view>
</view>
<view v-if="state.appInfo == 'sh'" class="flex-col-center m-t-40rpx m-x-30rpx">
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff" @tap="doChangeRole">
切换角色
</view>
</view>
</crud-layout>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref } from "vue" import { ref } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app" import { onLoad, onShow } from "@dcloudio/uni-app"
import { isEmpty } from "radash" import { isEmpty } from "radash"
import { filePath } from "@@/utils/file"
import { getAppInfo } from "@@/utils/getAppInfo"
import { hooks, router, constant } from "@@/index"
import { userApi } from "@@/api"
import isDevEnv from "@/sheep/utils/isDevEnv"
import { usePayStore } from "@/sheep/stores/pay"
// import dd from "gdt-jsapi"
const state = reactive<{ const userInfo = ref<any>({})
wallet: any[] const token = ref("")
userInfo: any
menuList: {
text: string
icon: string
router: string
}[]
appInfo: string
}>({
wallet: [],
userInfo: {},
menuList: [
// { text: "扫一扫", icon: "/static/icon/scan.png", router: "" },
// { text: "钱包管理", icon: "/static/icon/service_3.png", router: "/pages/wallet/list" },
// { text: "包厢预定", icon: "/static/icon/service_4.png", router: "/pages/boxes/apply-list" },
// { text: "外来人员就餐", icon: "/static/icon/service_1.png", router: "/pages/apply/foreign-list" },
// { text: "跨单位就餐", icon: "/static/icon/service_1.png", router: "/pages/apply/stride-list" },
{ text: "人脸管理", icon: "/static/icon/service_5.png", router: "/pages/human-face/list" },
// { text: "访客", icon: "/static/icon/service_5.png", router: "/pages/integrated/visitor/index" },
// { text: "访客记录", icon: "/static/icon/service_5.png", router: "/pages/integrated/visitor/myvisitor" },
// { text: "访客邀约", icon: "/static/icon/service_5.png", router: "/pages/integrated/visitor/myyyvisitor" },
// { text: "邀约记录", icon: "/static/icon/service_5.png", router: "/pages/integrated/visitor/myyyvisitorlist" },
// { text: "访客核销", icon: "/static/icon/service_5.png", router: "/pages/integrated/visitor/verify" },
// { text: "意见反馈", icon: "/static/icon/service_5.png", router: "/pages/integrated/feedback/index" } // 获取存储的用户信息
], const getUserInfo = () => {
appInfo: "", const tokenStorage = uni.getStorageSync("token")
const userStorage = uni.getStorageSync("user_info")
if (!isEmpty(userStorage) && !isEmpty(tokenStorage)) {
userInfo.value = userStorage
token.value = tokenStorage
} else {
userInfo.value = {}
token.value = ""
}
}
// 点击登录
const onTabLogin = () => {
uni.navigateTo({
url: "/pages/test/login",
})
}
// 点击菜单
const onTabMenu = () => {
uni.navigateTo({ url: "/pages/integrated/visitor/verify" })
}
onLoad(() => {
getUserInfo()
}) })
// 常量
const STORAGE_KEYS = {
TOKEN: constant.storageConstant.TOKEN,
USER_INFO: constant.storageConstant.USERINFO,
}
const storage = {
// 获取存储的用户信息
getUserInfo: () => {
const tokenStorage = uni.getStorageSync(STORAGE_KEYS.TOKEN)
const userStorage = uni.getStorageSync(STORAGE_KEYS.USER_INFO)
if (!isEmpty(userStorage) && !isEmpty(tokenStorage)) {
state.userInfo = userStorage
} else {
state.userInfo = {}
}
},
// 保存用户信息到存储
saveUserInfo: (userData: any) => {
uni.setStorageSync(STORAGE_KEYS.USER_INFO, userData)
storage.getUserInfo()
},
// 保存token到存储
saveToken: (tokenData: any) => {
uni.setStorageSync(STORAGE_KEYS.TOKEN, tokenData)
},
// 清除用户登录相关的存储
clearUserStorage: () => {
uni.removeStorageSync(STORAGE_KEYS.TOKEN)
uni.removeStorageSync(STORAGE_KEYS.USER_INFO)
state.userInfo = {}
},
}
const login = {
// 检查应用环境并登录
checkAppInfo: async () => {
try {
// 先尝试诗画登录
const shihuaResult = await login.shihuaLogin()
if (shihuaResult) return
// 尝试浙政钉登录
const zhengdingResult = login.zhengdingLogin()
if (zhengdingResult) return
uni.showToast({ title: "当前登录环境未知", icon: "none" })
} catch (error) {
console.error("登录失败:", error)
uni.showToast({ title: "登录失败", icon: "none" })
}
},
// 诗画环境登录
shihuaLogin: async () => {
const appInfo = await (window as any).CP2.getAppInfo()
if (appInfo.code == 0) return false
const userInfo = await (window as any).CP2.getUserInfo()
if (userInfo.code == 0) {
uni.showToast({ title: "获取诗画用户信息失败", icon: "none" })
return false
} else {
const { accountId, sessionId } = userInfo.data
return login.doLogin(1, "", accountId, sessionId)
// return login.doLogin(1, "", "67186e49d63fea141aad6772", "67186e49d63fea141aad6776")
}
},
// 浙政钉登录
zhengdingLogin: () => {
const params = new URL(window.location.href).searchParams
const token = params.get("uabaseToken")
if (token) {
return login.doLogin(2, token, "", "")
}
return false
},
// 执行登录操作 - loginType 1:诗画登录 2:浙政钉登录
doLogin: async (loginType: number, token: string, accountId: string, sessionId: string) => {
const tokenStorage = uni.getStorageSync(STORAGE_KEYS.TOKEN)
const userStorage = uni.getStorageSync(STORAGE_KEYS.USER_INFO)
switch (loginType) {
case 1: // 诗画登录
uni.showLoading({ title: "登录中...", mask: true })
const response = await userApi.authsh({ accountId, sessionId })
if (response.code == 0) {
storage.saveToken({ accessToken: response.data.accessToken })
uni.hideLoading()
uni.showToast({ title: "登录成功", icon: "none" })
const { data: userData } = await userApi.userInfo()
storage.saveUserInfo(userData)
} else {
uni.hideLoading()
storage.clearUserStorage()
if (response.msg == "token已失效") {
uni.showToast({ title: "用户凭证已失效,请退出并重新进入智慧后勤应用", icon: "none", duration: 6000 })
} else {
uni.showToast({ title: response.msg || "登录失败", icon: "none" })
}
}
break
case 2: // 浙政钉登录
if (isEmpty(tokenStorage) || isEmpty(userStorage)) {
try {
uni.showLoading({ title: "登录中...", mask: true })
const res = await userApi.auth(token)
if (res.code == 0) {
storage.saveToken(res.data)
uni.hideLoading()
uni.showToast({ title: "登录成功", icon: "none" })
const { data: userData } = await userApi.userInfo()
storage.saveUserInfo(userData)
} else {
storage.clearUserStorage()
if (res.msg == "token已失效") {
uni.showToast({ title: "用户凭证已失效,请退出并重新进入智慧后勤应用", icon: "none", duration: 6000 })
} else {
uni.showToast({ title: res.msg || "登录失败", icon: "none" })
}
}
} catch (error) {
uni.showToast({ title: "登录错误", icon: "none" })
} finally {
uni.hideLoading()
}
}
break
default:
uni.showToast({ title: "当前登录环境未知", icon: "none" })
}
},
// 退出登录
logout: async () => {
const isLogin = await hooks.useCheckLogin()
if (!isLogin) return
uni.showLoading({ title: "退出登录中...", mask: true })
storage.clearUserStorage()
uni.hideLoading()
uni.showToast({ title: "退出登录成功", icon: "none" })
},
// 切换角色
changeRole: () => {
router.jump("/pages/user/userRole")
},
}
const scanCode = {
// 扫一扫
scan: async () => {
usePayStore().setPayData({})
try {
const decodedString = await scanCode.getScanResult()
if (decodedString) {
scanCode.processScanResult(decodedString)
}
// else {
// uni.showToast({ title: "无效的二维码", icon: "none" })
// }
} catch (error: any) {
// if (error) {
// uni.showToast({ title: error, icon: "none" })
// }
}
},
// 获取扫码结果
getScanResult: async (): Promise<string | undefined> => {
if (isDevEnv()) {
// 开发环境模拟扫码
// const testQrCode = "eyJpZCI6IjE4OTYxNTcyMDc2NDQwNTM1MDYiLCJuYW1lIjoi5rWL6K+V5ZWG5oi3In0=" // 线上-测试超市-收款码
// const testQrCode = "eyJpZCI6IjE5MDU4MTY3NTEwNDM0NTI5MzAiLCJuYW1lIjoi5paw5Y6/5bqc6LaF5biCIn0=" // 测试环境-邓本地-新县府超市-收款码
const testQrCode = "eyJpZCI6IjE5MDc2ODQwNTY4NDA4NDMyNjYiLCJuYW1lIjoi5pm66IO95p+c6Ieq6JCl5ZWG5oi3MSJ9" // 测试环境-邓本地-智能柜自营商户1-收款码
uni.showToast({ title: "开发环境不是浙政钉环境,扫码仅供测试,收款商家为测试商户超市", icon: "none" })
return scanCode.decodeBase64(testQrCode)
} else if (state.appInfo == "dd") {
// 浙政钉扫码
const res: any = await (window as any).dd.scan({ type: "all" })
return scanCode.decodeBase64(res.text)
} else if (state.appInfo == "sh") {
// 诗画扫码
const res = await (window as any).CP2.showScanQr({ needResult: 1 })
if (res.code == 1) return scanCode.decodeBase64(res.data.qrCode)
} else {
uni.showToast({ title: "当前环境未知", icon: "none" })
}
},
// 处理扫码结果
processScanResult: (decodedString: string) => {
try {
const decodedJson = JSON.parse(decodedString)
if (decodedJson?.id && decodedJson?.name) {
usePayStore().setPayData(decodedJson)
router.jump("/pages/pay/scanPay")
} else {
uni.showToast({ title: "无效的二维码", icon: "none" })
}
} catch (error) {
uni.showToast({ title: "无效的二维码内容", icon: "none" })
}
},
// 解码base64
decodeBase64: (base64String: string): string | undefined => {
try {
const decodedBinaryString = atob(base64String)
const uint8Array = new Uint8Array(decodedBinaryString.length)
for (let i = 0; i < decodedBinaryString.length; i++) {
uint8Array[i] = decodedBinaryString.charCodeAt(i)
}
const decoder = new TextDecoder("utf-8")
const decodedString = decoder.decode(uint8Array)
console.log("解码结果", decodedString)
return decodedString
} catch (error) {
console.error("Base64 解码失败:", error)
uni.showToast({ title: "无效的二维码", icon: "none" })
return undefined
}
},
}
const onTabLogin = () => (isDevEnv() ? router.jump("/pages/test/login") : login.checkAppInfo())
const onTabMenu = async (item: any, index: number) => {
if (index == state.menuList.length - 1) {
router.jump(item.router)
return
}
const isLogin = await hooks.useCheckLogin()
if (isLogin) {
if (item.text === "扫一扫") {
await scanCode.scan()
} else {
router.jump(item.router)
}
}
}
// 退出登录
const doLogout = async () => {
await login.logout()
}
// 切换角色
const doChangeRole = () => {
login.changeRole()
}
// 初始化配置菜单
const initMenuConfig = () => {
if (isDevEnv()) {
state.menuList.push({
text: "测试-登录",
icon: "/static/icon/login.png",
router: "/pages/test/login",
})
state.menuList.push({
text: "测试-角色选择",
icon: "/static/icon/login.png",
router: "/pages/user/userRole",
})
}
}
onLoad(async () => {
const app = getApp()
app._initTabbar()
// 初始化配置菜单
initMenuConfig()
// 获取应用信息
state.appInfo = await getAppInfo()
})
onShow(() => { onShow(() => {
// 获取存储中的用户信息 getUserInfo()
storage.getUserInfo()
}) })
</script> </script>
<style lang="scss" scoped>
.service_item:not(:last-child) {
padding-bottom: 40rpx;
border-bottom: 1px solid #fafafa;
margin-bottom: 40rpx;
}
</style>
+114 -42
View File
@@ -1,13 +1,36 @@
<template>
<view class="bg-#F8F8F8 w-full h-screen overflow-hidden">
<view class="card p-30rpx m-4">
<view class="text-lg text-center my-1">员工登录</view>
<tm-form ref="applyFormRef" v-model="applyForm" :rules="applyFormRules" direction="vertical">
<tm-form-item name="username" label="用户名" :showBottom="false">
<tm-input v-model="applyForm.username" placeholder="请输入用户名..." />
</tm-form-item>
<view class="h-10rpx" />
<tm-form-item name="password" label="密码" :showBottom="false">
<tm-input password showEye v-model="applyForm.password" placeholder="请输入用户密码..." />
</tm-form-item>
</tm-form>
<tm-button block @tap="doSubmit">登录</tm-button>
</view>
<view class="card p-30rpx m-4">
<view class="text-lg text-center my-1">访客登录</view>
<button class="bg-#0779fd rounded-3 c-white text-lg py-2" open-type="getUserInfo" @click="getUserInfo">微信登录</button>
</view>
</view>
</template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive } from "vue" import { ref, reactive } from "vue"
import { onShow } from "@dcloudio/uni-app"
import { constant, router } from "@@/index" import { constant, router } from "@@/index"
import { userApi } from "@@/api" import { userApi } from "@@/api"
const state = reactive<any>({ const tokenKey = constant.storageConstant.TOKEN
userInfo: null, const userInfoKey = constant.storageConstant.USERINFO
})
const applyForm = reactive<any>({ let applyForm = reactive<any>({
username: "", username: "",
password: "", password: "",
}) })
@@ -16,21 +39,15 @@ const applyFormRules = reactive<any>({
password: [{ required: true, message: "请输入密码" }], password: [{ required: true, message: "请输入密码" }],
}) })
onShow(() => {
const userInfoKey = constant.storageConstant.USERINFO
state.userInfo = uni.getStorageSync(userInfoKey)
})
const applyFormRef = ref<any>(null) const applyFormRef = ref<any>(null)
const doSubmit = async () => { const doSubmit = async () => {
applyFormRef.value.submit() applyFormRef.value.submit()
const { isPass } = applyFormRef.value.validate() const { isPass } = applyFormRef.value.validate()
if (!isPass) return if (!isPass) return
const tokenKey = constant.storageConstant.TOKEN
const userInfoKey = constant.storageConstant.USERINFO
uni.removeStorageSync(tokenKey) uni.removeStorageSync(tokenKey)
uni.showLoading({ title: "登录中...", mask: true }) uni.showLoading({ title: "登录中...", mask: true })
const res = await userApi.login({ const res: any = await userApi.login({
...applyForm, ...applyForm,
grant_type: "password", grant_type: "password",
scope: "server", scope: "server",
@@ -46,36 +63,91 @@ const doSubmit = async () => {
uni.showToast({ title: "登录成功", icon: "none" }) uni.showToast({ title: "登录成功", icon: "none" })
router.back() router.back()
} }
</script>
<template> // 获取用户信息
<crud-layout title="登录" bg="/static/page/blue-bg.png"> const getUserInfo = () => {
<view class="h-30rpx" /> uni.showLoading({ title: "登录中...", mask: true })
<view class="card p-x-30rpx p-y-40rpx"> uni.getUserInfo({
<tm-form ref="applyFormRef" v-model="applyForm" :rules="applyFormRules" direction="vertical"> success: (res) => {
<tm-form-item name="username" label="用户名" :showBottom="false"> // 存储用户信息
<tm-input v-model="applyForm.username" placeholder="请输入用户名.." /> uni.setStorageSync(userInfoKey, res.userInfo)
</tm-form-item>
<view class="h-30rpx" />
<tm-form-item name="password" label="密码" :showBottom="false">
<tm-input password showEye v-model="applyForm.password" placeholder="请输入用户密码.." />
</tm-form-item>
</tm-form>
</view>
<view
v-if="state.detailType != 'view'"
class="bg-#0094ff p-y-20rpx text-center rounded-40rpx m-x-30rpx m-t-60rpx sub-text-30 color-white!"
@tap="doSubmit"
>
登录
</view>
</crud-layout>
</template>
<style lang="scss" scoped> // 微信登录
:deep() { login()
.tmFormItemLabel { },
padding-bottom: 10rpx !important; fail: (res) => {
return uni.showToast({ title: "获取用户信息失败", icon: "error" })
},
complete: () => {
uni.hideLoading()
},
})
}
// 登录
const login = async () => {
uni.showLoading({ title: "登录中...", mask: true })
const result = await uni.login({ provider: "weixin" }).catch((error) => error)
if (result.errMsg === "login:ok") {
console.log("code", result.code)
const res: any = await userApi.mnpLogin(result.code)
if (res.code == 0) {
uni.setStorageSync(tokenKey, res.data)
uni.hideLoading()
uni.showToast({ title: "登录成功", icon: "none" })
router.back()
} else {
uni.hideLoading()
uni.showToast({ title: "获取token失败", icon: "error" })
}
} else {
uni.hideLoading()
uni.showToast({ title: "获取登录code失败", icon: "error" })
} }
} }
</style>
// 绑定
// const bind = async (mobile?: string, code?: string) => {
// // 1. 使用验证码登录获取token
// const params = {
// mobile: "SMS@" + "17319791971",
// code: "0e3XvSll2ptntf4bg3ol2BdVFH1XvSlM",
// grant_type: "mobile",
// }
// const res = await request({
// url: "/auth/oauth2/token",
// method: "post",
// data: params,
// header: {
// Authorization: "Basic bWluaTptaW5p",
// "Content-Type": "application/x-www-form-urlencoded",
// },
// })
// console.log(res)
// const token = res.data.access_token
// // 2. 获取新的小程序code并完成绑定
// // uni.login({
// // success(loginRes) {
// // wx.request({
// // url: "${BASE_URL}/admin/social/bind?state=MINI&code=" + loginRes.code,
// // method: "post",
// // header: {
// // Authorization: "Bearer " + token,
// // },
// // success() {
// // wx.showToast({
// // title: "绑定成功",
// // success() {
// // wx.navigateBack()
// // },
// // })
// // },
// // })
// // },
// // })
// }
</script>
+34 -11
View File
@@ -1,17 +1,18 @@
import request from "@/sheep/utils/request" import request from "@/sheep/utils/request"
import { encryption } from "@/sheep/utils/encryption" import { encryption, encryptionBase64 } from "@/sheep/utils/encryption"
// 将 Basic Auth 字符串提取为常量 // 加密
const BASIC_AUTH = "Basic " + window.btoa(import.meta.env.VITE_OAUTH2_APP_CLIENT) const basicAuth = "Basic " + encryptionBase64(import.meta.env.VITE_OAUTH2_APP_CLIENT)
// 定义接口类型 // 定义接口类型
interface LoginParams { interface LoginParams {
username: string username?: string
password: string password?: string
randomStr: string randomStr?: string
code: string mobile?: string
grant_type: string code?: string
scope: string grant_type?: string
scope?: string
} }
export default { export default {
@@ -64,7 +65,7 @@ export default {
// 登录 // 登录
login: (params: LoginParams) => { login: (params: LoginParams) => {
const { username, password, randomStr, code, grant_type, scope } = params const { username, password, randomStr, code, grant_type, scope } = params
const encPassword = encryption(password, import.meta.env.VITE_PWD_ENC_KEY) // const encPassword = encryption(password, import.meta.env.VITE_PWD_ENC_KEY)
return request({ return request({
url: `/auth/oauth2/token`, url: `/auth/oauth2/token`,
@@ -80,7 +81,7 @@ export default {
data: { password }, data: { password },
header: { header: {
skipToken: true, skipToken: true,
Authorization: BASIC_AUTH, Authorization: basicAuth,
"content-type": "application/x-www-form-urlencoded", "content-type": "application/x-www-form-urlencoded",
}, },
custom: { custom: {
@@ -97,4 +98,26 @@ export default {
showLoading: false, showLoading: false,
}, },
}), }),
mnpLogin: (code: string) => {
const form = {
mobile: `APP-MINI@${code}`,
code: code,
grant_type: "mobile",
scope: "server",
}
return request({
url: "/auth/oauth2/token",
method: "POST",
params: form,
header: {
Authorization: basicAuth,
"content-type": "application/x-www-form-urlencoded",
},
custom: {
showLoading: false,
withToken: false,
},
})
},
} }
@@ -1,5 +1,5 @@
<script setup> <script setup>
import { defineProps, computed } from "vue" import { computed } from "vue"
import { router } from "@@/index" import { router } from "@@/index"
const props = defineProps({ const props = defineProps({
@@ -1,5 +1,5 @@
<script setup> <script setup>
import { defineProps, computed } from "vue" import { computed } from "vue"
import { router } from "@@/index" import { router } from "@@/index"
const props = defineProps({ const props = defineProps({
@@ -1,31 +1,31 @@
<script setup lang="ts"> <script setup lang="ts">
import { defineProps, withDefaults } from "vue"; import { withDefaults } from "vue"
export interface Props { export interface Props {
type: "top" | "bottom"; type: "top" | "bottom"
bgColor: string; bgColor: string
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
type: "top", type: "top",
bgColor: "transparent", bgColor: "transparent",
}); })
function getHeight() { function getHeight() {
const { top, bottom } = uni.getSystemInfoSync().safeAreaInsets!; const { top, bottom } = uni.getSystemInfoSync().safeAreaInsets!
if (props.type && props.type === "bottom") { if (props.type && props.type === "bottom") {
return bottom + "px"; return bottom + "px"
} }
return top + "px"; return top + "px"
} }
</script> </script>
<template> <template>
<view class="safe" :style="{ backgroundColor: props.bgColor, height: getHeight() }"></view> <view class="safe" :style="{ backgroundColor: props.bgColor, height: getHeight() }"></view>
</template> </template>
<style lang="scss"> <style lang="scss">
.safe-top { .safe-top {
width: 100%; width: 100%;
} }
</style> </style>
@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { defineProps, withDefaults, watch, ref, reactive, onMounted } from "vue" import { withDefaults, watch, ref, reactive, onMounted } from "vue"
import { router } from "@@/index" import { router } from "@@/index"
export type Props = { export type Props = {
+56 -56
View File
@@ -1,82 +1,82 @@
import { throttle } from "radash"; import { throttle } from "radash"
function paramsToQuery(params: { [key: string]: string }) { function paramsToQuery(params: { [key: string]: string }) {
let query = []; let query = []
for (let key in params) { for (let key in params) {
query.push(key + "=" + params[key]); query.push(key + "=" + params[key])
} }
return query.join("&"); return query.join("&")
} }
// 路由跳转 // 路由跳转
function _jump(path = "", params = {}, type = 1) { function _jump(path = "", params = {}, type = 1) {
if (!path) { if (!path) {
console.error("router::jump>>path 不能为空"); console.error("router::jump>>path 不能为空")
} }
if (hasTabbar(path)) { if (hasTabbar(path)) {
uni.switchTab({ url: path }); uni.switchTab({ url: path })
return; return
} }
let url: string = ""; let url: string = ""
if (Object.keys(params)) { if (Object.keys(params)) {
url = `${path}?${paramsToQuery(params)}`; url = `${path}?${paramsToQuery(params)}`
} }
if (type == 1) { if (type == 1) {
uni.navigateTo({ url: url }); uni.navigateTo({ url: url })
return; return
} }
if (type == 2) { if (type == 2) {
uni.redirectTo({ url: url }); uni.redirectTo({ url: url })
return; return
} }
if (type == 3) { if (type == 3) {
uni.reLaunch({ url: url }); uni.reLaunch({ url: url })
return; return
} }
} }
// 路由返回 // 路由返回
function _back(page = 1) { function _back(page = 1) {
if (hasHistory()) { if (hasHistory()) {
uni.navigateBack({ uni.navigateBack({
delta: page, delta: page,
}); })
} else { } else {
_jump(__TABBAR__[0], {}); _jump(__TABBAR__[0], {})
} }
} }
export function jump(...args: any) { export function jump(...args: any) {
throttle({ interval: 500 }, () => _jump(...args))(); throttle({ interval: 500 }, () => _jump(...args))()
} }
export function back(...args: any) { export function back(...args: any) {
throttle({ interval: 500 }, () => _back(...args))(); throttle({ interval: 500 }, () => _back(...args))()
} }
export function hasHistory() { export function hasHistory() {
const pages = getCurrentPages(); const pages = getCurrentPages()
if (pages.length > 1) { if (pages.length > 1) {
return true; return true
} }
return false; return false
} }
export function getCurrentPage() { export function getCurrentPage() {
const pages = getCurrentPages(); const pages = getCurrentPages()
return pages[pages.length - 1]; return pages[pages.length - 1]
} }
export function hasTabbar(str: string = "") { export function hasTabbar(str: string = "") {
if (!str) { if (!str) {
const { route } = getCurrentPage(); const { route } = getCurrentPage()
if (route) { if (route) {
str = `/${route}`; str = `/${route}`
} }
} }
if (Array.isArray(__TABBAR__) && __TABBAR__.indexOf(str) !== -1) { if (Array.isArray(__TABBAR__) && __TABBAR__.indexOf(str) !== -1) {
return true; return true
} else { } else {
return false; return false
} }
} }
+17 -9
View File
@@ -1,15 +1,23 @@
import * as CryptoJS from "crypto-js"; import * as CryptoJS from "crypto-js"
/** /**
*加密处理 *加密处理
*/ */
export function encryption(src: string, keyWord: string) { export function encryption(src: string, keyWord: string) {
const key = CryptoJS.enc.Utf8.parse(keyWord); const key = CryptoJS.enc.Utf8.parse(keyWord)
// 加密 // 加密
var encrypted = CryptoJS.AES.encrypt(src, key, { var encrypted = CryptoJS.AES.encrypt(src, key, {
iv: key, iv: key,
mode: CryptoJS.mode.CFB, mode: CryptoJS.mode.CFB,
padding: CryptoJS.pad.NoPadding, padding: CryptoJS.pad.NoPadding,
}); })
return encrypted.toString(); return encrypted.toString()
}
/**
*加密处理
*/
export function encryptionBase64(rawStr: string) {
let wordArray = CryptoJS.enc.Utf8.parse(rawStr)
return CryptoJS.enc.Base64.stringify(wordArray)
} }
+14 -138
View File
@@ -1,8 +1,6 @@
import Request from "luch-request" import Request from "luch-request"
import { isEmpty } from "radash" import { isEmpty } from "radash"
import isDevEnv from "./isDevEnv"
import { router, constant } from "@@/index" import { router, constant } from "@@/index"
import { userApi } from "../api"
// 常量定义 // 常量定义
const TOKEN_KEY = constant.storageConstant.TOKEN const TOKEN_KEY = constant.storageConstant.TOKEN
@@ -32,116 +30,6 @@ function showLoading(message: string) {
} }
} }
// 读取token并处理Authorization请求头
const getAuthHeader = (tokenStorage: any) => {
if (isDevEnv()) {
return tokenStorage?.access_token ? `Bearer ${tokenStorage.access_token}` : null
}
return tokenStorage?.accessToken?.tokenValue ? `Bearer ${tokenStorage.accessToken.tokenValue}` : null
}
// 浙政钉登录 登录成功返回true,否则返回false
async function handleZzdLogin(): Promise<boolean> {
try {
uni.showLoading({ title: "加载中...", mask: true })
const params = new URL(window.location.href).searchParams
const token = params.get("uabaseToken")
if (!token) return false
const res = await userApi.auth(token)
if (res?.code == 0) {
uni.setStorageSync(TOKEN_KEY, res.data)
const { data: userData } = await userApi.userInfo()
uni.setStorageSync(USER_INFO_KEY, userData)
return true
} else {
uni.removeStorageSync(TOKEN_KEY)
uni.removeStorageSync(USER_INFO_KEY)
if (res.msg == "token已失效") {
uni.showToast({ title: "用户凭证已失效,请退出并重新进入智慧后勤应用", icon: "none", duration: 6000 })
} else {
uni.showToast({ title: res.msg || "登录失败", icon: "none" })
}
return false
}
} catch (error) {
uni.showToast({ title: "登录错误", icon: "none" })
return false
} finally {
uni.hideLoading()
}
}
// 诗画登录 登录成功返回true,否则返回false
async function handleShLogin(): Promise<boolean> {
try {
uni.showLoading({ title: "登录中...", mask: true })
const userInfo = await (window as any).CP2.getUserInfo()
if (userInfo.code == 0) {
uni.showToast({ title: "获取诗画用户信息失败", icon: "none" })
return false
}
const { accountId, sessionId } = userInfo.data
const response = await userApi.authsh({ accountId, sessionId })
if (response.code == 0) {
uni.setStorageSync(TOKEN_KEY, { accessToken: response.data.accessToken })
const { data: userData } = await userApi.userInfo()
uni.setStorageSync(USER_INFO_KEY, userData)
uni.showToast({ title: "登录成功", icon: "none" })
return true
} else {
uni.removeStorageSync(TOKEN_KEY)
uni.removeStorageSync(USER_INFO_KEY)
if (response.msg == "token已失效") {
uni.showToast({ title: "用户凭证已失效,请退出并重新进入智慧后勤应用", icon: "none", duration: 6000 })
} else {
uni.showToast({ title: response.msg || "登录失败", icon: "none" })
}
return false
}
} catch (error) {
uni.showToast({ title: "登录错误", icon: "none" })
return false
} finally {
uni.hideLoading()
}
}
// 根据应用环境自动登录 返回登录结果
async function autoLogin(): Promise<boolean> {
const appInfo = await (window as any).CP2.getAppInfo()
// 浙政钉环境
if (appInfo.code == 0) {
return await handleZzdLogin()
}
// 诗画环境
else {
return await handleShLogin()
}
}
// token过期刷新检查
async function checkAndRefreshToken(tokenStorage: any) {
const { accessToken } = tokenStorage
if (!accessToken) return
const expiresDate = new Date(accessToken.expiresAt).getTime()
// console.log("过期时间", expiresDate)
// console.log("现在时间", new Date().getTime())
// console.log("现在时间大于过期时间吗", new Date().getTime() >= expiresDate)
// token已过期,尝试刷新
if (new Date().getTime() >= expiresDate) {
await autoLogin()
}
}
// 处理错误响应 // 处理错误响应
function handleErrorResponse(response: any) { function handleErrorResponse(response: any) {
if (!response.config?.custom?.showError) return if (!response.config?.custom?.showError) return
@@ -156,7 +44,8 @@ function handleErrorResponse(response: any) {
break break
case 424: case 424:
// 处理token过期 // 处理token过期
autoLogin() uni.showToast({ title: "登录已过期,请重新登录", icon: "none" })
router.jump("/pages/test/login")
break break
default: default:
uni.showToast({ uni.showToast({
@@ -180,8 +69,9 @@ function handleSuccessMessage(response: any) {
// 创建请求实例 // 创建请求实例
const http = new Request({ const http = new Request({
baseURL: import.meta.env.VITE_API_URL, // baseURL: import.meta.env.VITE_API_URL,
timeout: 8000, baseURL: import.meta.env.VITE_APP_PROD_SERVICE,
timeout: 15000,
method: "GET", method: "GET",
header: { header: {
Accept: "*/*", Accept: "*/*",
@@ -223,23 +113,13 @@ http.interceptors.request.use(async (config) => {
if (needAuth) { if (needAuth) {
if (!isEmpty(tokenStorage)) { if (!isEmpty(tokenStorage)) {
// token过期刷新检查 const authHeader = tokenStorage?.access_token ? `Bearer ${tokenStorage.access_token}` : null
await checkAndRefreshToken(tokenStorage)
const authHeader = getAuthHeader(tokenStorage)
if (authHeader) { if (authHeader) {
config_header.Authorization = authHeader config_header.Authorization = authHeader
} }
} else { } else {
// token不存在,则发起登录 uni.showToast({ title: "登录已过期,请重新登录", icon: "none" })
await autoLogin() router.jump("/pages/test/login")
// 登录后重新获取token
const newTokenStorage = uni.getStorageSync(TOKEN_KEY)
const authHeader = getAuthHeader(newTokenStorage)
if (authHeader) {
config_header.Authorization = authHeader
}
} }
} }
@@ -282,16 +162,12 @@ http.interceptors.response.use(
// 处理 424 错误(token过期) // 处理 424 错误(token过期)
if (error.statusCode === 424) { if (error.statusCode === 424) {
if (isDevEnv()) { // 清除token并跳转登录
// 开发环境直接清除token并跳转登录 uni.removeStorageSync(TOKEN_KEY)
uni.removeStorageSync(TOKEN_KEY) uni.removeStorageSync(USER_INFO_KEY)
uni.removeStorageSync(USER_INFO_KEY) uni.showToast({ title: "登录已过期,请重新登录", icon: "none" })
uni.showToast({ title: "登录已过期,请重新登录", icon: "none" }) router.jump("/pages/test/login", {}, 1)
router.jump("/pages/test/login") return false
return false
} else {
await autoLogin()
}
} }
// 显示错误信息 // 显示错误信息