鸿蒙智能电子锁前端项目
he wei
2025-01-15 20cfb19d192127e304a081ceb60ca9052f813bf7
U 修改完成
4 文件已重命名
24个文件已修改
3个文件已删除
32个文件已添加
23399 ■■■■ 已修改文件
cert.pem 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
components.d.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
key.pem 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package-lock.json 8039 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pnpm-lock.yaml 6971 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/App.vue 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/area.js 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/auth.js 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/face.js 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/keys.js 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/lockManager.js 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/loginfo.js 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/user.js 227 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/HdwTree/index.vue 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Upload/SingleImage.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Upload/SingleImage3.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/hooks/useElement.js 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/hooks/useWebSocket.js 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/components/Navbar.vue 320 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/index.vue 177 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.ts 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/permission.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/modules/devices.js 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/modules/general.js 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/modules/system.js 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/permission.ts 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/ukey.js 252 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/user.js 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/SoftUKey.js 284 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/Syunew3.js 592 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/auth.js 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/config.js 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/getWsUrl.js 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/request.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/throttle.js 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/tree.js 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/device/key/index.vue 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/device/keys/addEdit.vue 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/device/keys/index.vue 283 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/device/lock/index.vue 247 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/device/locks/addEdit.vue 245 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/device/locks/index.vue 401 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/general/authorize/addEdit.vue 223 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/general/authorize/index.vue 280 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/general/log/index.vue 249 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/components/UKeyBind/BindStep2.vue 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/components/UKeyBind/BindStep3.vue 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/components/UKeyBind/index.vue 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/components/face/FaceLogin.vue 263 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/components/face/faceManager.js 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/index.vue 514 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/logs.vue 246 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/region/addEdit.vue 281 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/region/index.vue 253 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/user/addEdit.vue 292 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/user/index.vue 366 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/user/userFaceManager.vue 322 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.ts 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.ts.timestamp-1736237543907-c4ab2533782e7.mjs 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.ts.timestamp-1736473700275-bf6b2acdf6d8d.mjs 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.ts.timestamp-1736919044075-8e0cad74d89b3.mjs 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cert.pem
New file
@@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
MIIFyzCCA7OgAwIBAgIUals9i1HBk6U5fzCWQd0/B5YW/5cwDQYJKoZIhvcNAQEL
BQAwdTELMAkGA1UEBhMCemgxDTALBgNVBAgMBHdoeWMxDTALBgNVBAcMBHdoeWMx
DTALBgNVBAoMBHdoeWMxDTALBgNVBAsMBHdoeWMxDTALBgNVBAMMBHdoeWMxGzAZ
BgkqhkiG9w0BCQEWDHdoeWNAMTYzLmNvbTAeFw0yNTAxMTUwMTM5MTVaFw0yNjAx
MTUwMTM5MTVaMHUxCzAJBgNVBAYTAnpoMQ0wCwYDVQQIDAR3aHljMQ0wCwYDVQQH
DAR3aHljMQ0wCwYDVQQKDAR3aHljMQ0wCwYDVQQLDAR3aHljMQ0wCwYDVQQDDAR3
aHljMRswGQYJKoZIhvcNAQkBFgx3aHljQDE2My5jb20wggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQCcyay5Z0Qt2PnsyFYH0ZkhJRzN18nXLe9cnhMJ0FpF
IHOoQ8Bs4w/19ReODgvxo9gOMc3dQcWbArxTU3K6ZMML8BRoCyY7pB0uxOgFkmzj
vQWb1R/COCkPKDadF9juWMn0EnpNcS3CtLWyQ1urkNcn0M4vdJkoUxKT93Jd3lgf
kaDaa45ZxkNa9k8G6VNtiPFt+PGUAM0m5rTtYG/e0NNfqmEhLRvdcCRPpFIpQnxo
8Xx57afFen6M9i9WWdA1+nLC9paZMKi8ld5qGieDI9+SFGLWBC19PvMb6BsE+TBT
nmRx4KyXM0460v7gTTgjnL5av4T+f8yfZ0MHv4Hc5d8H1ECLgj4MRkk1IwE0fDmN
w5VGwSYTUzg2rpRIz+5IDE6Wgck9puaWecihiuhnv1d85PQ2W38hIFfcaT+Ay/R8
zmramqwv+cTXcTK6HwwsXF2V1VhFTSXYGVsXq1sA9bxNmfx4sUOK2budN5Xg0qKx
eVASw30/EckJ84ksgmnTT4jQ1MOLAorJR8aK40qBkL+R48BauTkGBdREBRyemfr9
3pNf/iUjpTBS78XqbjFWUEDZfpSf4INdFLOVQLl7/EISJ96nnBvzHngl/TP4dYAY
XijmYFWXa0XWSfr/DzhQkaozZwRoQpKGhHKKEbGyHgoAYBFBP2tU21lWNt4CuZM1
GQIDAQABo1MwUTAdBgNVHQ4EFgQURw4lU60mPMpfnsF551ZybDrU2AgwHwYDVR0j
BBgwFoAURw4lU60mPMpfnsF551ZybDrU2AgwDwYDVR0TAQH/BAUwAwEB/zANBgkq
hkiG9w0BAQsFAAOCAgEAUMjfYRY1DSdwiItJg6mvwNxQlhC1EJFQjR/pkGRXqUEN
zQAUozyRlFVHC1BfXe9q3vs3JOLXW79u0xxmAS6K/doTky9vr20aUQTgUxvIFTWA
Qv/yOiG8JLr/dU6i+qG6CNFN8G2Fj7bQ2J2FzcdKsPeSPczj2y7URk9Urf4U/Hsz
TwHIt7Sis0H6XfuXHwZYweO+oVkpNUzD6oAOvFUqdqVmxvrqWjEeNE6dXgfINh7r
IcWRn2FrXsa2pBo1fSRSW9R2TZH3dxMFOXKF98UqZxvBr1jK1btYyjA4RfFLUoan
Op6Fx7p2BJizFV9NWmH9/gqPTQl0qRXsYN3A5u9+ZZkM6oIXsHLuSyEGT1K5QEOD
988hGC4V3dK3DSgjdN9VoplNUh8eiA4XMIS6mLl4I2M1WHT9S+ct2TCVu9cziZ76
VChFiMihq49r64mvbH4dI2zMu22nbgg2ZS+kHvq+LzirgLtkYaXwWwUw9rqIBnVy
AWJgHe8ROJolTi/yhOtiKeTrBRNiqd6cy3YT8IX9MgIwtzYsY10frS79sni+arWW
ZQKCNic/weE+L8Qy3le0ZsncTxaHR+gXFXXEOgP4FfU8i5MtE0iHuaFBfQGm72dV
EUVg9jsM3lXA7iyLKeexrRegpXsh7zO/ksF1Xk2WD4ZYSpBG8JbBT0tUJ9YoEzA=
-----END CERTIFICATE-----
components.d.ts
@@ -14,10 +14,8 @@
    DropdownMenu: typeof import('./src/components/Share/DropdownMenu.vue')['default']
    Dropzone: typeof import('./src/components/Dropzone/index.vue')['default']
    EditorImage: typeof import('./src/components/Tinymce/components/EditorImage.vue')['default']
    ElAvatar: typeof import('element-plus/es')['ElAvatar']
    ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
    ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
    ElButton: typeof import('element-plus/es')['ElButton']
    ElCascader: typeof import('element-plus/es')['ElCascader']
    ElCol: typeof import('element-plus/es')['ElCol']
    ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
@@ -34,17 +32,18 @@
    ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
    ElOption: typeof import('element-plus/es')['ElOption']
    ElPagination: typeof import('element-plus/es')['ElPagination']
    ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
    ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
    ElRow: typeof import('element-plus/es')['ElRow']
    ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
    ElSelect: typeof import('element-plus/es')['ElSelect']
    ElStep: typeof import('element-plus/es')['ElStep']
    ElSteps: typeof import('element-plus/es')['ElSteps']
    ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
    ElSwitch: typeof import('element-plus/es')['ElSwitch']
    ElTable: typeof import('element-plus/es')['ElTable']
    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
    ElTooltip: typeof import('element-plus/es')['ElTooltip']
    ElTree: typeof import('element-plus/es')['ElTree']
    ElUpload: typeof import('element-plus/es')['ElUpload']
    ErrorLog: typeof import('./src/components/ErrorLog/index.vue')['default']
    GithubCorner: typeof import('./src/components/GithubCorner/index.vue')['default']
    Hamburger: typeof import('./src/components/Hamburger/index.vue')['default']
key.pem
New file
@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCcyay5Z0Qt2Pns
yFYH0ZkhJRzN18nXLe9cnhMJ0FpFIHOoQ8Bs4w/19ReODgvxo9gOMc3dQcWbArxT
U3K6ZMML8BRoCyY7pB0uxOgFkmzjvQWb1R/COCkPKDadF9juWMn0EnpNcS3CtLWy
Q1urkNcn0M4vdJkoUxKT93Jd3lgfkaDaa45ZxkNa9k8G6VNtiPFt+PGUAM0m5rTt
YG/e0NNfqmEhLRvdcCRPpFIpQnxo8Xx57afFen6M9i9WWdA1+nLC9paZMKi8ld5q
GieDI9+SFGLWBC19PvMb6BsE+TBTnmRx4KyXM0460v7gTTgjnL5av4T+f8yfZ0MH
v4Hc5d8H1ECLgj4MRkk1IwE0fDmNw5VGwSYTUzg2rpRIz+5IDE6Wgck9puaWecih
iuhnv1d85PQ2W38hIFfcaT+Ay/R8zmramqwv+cTXcTK6HwwsXF2V1VhFTSXYGVsX
q1sA9bxNmfx4sUOK2budN5Xg0qKxeVASw30/EckJ84ksgmnTT4jQ1MOLAorJR8aK
40qBkL+R48BauTkGBdREBRyemfr93pNf/iUjpTBS78XqbjFWUEDZfpSf4INdFLOV
QLl7/EISJ96nnBvzHngl/TP4dYAYXijmYFWXa0XWSfr/DzhQkaozZwRoQpKGhHKK
EbGyHgoAYBFBP2tU21lWNt4CuZM1GQIDAQABAoICAAH34y/5rKixsGOJWVEkT7qT
i3LHGnzV0gs03dLR94wmHeWXIieLyzPAhbDuGrbIIop9FBNKw9cQF/qo0cgmwy8X
br4mo658fzRK2wnU3oKWaSdda9Zlm5hZ3iOnPL0zRqfGgpwNx3AKEjzgbRpwiU7x
52C/ozuXHWTXpqsogy00SBNt5b3ZSBewf2n8DGTT4woJJclAoFK0c0GGpCjlML4i
9rCyrwae5YnNa42ijMrJe1FRizlmsgeFgK8iFgPTdtTdQSM/seKaNA5bcTMLhH9C
O8Jpj4Oiwm0PgyVTFoj6NFMcqbYFOh5xbys0NPf3fAlKHbqPMquuoXALtq7mAOCE
eyhhrWyUvcwC9KhR2XoTNmukJ+F1DU/4yBOKjzHqkusM10yHfuqGt/Z0aJrpS3VT
kcEiGdPf9Cpp0A82QTMjWimVxe0rxNu9oQjV2rKMTlQ2kgrKrVOSqFrcKqbTjqQP
pXhTlUa6q3sikJMihYUF6aZ2xjjp3YGCn6GdEf+pw3ggHLeNm21Air05z0AFUcCq
RN4tI34Kg4HFEuBu74bh9YPf3OKNhq4bBniTSAP1FyjjRcysPCl/H/zN3BB5HEiA
9juIn/6RLj0qh7wGCiDRBKul/SWA8X37F7NByRDoAICpgebX2ZX6yU4j+QWJGS08
vYj6Y2q1UgNaMvWPlDNBAoIBAQDclQArQ8X/AR51Ll8LbZ3vNsM9l1m0XhxqIn3F
WzNhevDw2bW5U+HnvhQZTBexQ0yJDDnB3DULrQR/hpRVIiRdjiDv0BREpisLF2dP
ZpZcbfM/9rpWIDFoVbpSK+cmJLXjvuxpoE/AQD2YyZYjHC3Pv9lLvvIkD56lL2ya
HMS2NONYuExA3yPxiaoyt2i5mGn6qb3QxDAlAivjecVuG+9g8LKcdlm4Kp0iBSN+
y8MymbA0vxEFeBkdR6qdfBi74FuK33P57k7shgW5bysMzkWoGBWXz6tJLTVUyirI
FV3em94uxUPSXnprbyOGsKcRAgEKEBKMj7FEHMbnl5xUShUhAoIBAQC19mtDvycw
hxiLi1a40FEBaOeuVYQLq8+3oieREvi8TYB1Xi+MC0UO+Nc6DSlFHZBDTNCKF0zq
uiGVuEswm04UM3pyJhoXR25n41PHeAhYzuFjwC0BuFR5gLsTe32gJtdUr0hG0+gd
2baSrPV6WSZqbGcTLvTsWWPMUqzJYmBFvhCejZD0jM7Ou+ZaZKH1e6NuVCkAhKX1
f1FHyM5+ulHIHDvZZTpHfrUKK6BaBAg7QPRp5v/e6DBaN5Zc9d/4CXdfjHdGiQ9p
AJCBuAdGk4fVaxNR9dyMM0F8VGzTDeK7VHJPLJj2zGi0gB4JNBHH96KjeXPMU8qR
Bb5OKrRTx6j5AoIBAQCQlv8AXTGZQVOEvnvA0v7Wwk/ivKYpjBMWIzvMNUcN2/e5
+QucyvZm4Prtxio1oQa3QxNNFxqSZ7xuF3p9/55U0QiYCg5vHXlTkOjAbF+dgNkV
kZZ9RCb+3EsA+BRRtXihLBJt/o6oV7PIxkMrCpqg9oHqm+S/bJmyb87BiqALS8nG
6y/YyiwWdQXirsTv+49kpwVspl1ktPKt7rFSxu733W9HDnErPou3tCSbRdfTAFk3
ihMPQbU8szCMgL750ExRUL4GE4calZOFBPD0OPYCBJ+K3q7Jjd/Kj6bRdugMNqHC
iL43VITsjXJ9JKiuhmDGBtkR2FL7cd2UC5aGzfThAoIBAQConEaY6DAwT373D460
gpQNcl/8s1FAX1yDFMcFL0c9dbNrDnOQmYDMdtk4+akcWmCrfCphS2YCu6cTTUqf
J8oZ/fmgfJ4ApDgyG/wuAZP6dvlzrUgHJ/9e92JikzO9i0BNQH8A4qtbvbnoAE0X
qjRmuTxqEkDyIYLQemuR0I7VEWHlCdkgQrn/5y105XsONhsiyOmViVUfP3LZycFO
vOe7Pi5XK+3tAgRQAEz+HG5kObRBzcObkhv7/TZfvZZkCKGM8GQ2ABs/eT5udVCI
nXxozmeAswpC/9vWkYSlrWBDpeSmaPT0/ESpvW4XqSAw487ZHFmpJJu39lJ5rCic
2Y5BAoIBAGGV+l+e9zX45zUoql7ISi9u+czmAgENSYLAXrvGjuN6cP8Z+wJtjsRC
C3s+8Scz9sH35iCDW1L145cdIA7+f4l6mMCSw2q5OANyZapvFYC1EZy/ftiyzLkU
4ixm0/hTtpAdbxDQJ7KSR2K6bgoAYoWRC1PobqEzWqCoN8bpfN1VpV8LGJEZFJ4r
3giX434PyhnbEj3o0slSH5YF6Ip33QbnwNAFSw3kGNwizIf4P4OWOcWTYmU+DcAH
ewZcS6sP1N8j43QQmeQo2iMTv1jUaC2K/MUPT930b8uPxE0dwEJoT0BlA6UoIqHm
uZg/MOEcSLDUeoVr/e3yChqMt8FKWqE=
-----END PRIVATE KEY-----
package-lock.json
File was deleted
package.json
@@ -12,12 +12,13 @@
    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
  },
  "dependencies": {
    "@element-plus/icons-vue": "^2.3.1",
    "axios": "^1.7.2",
    "clipboard": "^2.0.11",
    "driver.js": "^1.3.1",
    "dropzone": "^5.9.3",
    "echarts": "^5.5.0",
    "element-plus": "^2.7.6",
    "element-plus": "^2.8.1",
    "file-saver": "^2.0.5",
    "fuse.js": "^6.6.2",
    "js-cookie": "^3.0.5",
@@ -53,6 +54,7 @@
    "vite": "^5.3.1",
    "vite-plugin-inspect": "^0.8.4",
    "vite-plugin-mock": "^2.9.8",
    "vite-plugin-vue-setup-extend": "^0.4.0",
    "vue-tsc": "^2.0.21"
  }
}
pnpm-lock.yaml
Diff too large
src/App.vue
@@ -5,30 +5,44 @@
</template>
<script>
import { defineComponent } from 'vue';
import { ElConfigProvider } from 'element-plus';
import zhCn from 'element-plus/dist/locale/zh-cn.mjs';
import { mapState } from 'pinia';
import store from '@/store';
    import { defineComponent } from 'vue';
    import { ElConfigProvider } from 'element-plus';
    import zhCn from 'element-plus/dist/locale/zh-cn.mjs';
    import { mapState } from 'pinia';
    import store from '@/store';
export default defineComponent({
  components: {
    ElConfigProvider
  },
  computed: {
    ...mapState(store.app, ['size'])
  },
  data() {
    return {
      locale: zhCn,
      zIndex: 3000
    };
  }
});
    export default defineComponent({
        components: {
            ElConfigProvider
        },
        computed: {
            ...mapState(store.app, ['size']),
            ...mapState(store.ukey, ['isIn', 'connect'])
        },
        data() {
            return {
                locale: zhCn,
                zIndex: 3000
            };
        },
        watch: {
            isIn(isIn) {
                store.ukey().changeId(isIn);
            },
            connect(connect) {
                store.ukey().checkIsIn(connect);
            }
        },
        mounted() {
            store.ukey().load(true);
        },
    });
</script>
<style lang="scss">
@import "@/styles/element/index.scss";
@use "@/styles/element/index.scss";
html,
body,
#app {
src/api/area.js
@@ -6,7 +6,48 @@
 */
export function getAreaTreeApi() {
  return request({
    url: '/areaInf/getAllAreaInf',
    url: 'areaInf/getAllAreaInfInPaltForm',
    method: 'get'
  });
}
/**
 * 添加区域
 * areaName, areaDescript,  parentId
 * [{uid, uname}]
 */
export function addArea(params, data) {
  return request({
    url: "areaInf/addArea",
    method: "POST",
    params,
    data,
  });
}
/**
 * 删除区域
 * id
 */
export function delArea(id) {
  return request({
    url: "areaInf/delArea",
    method: "GET",
    params: { id }
  });
}
/**
 * 修改区域
 * areaId, areaName, areaDescript, parentId
 * [{uid, uname}]
 */
export function updateArea(params, data) {
  return request({
    url: "areaInf/updateArea",
    method: "POST",
    params,
    data
  });
}
src/api/auth.js
New file
@@ -0,0 +1,58 @@
import request from '@/utils/request';
/**
 * 查询所有授权信息
 * areaId, pageNum, pageSize, state, uname
 */
export function getAllAuthInf(params) {
  return request({
    url: 'authInf/getAllAuthInf',
    method: 'GET',
    params
  });
}
/**
 * 添加授权(批量)
 */
export function addAuth(data) {
  return request({
    url: 'authInf/addAuth',
    method: 'POST',
    data
  });
}
/**
 * 修改授权
 */
export function updateAuth(id, data) {
  return request({
    url: 'authInf/updateAuth',
    method: 'POST',
    params: { id },
    data
  });
}
/**
 * 删除授权(批量)
 */
export function delAuth(ids) {
  return request({
    url: 'authInf/delAuth',
    method: 'POST',
    data: ids
  });
}
/**
 * 根据mac检测蓝牙锁是否有权限
 */
export function checkAuth(mac) {
  return request({
    url: 'authInf/getAuthByUidAndMac',
    method: 'GET',
    params: { mac }
  });
}
src/api/face.js
New file
@@ -0,0 +1,59 @@
import request from '@/utils/request';
/*
*用户人脸查询
*传参:null
*参数: data = 传参   str=请求方式
*/
export const faceInfo = function (data, str) {
  return request({
    method: str,
    url: 'face/info',
    params: data
  });
}
/*
*用户人脸管理      新增
*传参:-
*
*
*/
export const faceMagerAdd = function (data) {
  return request({
    method: "POST",
    url: 'face',
    headers: { 'content-type': 'application/json' },
    data: data
  });
}
/*
*用户人脸管理      更新
*传参:-
*
*
*/
export const faceMagerChange = function (data) {
  return request({
    method: "POST",
    url: 'face/update',
    headers: { 'content-type': 'application/json' },
    params: {
      uName: data.uName
    },
    data: data.fileData
  });
}
/*
*用户人脸管理      删除
*传参:-
*
*
*/
export const faceMagerDelete = function (data) {
  return request({
    method: "POST",
    url: 'face/delete',
    params: data
  });
}
src/api/keys.js
New file
@@ -0,0 +1,70 @@
import request from '@/utils/request';
/**
 * 添加钥匙
 */
export function addKey(data) {
  return request({
    url: 'lockInf/addKey',
    method: 'POST',
    data,
  });
}
/**
 * 删除钥匙
 */
export function delKey(keyId) {
  return request({
    url: 'lockInf/delKey',
    method: 'GET',
    params: { keyId },
  });
}
/**
 * 查询所有钥匙信息
 * { keyName, pageNum, pageSize, uname
 * }
 * @returns
 */
export function getAllKeyList(params) {
  return request({
    url: 'lockInf/getAllKeyInf',
    method: 'GET',
    params
  });
}
/**
 * 授权时查询所有钥匙信息(不分页)
 *
 * @returns
 */
export function getKeyInfAuth() {
  return request({
    url: 'lockInf/getKeyInfAuth',
    method: 'GET',
  });
}
/**
 * 查询所有钥匙名信息(用于下拉)
 */
export function authKey() {
  return request({
    url: 'lockInf/getkinf',
    method: 'GET',
  });
}
/**
 * 更新钥匙信息
 */
export function updateKey(data) {
  return request({
    url: 'lockInf/updateKey',
    method: 'POST',
    data,
  });
}
src/api/lockManager.js
New file
@@ -0,0 +1,94 @@
import request from "@/utils/request";
/**
 * 查询所有锁信息
 * areaId lockName lockState lockType pageNum pageSize
 */
export function getAllLockInf(params) {
  return request({
    url: "lockInf/getAllLockInf",
    method: "GET",
    params,
  });
}
/**
 * 编辑
 * lockId lockName areaId lockType
 */
export function updateLock(params) {
  return request({
    url: "lockInf/updateLock",
    method: "GET",
    params,
  });
}
/**
 * 添加锁 包含批量
 * areaId lockName  lockType    num
 */
export function addLock(params) {
  return request({
    url: "lockInf/addLock",
    method: "GET",
    params,
  });
}
/**
 * 删除锁
 * lockId
 */
export function delLock(lockId) {
  return request({
    url: "lockInf/delLock",
    method: "GET",
    params: { lockId },
  });
}
/**
 * 查询所有锁信息
 *
 */
export function getLockInfAuth() {
  return request({
    url: "lockInf/getLockInfAuth",
    method: "GET",
  });
}
/**
 * 远程开锁
 * lockId
 */
export function lockOpen(lockId) {
  return request({
    url: "lockRt/lockOpen",
    method: "GET",
    params: { lockId },
  });
}
/**
 * 查询当前管理员 所管理区域下的所有锁具 下拉
 */
export function getAreaUserLock() {
  return request({
    url: "lockInf/getAreaUserLock",
    method: "GET",
  });
}
/**
 * 查询指定区域下的所有锁具  下拉用
 * id
 */
export function getLinfById(id) {
  return request({
    url: "areaInf/getLinfById",
    method: "GET",
    params: { id },
  });
}
src/api/loginfo.js
New file
@@ -0,0 +1,34 @@
import request from '@/utils/request';
/**
 * 查询操作日志 锁相关
 */
export function getLockLog(pageNum, pageSize, data) {
  return request({
    url: 'areaInf/getLockLog',
    method: 'POST',
    params: { pageNum, pageSize },
    data
  });
}
/**
 * 查询系统日志
 */
export function getSysLog(data) {
  return request({
    url: 'operationLog/getPage',
    method: 'POST',
    data
  });
}
/**
 * 事件列表 下拉
 */
export function getTypes() {
  return request({
    url: 'operationLog/getTypes',
    method: 'GET'
  });
}
src/api/user.js
@@ -4,10 +4,11 @@
export function login(uname, usnId) {
  return request({
    method: 'GET',
    url: '/login/login',
    url: 'login/login',
    params: {
      uname,
      usnId: encodeURIComponent(formatPassword(usnId))
      usnId: encodeURIComponent(formatPassword(usnId)),
      platFrom: 1,
    }
  });
}
@@ -15,7 +16,7 @@
export function getInfo(uname, usnId) {
  return request({
    method: 'GET',
    url: '/login/login',
    url: 'login/login',
    params: {
      uname,
      usnId: encodeURIComponent(formatPassword(usnId))
@@ -25,7 +26,223 @@
export function logout(token) {
  return request({
    url: '/login/logout',
    method: 'post'
    url: 'login/logout',
    method: 'GET'
  });
}
/**
 * 空接口 用于重置会话有效期
 */
export const pingpong = () => {
  return request({
    method: "GET",
    url: "heart/getCookie",
  });
};
/**
 * 查询所有用户信息
 * @param {*} areaId, pageCurr, pageSize
 * @returns
 */
export const getAllUser = (areaId, pageCurr, pageSize) => {
  return request({
    method: "GET",
    url: "userInf/getAllUser",
    params: { areaId, pageCurr, pageSize },
  });
};
/**
 * 查询区域管理员下所有的用户名 只能是管理员才能查询
 */
export const getAllUserName = () => {
  return request({
    method: "GET",
    url: "areaUser/getUserNameByAreaIds",
  });
}
/**
 * 查询当前管理员下所有用户对象 只能是管理员才能查询 (不包含管理员自己)
 */
export const getUinfByAreaIds = () => {
  return request({
    method: "GET",
    url: "areaUser/getUinfByAreaIds",
  });
};
/**
 * 编辑用户信息
 * 不能改用户名
 * @returns
 */
export const updateUser = (data) => {
  return request({
    method: "POST",
    url: "userInf/updateUinf",
    data
  });
};
/**
 * 新添加用户信息
 *
 */
export const addUser = (data) => {
  return request({
    method: "POST",
    url: "userInf/addUser",
    data,
  });
};
/**
 * 删除用户信息
 *
 */
export const deleteUser = (uname) => {
  return request({
    method: "GET",
    url: "userInf/deleteUser",
    params: { uname },
  });
};
/**
 * 将管理员变成普通用户
 *
 */
export const dropRole = (uid) => {
  return request({
    method: "GET",
    url: "userInf/dropRole",
    params: { uid },
  });
};
/**
 * 将普通用户变成管理员
 *
 */
export const improveRole = (uid) => {
  return request({
    method: "GET",
    url: "userInf/improveRole",
    params: { uid },
  });
};
/**
 * 重置用户密码
 *
 */
export const resetSnId = (uid) => {
  return request({
    method: "GET",
    url: "userInf/resetSnId",
    params: { uid },
  });
};
/**
 * 查询所有包机组信息
 */
export const getBaojiInfo = () => {
  return request({
    method: "POST",
    url: "baoji/getBaojiInfo",
  });
};
/**
 * 查询所有设备
 */
export const getAllDevs = () => {
  return request({
    method: "GET",
    url: "devInf/getDinf",
  });
};
/**
 * 查询所有用户信息(不分页除内置用户外用于下拉)
 */
export const getAllUsers = () => {
  return request({
    method: "GET",
    url: "userInf/getUinf",
  });
};
/**
 * 查询指定区域下的所有用户名 下拉用
 */
export const getUinfById = (id) => {
  return request({
    method: "GET",
    url: "areaInf/getUinfById",
    params: { id },
  });
};
/**
 * 根据用户名查询绑定的ukey
 * @returns {AxiosPromise}
 */
export const getUKeyByUName = (uname) => {
  return request({
    method: "GET",
    url: "userInf/searchUNameToUKey",
    params: {
      uname,
    },
  });
};
/**
 * 根据uKey的id查询已绑定的用户名
 */
export const getUNameByUKey = (ukeyId) => {
  return request({
    method: "GET",
    url: "userInf/searchUKeyToUName",
    params: {
      ukeyId
    },
  });
};
/**
 * 绑定UKey
 */
export const bindUKey = (UName, UKeyId, upubKeyX, upubKeyY) => {
  return request({
    method: "POST",
    url: "userInf/bindUkey",
    data: {
      uname: UName,
      ukeyId: UKeyId,
      upubKeyX,
      upubKeyY,
    },
  });
};
/**
 * uKeyLogin登录
 */
export const uKeyLogin = (uname, password, ukeyId) => {
  return request({
    method: "POST",
    url: `login/loginWithUKey`,
    params: {
      uname,
      usnId: encodeURIComponent(formatPassword(password)),
      ukeyId,
    },
  });
};
src/components/HdwTree/index.vue
@@ -1,60 +1,90 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { getAreaTreeApi } from '@/api/area';
import { formatAreaTree } from '@/utils/tree';
<script setup>
    import { ref, onMounted, reactive, nextTick } from "vue";
    import { getAreaTreeApi } from "@/api/area";
    import { formatAreaTree } from "@/utils/tree";
export default defineComponent({
  name: 'HdwTree',
  data() {
    return {
      defaultProps: {
        children: 'children',
        label: 'label'
      },
      data: []
    };
  },
  methods: {
    handleNodeClick(data) {
      console.log(data);
    },
    async getAreaTree() {
      try {
        const res = await getAreaTreeApi();
        let data = [];
        if (res.code === 1 && res.data) {
          data = res.data2;
        }
        const treeList = [];
        for (let i = 0; i < data.length; i++) {
          formatAreaTree(data[i], treeList);
        }
        console.log(data);
        console.log(treeList);
        this.data = treeList;
      } catch (e) {
        console.log(e);
      }
    }
  },
  mounted() {
    this.getAreaTree();
  }
});
    const emit = defineEmits(["itemClick"]);
    const defaultProps = reactive({
        children: "children",
        label: "label",
    });
    const treeRef = ref();
    const data = ref([]);
    const defaultSelect = ref([1]);
    onMounted(() => {
        getAreaTree();
    });
    function handleNodeClick(data) {
        console.log(data);
        emit("itemClick", data);
    }
    async function getAreaTree() {
        try {
            const res = await getAreaTreeApi();
            let _data = [];
            if (res.code === 1 && res.data) {
                _data = res.data2;
            }
      console.log('_data', _data, '=============');
            const treeList = [];
      let ids = _data.map((v) => v.id);
            for (let i = 0; i < _data.length; i++) {
                formatAreaTree(_data[i], ids, treeList);
            }
            // console.log(_data, 'data');
            console.log(treeList, "treeList");
            data.value = treeList;
            await nextTick();
            // defaultSelect.value = [treeList[0].id]
            handleNodeClick(treeList[0]);
        } catch (e) {
            console.log(e);
        }
    }
    const collapseAll = () => {
        const nodes = treeRef.value.store.nodesMap;
        Object.values(nodes).forEach((node) => {
            if (node.level > 0) {
                // 排除根节点
                node.expanded = false;
            }
        });
        treeRef.value.store.updateExpandedKeys(); // 更新展开状态
    };
    const expandAll = () => {
        const nodes = treeRef.value.store.nodesMap;
        Object.values(nodes).forEach((node) => {
            if (node.level > 0) {
                // 排除根节点
                node.expanded = true;
            }
        });
        treeRef.value.store.updateExpandedKeys(); // 更新展开状态
    };
</script>
<template>
  <el-scrollbar>
    <el-tree
        style="max-width: 600px"
        :data="data"
        :props="defaultProps"
        :expand-on-click-node="false"
        @node-click="handleNodeClick"
    />
  </el-scrollbar>
    <el-scrollbar>
    <el-button type="warning" size="small" @click="collapseAll">折叠全部</el-button>
    <el-button type="primary" size="small" @click="expandAll">展开全部</el-button>
        <el-tree
            style="max-width: 600px"
            ref="treeRef"
            :data="data"
            :props="defaultProps"
            node-key="id"
            :expand-on-click-node="false"
            :default-checked-keys="defaultSelect"
            @node-click="handleNodeClick"
        />
    </el-scrollbar>
</template>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>
src/components/Upload/SingleImage.vue
@@ -80,7 +80,7 @@
</script>
<style lang="scss" scoped>
    @import "@/styles/mixin.scss";
    @use "@/styles/mixin.scss";
    .upload-container {
        width: 100%;
        position: relative;
src/components/Upload/SingleImage3.vue
@@ -88,7 +88,7 @@
</script>
<style lang="scss" scoped>
@import "@/styles/mixin.scss";
@use "@/styles/mixin.scss";
.upload-container {
  width: 100%;
  position: relative;
src/hooks/useElement.js
New file
@@ -0,0 +1,59 @@
import { ref, reactive } from "vue";
import { ElMessageBox, ElMessage, ElLoading } from "element-plus";
export default () => {
  const $message = ElMessage;
  function $alert(title, fnCancel = () => {}) {
    ElMessageBox.confirm(title, "系统提示", {
      confirmButtonText: "确定",
      type: "warning",
      showCancelButton: false,
      center: true,
    })
      .then(() => {
        fnCancel();
      })
      .catch(() => {
        fnCancel();
      });
  }
  function $confirm(operate, fnOk, fnCancel = () => {}) {
    ElMessageBox.confirm(`请确认是否${operate}?`, "系统提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
      center: true,
    })
      .then(() => {
        fnOk();
      })
      .catch(() => {
        fnCancel();
      });
  }
  function $loading(customOptions = {}) {
    const defaultOptions = {
      lock: true,
      text: "Loading",
      background: "rgba(0, 0, 0, 0.7)",
      target: document.body,
      // 可以添加更多默认配置
    };
    // 合并默认配置和自定义配置
    const options = { ...defaultOptions, ...customOptions };
    // 调用Element UI的加载服务
    const loadingInstance = ElLoading.service(options);
    return loadingInstance;
    // return () => {
    //   console.log('?', 'close', '=============');
    //   loadingInstance.close();
    // };
  }
  return { $alert, $confirm, $message, $loading };
};
src/hooks/useWebSocket.js
New file
@@ -0,0 +1,148 @@
// useWebSocket.js
import {
  ref,
  reactive,
  onMounted,
  onUnmounted,
  onActivated,
  onDeactivated,
} from "vue";
import getWsUrl from "@/utils/getWsUrl";
export default function (url) {
  url = getWsUrl(url);
  // 重连时间间隔 默认5秒
  const reConnectDelay = 5 * 1000;
  const socket = ref(null);
  const isConnected = ref(false);
  const message = ref("");
  let timer = null;
  const sendCallback = reactive([]);
  const connect = () => {
    if (socket.value) {
      socket.value.close();
    }
    socket.value = new WebSocket(url);
    socket.value.onopen = () => {
      isConnected.value = true;
      console.log("WebSocket Connected, url: ", url);
      sendCallback.forEach((v) => v());
      sendCallback.length = 0;
    };
    socket.value.onmessage = (event) => {
      // 处理接收到的消息
      // console.log("Received:", event.data);
      // 可以在这里通过 emit 发送消息到组件
      message.value = event.data;
    };
    socket.value.onerror = (Event) => {
      console.error("WebSocket Error:", Event, url);
      WSClose(Event);
    };
    socket.value.onclose = WSClose;
  };
  // 发送数据
  const sendData = (data) => {
    if (socket.value && socket.value.readyState === socket.value.OPEN) {
      // console.log('send', data, '=============');
      socket.value.send(data);
    } else {
      sendCallback.push(() => sendData(data));
    }
  };
  function WSClose(Event) {
    isConnected.value = false;
    if (socket.value) {
      switch (socket.value.readyState) {
        case 0:
          socket.value.onopen = () => {
            socket.value.close();
            console.log('链接关闭', url, 'close事件对象:', Event);
            socket.value = null;
            // 没有event对象 则为手动关闭
            if (Event) {
              reConnect();
            }
          }
          break;
        case 1:
          socket.value.close();
          console.log('链接关闭', url, 'close事件对象:', Event);
          socket.value = null;
          // 没有event对象 则为手动关闭
          if (Event) {
            reConnect();
          }
          break;
        case 2:
          socket.value.onclose = () => {
            console.log('链接关闭', url, 'close事件对象:', Event);
            socket.value = null;
            // 没有event对象 则为手动关闭
            if (Event) {
              reConnect();
            }
          }
          break;
        case 3:
          console.log('链接关闭', url, 'close事件对象:', Event);
          socket.value = null;
          // 没有event对象 则为手动关闭
          if (Event) {
            reConnect();
          }
          break;
      }
    }
  }
  // 重连
  function reConnect() {
    if (timer) {
      return false;
    }
    timer = setTimeout(() => {
      timer = null;
      connect();
    }, reConnectDelay);
  }
  onMounted(() => {
    // console.log('socket mount', Date.now(), '=============');
    console.log("on socket mount", url, "=============");
    connect();
  });
  onActivated(() => {
    if (!socket.value) {
      console.log("on socket active", url, "=============");
      connect();
    }
  });
  onDeactivated(() => {
    if (socket.value) {
      WSClose();
    }
  });
  onUnmounted(() => {
    if (socket.value) {
      WSClose();
    }
  });
  // 返回 socket 对象和状态
  return { socket, isConnected, message, sendData };
}
src/layout/components/Navbar.vue
@@ -1,170 +1,196 @@
<template>
  <div class="navbar">
    <div class="sys-name-wrapper">
      <div class="sys-icon">
        <svg-icon icon-class="lock-hdw"></svg-icon>
      </div>
      <div class="sys-name">{{ title }}</div>
    </div>
    <div class="right-menu">
      <el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click">
        <div class="avatar-wrapper">
          <img :src="avatar + '?imageView2/1/w/80/h/80'" class="user-avatar" alt="" />
          <el-icon class="el-icon-caret-bottom" size="small">
            <CaretBottom />
          </el-icon>
        </div>
        <template #dropdown>
          <el-dropdown-menu>
            <router-link to="/">
              <el-dropdown-item>首页</el-dropdown-item>
            </router-link>
            <el-dropdown-item @click="logout">
              <span style="display:block;">退出登录</span>
            </el-dropdown-item>
          </el-dropdown-menu>
        </template>
      </el-dropdown>
    </div>
  </div>
    <div class="navbar">
        <div class="sys-name-wrapper">
            <div class="sys-icon">
                <svg-icon icon-class="lock-hdw"></svg-icon>
            </div>
            <div class="sys-name">{{ title }}</div>
        </div>
        <div class="right-menu">
            <el-dropdown
                class="avatar-container right-menu-item hover-effect"
                trigger="click"
            >
                <div class="avatar-wrapper">
                    <img
                        :src="avatar + '?imageView2/1/w/80/h/80'"
                        class="user-avatar"
                        alt=""
                    />
          <span>{{ uname }}</span>
                    <el-icon class="el-icon-caret-bottom" size="small">
                        <CaretBottom />
                    </el-icon>
                </div>
                <template #dropdown>
                    <el-dropdown-menu>
                        <router-link to="/">
                            <el-dropdown-item>首页</el-dropdown-item>
                        </router-link>
                        <el-dropdown-item @click="logout">
                            <span style="display: block">退出登录</span>
                        </el-dropdown-item>
                    </el-dropdown-menu>
                </template>
            </el-dropdown>
        </div>
    </div>
</template>
<script>
import { mapState } from 'pinia';
import store from '@/store';
import { defineComponent } from 'vue';
import { CaretBottom } from '@element-plus/icons-vue';
import SvgIcon from '@/components/SvgIcon/index.vue';
<script setup name="Navbar">
    import store from "@/store";
    import { computed, ref, watchEffect } from "vue";
    import { useRoute, useRouter } from "vue-router";
    import { CaretBottom } from "@element-plus/icons-vue";
    import SvgIcon from "@/components/SvgIcon/index.vue";
    import useWebSocket from "@/hooks/useWebSocket";
    import useElement from "@/hooks/useElement.js";
    const { message } = useWebSocket("loginCheck");
const { $alert, $loading, $message, $confirm } = useElement();
export default defineComponent({
  components: {
    SvgIcon,
    CaretBottom
  },
  data() {
    return {
      title: '鸿蒙智能电子锁管理平台'
    };
  },
  computed: {
    ...mapState(store.app, [
      'sidebar',
      'device'
    ]),
    ...mapState(store.user, [
      'avatar'
    ])
  },
  methods: {
    toggleSidebar() {
      store.app().toggleSidebar();
    },
    async logout() {
      await store.user().logout();
      this.$router.push(`/login?redirect=${this.$route.fullPath}`);
    const router = useRouter();
    const route = useRoute();
    const title = ref("鸿蒙智能电子锁管理平台");
    const sidebar = computed(() => store.app().sidebar);
    const device = computed(() => store.app().device);
    const avatar = computed(() => store.user().avatar);
  const uname = computed(() => store.user().name);
  watchEffect(() => {
  if (message.value) {
    const {
      code,
      data: { checkLogin },
    } = JSON.parse(message.value);
    if (!checkLogin.data) {
      $message(checkLogin.msg);
      logout();
      // localStorage.removeItem("uname");
      // localStorage.removeItem("uid");
      // setTimeout(() => {
      //   $router.push("/login");
      //   location.reload();
      // }, 2000);
    }
  }
});
    function toggleSidebar() {
        store.app().toggleSidebar();
    }
    async function logout() {
        await store.user().logout();
        // console.log('logout', '=============');
        router.push(`/login?redirect=${route.fullPath}`);
    }
</script>
<style lang="scss" scoped>
.navbar {
  height: 64px;
  overflow: hidden;
  position: relative;
  background: #fff;
  box-shadow: 0 1px 4px rgba(0, 21, 41, .08);
  .sys-name-wrapper {
    display: inline-block;
    line-height: 64px;
    color: var(--light-color);
    font-size: 24px;
    width: 894px;
    .sys-icon {
      display: inline-block;
      font-weight: 700;
      margin-left: 16px;
      margin-right: 8px;
    }
    .sys-name {
      font-weight: 700;
      display: inline-block;
    }
  }
  .hamburger-container {
    line-height: 46px;
    height: 100%;
    float: left;
    cursor: pointer;
    transition: background .3s;
    -webkit-tap-highlight-color: transparent;
    .navbar {
        height: 64px;
        overflow: hidden;
        position: relative;
        background: #fff;
        box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
        .sys-name-wrapper {
            display: inline-block;
            line-height: 64px;
            color: var(--light-color);
            font-size: 24px;
            width: 894px;
            .sys-icon {
                display: inline-block;
                font-weight: 700;
                margin-left: 16px;
                margin-right: 8px;
            }
            .sys-name {
                font-weight: 700;
                display: inline-block;
            }
        }
        .hamburger-container {
            line-height: 46px;
            height: 100%;
            float: left;
            cursor: pointer;
            transition: background 0.3s;
            -webkit-tap-highlight-color: transparent;
    &:hover {
      background: rgba(0, 0, 0, .025)
    }
  }
            &:hover {
                background: rgba(0, 0, 0, 0.025);
            }
        }
  .breadcrumb-container {
    float: left;
  }
        .breadcrumb-container {
            float: left;
        }
  .errLog-container {
    display: inline-block;
    vertical-align: top;
  }
        .errLog-container {
            display: inline-block;
            vertical-align: top;
        }
  .right-menu {
    float: right;
    height: 100%;
    line-height: 64px;
        .right-menu {
            float: right;
            height: 100%;
            line-height: 64px;
    &:focus {
      outline: none;
    }
            &:focus {
                outline: none;
            }
    .right-menu-item {
      display: inline-block;
      padding: 0 8px;
      height: 100%;
      line-height: 64px;
      font-size: 18px;
      color: #5a5e66;
      vertical-align: text-bottom;
            .right-menu-item {
                display: inline-block;
                padding: 0 8px;
                height: 100%;
                line-height: 64px;
                font-size: 18px;
                color: #5a5e66;
                vertical-align: text-bottom;
      &.hover-effect {
        cursor: pointer;
        transition: background .3s;
                &.hover-effect {
                    cursor: pointer;
                    transition: background 0.3s;
        &:hover {
          background: rgba(0, 0, 0, .025)
        }
      }
    }
                    &:hover {
                        background: rgba(0, 0, 0, 0.025);
                    }
                }
            }
    .avatar-container {
      margin-right: 30px;
            .avatar-container {
                margin-right: 30px;
      .avatar-wrapper {
        margin-top: 10px;
        position: relative;
        height: 45px;
                .avatar-wrapper {
                    margin-top: 10px;
                    position: relative;
                    height: 45px;
          display: flex;
          align-items: center;
          color: #fff;
        .user-avatar {
          cursor: pointer;
          width: 40px;
          height: 40px;
          border-radius: 10px;
        }
                    .user-avatar {
                        cursor: pointer;
                        width: 40px;
                        height: 40px;
                        border-radius: 10px;
            margin-right: 10px;
                    }
        .el-icon-caret-bottom {
          cursor: pointer;
          position: absolute;
          right: -20px;
          top: 25px;
          font-size: 12px;
        }
      }
    }
  }
}
                    .el-icon-caret-bottom {
                        cursor: pointer;
                        position: absolute;
                        right: -20px;
                        // top: 25px;
                        font-size: 12px;
                    }
                }
            }
        }
    }
</style>
src/layout/index.vue
@@ -1,7 +1,7 @@
<template>
  <div :class="classObj" class="app-wrapper">
    <div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
<!--    <sidebar class="sidebar-container" />-->
    <!--    <sidebar class="sidebar-container" />-->
    <div :class="{ hasTagsView: needTagsView }" class="main-container">
      <div :class="{ 'fixed-header': fixedHeader }">
        <navbar />
@@ -13,89 +13,120 @@
      </right-panel>
    </div>
  </div>
  <el-drawer
      v-model="isCollapse"
      title="I am the title"
      direction="ltr"
      size="300px"
      :with-header="false"
      :before-close="handleClose">
  <el-drawer v-model="isCollapseM" title="I am the title" direction="ltr" size="300px" :with-header="false"
    :before-close="handleClose">
    <sidebar class="sidebar-container" />
  </el-drawer>
</template>
<script>
import RightPanel from '@/components/RightPanel';
import { AppMain, Navbar, Settings, TagsView, Sidebar } from './components';
import ResizeMixin from './mixin/ResizeHandler';
import { mapState } from 'pinia';
import store from '@/store';
import { defineComponent } from 'vue';
import changeTheme from '@/utils/changeTheme.js';
    import RightPanel from "@/components/RightPanel";
    import { AppMain, Navbar, Settings, TagsView, Sidebar } from "./components";
    import ResizeMixin from "./mixin/ResizeHandler";
    import { mapState } from "pinia";
    import store from "@/store";
    import { defineComponent } from "vue";
    import changeTheme from "@/utils/changeTheme.js";
    import { throttle } from "@/utils/throttle.js";
    import { pingpong } from "@/api/user";
    import config from '@/utils/config.js';
    const { ukey, face } = config;
export default defineComponent({
  name: 'LayoutIndex',
  components: {
    AppMain,
    Navbar,
    RightPanel,
    Settings,
    TagsView,
    Sidebar
  },
  watch: {
    theme() {
      changeTheme(this.theme);
    }
  },
  mixins: [ResizeMixin],
  computed: {
    ...mapState(store.app, ['sidebar']),
    ...mapState(store.app, ['sidebar', 'device']),
    ...mapState(store.settings, {
      theme: 'theme',
      showSettings: 'showSettings',
      needTagsView: 'tagsView',
      fixedHeader: 'fixedHeader',
      secondMenuPopup: 'secondMenuPopup'
    }),
    classObj() {
      return {
        hideSidebar: !this.sidebar.opened,
        openSidebar: this.sidebar.opened,
        withoutAnimation: this.sidebar.withoutAnimation,
        mobile: this.device === 'mobile'
      };
    },
    isCollapse() {
      if (this.secondMenuPopup) {
        return true;
    const throttleConect = throttle(pingpong, 1000);
    export default defineComponent({
        name: "LayoutIndex",
        components: {
            AppMain,
            Navbar,
            RightPanel,
            Settings,
            TagsView,
            Sidebar,
        },
        watch: {
            theme() {
                changeTheme(this.theme);
            },
            isCollapse: {
                handler(val) {
                    this.isCollapseM = val;
                },
                immediate: true,
            },
      isIn: {
       async handler(val) {
         if (ukey && !val) {
           await store.user().logout();
           // console.log('logout', '=============');
           router.push(`/login?redirect=${route.fullPath}`);
         }
       },
        immediate: true
      }
      return !this.sidebar.opened;
    }
  },
  methods: {
    handleClickOutside() {
      store.app().closeSidebar({ withoutAnimation: false });
    },
    handleClose() {
      store.app().toggleSidebar();
    }
  },
  beforeMount() {
    changeTheme(this.theme);
  },
  mounted() {
  }
});
        },
        mixins: [ResizeMixin],
        computed: {
            ...mapState(store.app, ["sidebar"]),
            ...mapState(store.app, ["sidebar", "device"]),
            ...mapState(store.ukey, ["isIn"]),
            ...mapState(store.settings, {
                theme: "theme",
                showSettings: "showSettings",
                needTagsView: "tagsView",
                fixedHeader: "fixedHeader",
                secondMenuPopup: "secondMenuPopup",
            }),
            classObj() {
                return {
                    hideSidebar: !this.sidebar.opened,
                    openSidebar: this.sidebar.opened,
                    withoutAnimation: this.sidebar.withoutAnimation,
                    mobile: this.device === "mobile",
                };
            },
            isCollapse() {
                if (this.secondMenuPopup) {
                    return true;
                }
                return !this.sidebar.opened;
            },
        },
        data() {
            return {
                isCollapseM: false,
            };
        },
        methods: {
            handleClickOutside() {
                store.app().closeSidebar({ withoutAnimation: false });
            },
            handleClose() {
                store.app().toggleSidebar();
            },
        },
        beforeMount() {
            changeTheme(this.theme);
        },
        mounted() {
            document.addEventListener("click", throttleConect);
            document.addEventListener("mousemove", throttleConect);
            document.addEventListener("keydown", throttleConect);
        },
        unMounted() {
            document.removeEventListener("click", throttleConect);
            document.removeEventListener("mousemove", throttleConect);
            document.removeEventListener("keydown", throttleConect);
        },
    });
</script>
<style lang="scss" scoped>
@import "@/styles/mixin.scss";
@use "@/styles/mixin.scss";
.app-wrapper {
  @include clearfix;
  // @include clearfix;
  position: relative;
  height: 100%;
  width: 100%;
@@ -126,7 +157,7 @@
}
.hideSidebar .fixed-header {
  width: calc(100%)
  width: calc(100%);
}
.mobile .fixed-header {
src/main.ts
@@ -4,6 +4,10 @@
import 'element-plus/dist/index.css';
import router from './router';
import { setupStore } from './store';
import ElementPlus from "element-plus";
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import zhCn from "element-plus/es/locale/lang/zh-cn";
import '@/styles/index.scss';
import SvgIcon from './icons'; // icon
@@ -14,6 +18,12 @@
const app = createApp(App);
setupStore(app);
app.use(router);
app.use(ElementPlus, { locale: zhCn });
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}
app.component('svg-icon', SvgIcon);
app.directive('permission', vPermission);
checkEnableLogs(app);
src/permission.ts
@@ -30,7 +30,7 @@
    } else {
      // determine whether the user has obtained his permission roles through getInfo
      const hasRoles = userStore().roles && userStore().roles.length > 0;
      // console.log('hasRoles=', hasRoles);
      console.log('hasRoles=', hasRoles, userStore().roles ,  userStore().roles && userStore().roles.length);
      if (hasRoles) {
        next();
      } else {
@@ -46,14 +46,14 @@
          // generate accessible routes map based on roles
          const accessRoutes = await permissionStore().generateRoutes(roles);
          // console.log('accessRoutes=', accessRoutes)
          console.log('accessRoutes=', accessRoutes);
          // dynamically add accessible routes
          // router.addRoutes(accessRoutes);
          accessRoutes.forEach(item => {
            router.addRoute(item);
          });
          // console.log('next=', accessRoutes);
          console.log('next=', accessRoutes, to);
          // hack method to ensure that addRoutes is complete
          // set the replace: true, so the navigation will not leave a history record
src/router/index.js
File was renamed from src/router/index.ts
@@ -1,11 +1,11 @@
import { createRouter, createWebHashHistory } from 'vue-router'; // createWebHashHistory, createWebHistory
import type { Router, RouteRecordRaw, RouteComponent } from 'vue-router';
// import type { Router, RouteRecordRaw, RouteComponent } from 'vue-router';
import devicesRouter from './modules/devices';
import generalRouter from './modules/general';
import systemRouter from './modules/system';
/* Layout */
const Layout = ():RouteComponent => import('@/layout/index.vue');
const Layout = () => import('@/layout/index.vue');
/**
 * constantRoutes
@@ -14,7 +14,7 @@
 *
 * 注意:hidden、alwaysShow 属性配置移动到了meta中!!!
 */
export const constantRoutes:RouteRecordRaw[] = [
export const constantRoutes = [
  {
    path: '/redirect',
    component: Layout,
@@ -49,15 +49,16 @@
  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [
      {
        path: 'dashboard',
        component: () => import('@/views/dashboard/index.vue'),
        name: 'Dashboard',
        meta: { title: '首页', icon: 'home-hdw', affix: true }
      }
    ]
    // redirect: '/dashboard',
    redirect: '/device/lock',
    // children: [
    //   {
    //     path: 'dashboard',
    //     component: () => import('@/views/dashboard/index.vue'),
    //     name: 'Dashboard',
    //     meta: { title: '首页', icon: 'home-hdw', affix: true }
    //   }
    // ]
  }
];
@@ -67,7 +68,7 @@
 *
 * 注意:hidden、alwaysShow 属性配置移动到了meta中!!!
 */
export const asyncRoutes:RouteRecordRaw[] = [
export const asyncRoutes = [
  devicesRouter,
  generalRouter,
  systemRouter,
@@ -75,9 +76,11 @@
  { path: '/:pathMatch(.*)*', redirect: '/404', meta: { hidden: true }}
];
// console.log('asyncRoutes=', asyncRoutes, '=============');
console.log('BASE_URL=', import.meta.env);
const createTheRouter = ():Router => createRouter({
const createTheRouter = () => createRouter({
  history: createWebHashHistory(import.meta.env.BASE_URL),
  // 注意,如果要配置 HTML5 模式,则需要修改nginx配置,参考资料:
  // https://router.vuejs.org/zh/guide/essentials/history-mode.html
@@ -86,15 +89,15 @@
  routes: constantRoutes
});
interface RouterPro extends Router {
  matcher: unknown;
}
// interface RouterPro extends Router {
//   matcher: unknown;
// }
const router = createTheRouter() as RouterPro;
const router = createTheRouter() ;
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createTheRouter() as RouterPro;
  const newRouter = createTheRouter();
  router.matcher = newRouter.matcher; // reset router
}
src/router/modules/devices.js
File was renamed from src/router/modules/devices.ts
@@ -12,13 +12,14 @@
  children: [
    {
      path: 'lock',
      component: () => import('@/views/device/lock/index.vue'),
      component: () => import('@/views/device/locks/index.vue'),
      name: 'LockManage',
      meta: { title: '锁具管理', icon: 'lock-hdw', noCache: false }
      meta: { title: '锁具管理', icon: 'lock-hdw', affix: true, noCache: false }
    },
    {
      path: 'key',
      component: () => import('@/views/device/key/index.vue'),
      component: () => import('@/views/device/keys/index.vue'),
      // component: () => import('@/views/test/index2.vue'),
      name: 'KeyManage',
      meta: { title: '钥匙管理', icon: 'key-hdw', noCache: false }
    }
src/router/modules/general.js
File was renamed from src/router/modules/general.ts
@@ -7,7 +7,7 @@
  name: 'General',
  meta: {
    title: '通用管理',
    icon: 'general-hdw'
    icon: 'general-hdw',
  },
  children: [
    {
@@ -20,14 +20,14 @@
      path: 'log',
      component: () => import('@/views/general/log/index.vue'),
      name: 'LogManage',
      meta: { title: '日志管理', icon: 'log-hdw', noCache: false }
      meta: { title: '锁具日志', icon: 'log-hdw', noCache: false }
    },
    {
      path: 'map',
      component: () => import('@/views/general/map/index.vue'),
      name: 'MapManage',
      meta: { title: '地图定位', icon: 'map-hdw', noCache: false }
    }
    // {
    //   path: 'map',
    //   component: () => import('@/views/general/map/index.vue'),
    //   name: 'MapManage',
    //   meta: { title: '地图定位', icon: 'map-hdw', noCache: false, }
    // }
  ]
};
src/router/modules/system.js
File was renamed from src/router/modules/system.ts
@@ -21,6 +21,18 @@
      component: () => import('@/views/system/user/index.vue'),
      name: 'UserManage',
      meta: { title: '用户管理', icon: 'people-hdw', noCache: false }
    },
    {
      path: 'face',
      component: () => import('@/views/system/user/userFaceManager.vue'),
      name: 'UserFaceManage',
      meta: { title: '用户人脸管理', icon: 'people-hdw', noCache: false }
    },
    {
      path: 'logs',
      component: () => import('@/views/system/logs.vue'),
      name: 'sysLogs',
      meta: { title: '系统日志', icon: 'log-hdw', noCache: false }
    }
  ]
};
src/store/modules/permission.ts
@@ -13,6 +13,9 @@
 * @param route
 */
function hasPermission(roles:string[], route:RouteRecordRaw):boolean {
  console.trace('hasPermission');
  console.log('hasPermission', roles, route, '=============');
  if (route.meta && route.meta.roles) {
    const rolesArr = route.meta.roles as string[];
    return roles.some(role => rolesArr.includes(role));
@@ -53,15 +56,22 @@
    setRoutes(routes: RouteRecordRaw[]) {
      this.addRoutes = routes;
      this.routes = constantRoutes.concat(routes);
      console.log('this.routes=', this.routes, '=============', constantRoutes);
    },
    generateRoutes(roles: string[]) {
      let accessedRoutes;
      if (roles.includes('admin')) {
        console.log('1', '=============');
        accessedRoutes = asyncRoutes || [];
      } else {
        console.log('2', '=============');
        accessedRoutes = filterAsyncRoutes(asyncRoutes, roles);
      }
      this.setRoutes(accessedRoutes);
      console.log('accessedRoutes=', accessedRoutes, '=============');
      return accessedRoutes;
    }
  }
src/store/modules/ukey.js
New file
@@ -0,0 +1,252 @@
import SoftKey3W from "@/utils/Syunew3";
import { defineStore } from 'pinia';
export default defineStore({
  id: 'ukey',
  state() {
    return {
      connect: 0,     // 判断是否安装了客户端服务,0未安装,1已安装
      isIn: 0,     // uKey的插拔状态
      id: "",         // uKey的唯一编号
    }
  },
  getters: {
    getId(state) {
      if (state.isIn) {
        return state.id;
      } else {
        return "";
      }
    }
  },
  actions: {
    changeIsIn(isIn) {
      this.isIn = isIn;
    },
    changeConnect(isConnect) {
      this.connect = isConnect;
    },
    changeUKeyId(id) {
      this.id = id;
    },
    /**
     * 监控uKey的插拔事件
     * @param context
     * @returns {boolean}
     */
    load(isLoad) {
      //如果是IE10及以下浏览器,则跳过不处理,
      if (navigator.userAgent.indexOf("MSIE") > 0 && !navigator.userAgent.indexOf("opera") > -1) {
        //该例子只支持HTM5的浏览器
        return;
      }
      try {
        let s_pnp = new SoftKey3W();//创建UK类
        // context.commit('changeConnect', 0);     //客户端服务安装状态
        this.changeConnect(0);
        s_pnp.Socket_UK.onopen = () => {
          // context.commit('changeConnect', 1);//代表已经连接,用于判断是否安装了客户端服务
          this.changeConnect(1);
        }
        // uKey插拔事件监听
        s_pnp.Socket_UK.onmessage = (Msg) => {
          let PnpData = JSON.parse(Msg.data);
          console.log('PnpData', PnpData)
          if (PnpData.type == "PnpEvent")//如果是插拨事件处理消息
          {
            if (PnpData.IsIn) {
              // context.commit('changeIsIn', 1);   // 已插入uKey
              this.changeIsIn(1);
            } else {
              // context.commit('changeIsIn', 0);   // 未插入uKey
              this.changeIsIn(0);
              // context.commit('changeId', "");     // 初始化uKeyId为空
              this.changeUKeyId("");
            }
          }
        }
        s_pnp.Socket_UK.onclose = () => {
          console.log('关闭监控插拔监控');
        }
      } catch (error) {
        console.log(error.name + ": " + error.message);
        return false;
      }
    },
    /**
     * 检测是否插入uKey
     * @param context
     * @param isConnent 客户端服务安装状态
     */
    checkIsIn(isConnent) {
      if (isConnent) {
        try {
          let s_simnew1 = new SoftKey3W(); //创建UK类
          let DevicePath, version;
          s_simnew1.Socket_UK.onopen = () => {
            s_simnew1.ResetOrder();//这里调用ResetOrder将计数清零,这样,消息处理处就会收到0序号的消息,通过计数及序号的方式,从而生产流程
          }
          s_simnew1.Socket_UK.onmessage = (Msg) => {
            let UK_Data = JSON.parse(Msg.data);
            if (UK_Data.type != "Process") return;//如果不是流程处理消息,则跳过
            switch (UK_Data.order) {
              case 0:
                {
                  s_simnew1.FindPort(0); //发送命令取UK的路径
                }
                break;
              case 1:
                {
                  if (UK_Data.LastError != 0) {
                    // context.commit('changeIsIn', 0);    // 未插入
                    this.changeIsIn(0);
                    // context.commit('changeId', "");     // 初始化uKeyId为空
                    this.changeUKeyId("");
                    s_simnew1.Socket_UK.close();
                    return false;
                  }
                  // context.commit('changeIsIn', 1);    // 已插入
                  this.changeIsIn(1);
                  DevicePath = UK_Data.return_value;//获得返回的UK的路径
                  s_simnew1.GetVersion(DevicePath); //发送命令取UK的版本
                }
                break;
              case 2:
                {
                  if (UK_Data.LastError != 0) {
                    console.log("返回版本号错误,错误码为:" + UK_Data.LastError.toString());
                    s_simnew1.Socket_UK.close();
                    return false;
                  }
                  version = UK_Data.return_value;//获得返回的UK的版本
                  if (version > 10) {
                    //取得锁的出厂编码
                    s_simnew1.GetProduceDate(DevicePath);//发送命令取UK的出厂编码
                  } else {
                    console.log("锁的版本少于11,不支持返回锁的出厂编码功能\n2013年9月15号以后出厂的锁都支持这个功能。");
                  }
                }
                break;
              case 3:
                {
                  if (UK_Data.LastError != 0) {
                    console.log("取得锁的出厂编码错误,错误码为:" + UK_Data.LastError.toString());
                    s_simnew1.Socket_UK.close();
                    return false;
                  }
                  let id = UK_Data.return_value;//获得返回的UK的出厂编码
                  // context.commit('changeId', id);
                  this.changeUKeyId(id);
                  //所有工作处理完成后,关掉Socket
                  s_simnew1.Socket_UK.close();
                }
                break;
              default:
                {
                  //所有工作处理完成后,关掉Socket
                  s_simnew1.Socket_UK.close();
                }
                break;
            }
          }
          s_simnew1.Socket_UK.onclose = () => {
            console.log('初始化设置uKey插拔状态和uKey唯一编号监控程序关闭');
          }
          return true;
        } catch (e) {
          console.log('设置uKey插拔状态和uKey唯一编号监控程序报错');
          console.log(error.name + ": " + error.message);
          return false;
        }
      } else {
        // context.commit('changeIsIn', 0);
        this.changeIsIn(0);
      }
    },
    changeId(isIn) {
      if (isIn) {
        try {
          let s_simnew1 = new SoftKey3W(); //创建UK类
          let DevicePath, version;
          s_simnew1.Socket_UK.onopen = () => {
            s_simnew1.ResetOrder();//这里调用ResetOrder将计数清零,这样,消息处理处就会收到0序号的消息,通过计数及序号的方式,从而生产流程
          }
          s_simnew1.Socket_UK.onmessage = (Msg) => {
            let UK_Data = JSON.parse(Msg.data);
            if (UK_Data.type != "Process") return;//如果不是流程处理消息,则跳过
            switch (UK_Data.order) {
              case 0:
                {
                  s_simnew1.FindPort(0); //发送命令取UK的路径
                }
                break;
              case 1:
                {
                  if (UK_Data.LastError != 0) {
                    // context.commit('changeId', "");     // 初始化uKeyId为空
                    this.changeUKeyId("");
                    s_simnew1.Socket_UK.close();
                    return false;
                  }
                  DevicePath = UK_Data.return_value;//获得返回的UK的路径
                  s_simnew1.GetVersion(DevicePath); //发送命令取UK的版本
                }
                break;
              case 2:
                {
                  if (UK_Data.LastError != 0) {
                    console.log("返回版本号错误,错误码为:" + UK_Data.LastError.toString());
                    s_simnew1.Socket_UK.close();
                    return false;
                  }
                  version = UK_Data.return_value;//获得返回的UK的版本
                  if (version > 10) {
                    //取得锁的出厂编码
                    s_simnew1.GetProduceDate(DevicePath);//发送命令取UK的出厂编码
                  } else {
                    console.log("锁的版本少于11,不支持返回锁的出厂编码功能\n2013年9月15号以后出厂的锁都支持这个功能。");
                  }
                }
                break;
              case 3:
                {
                  if (UK_Data.LastError != 0) {
                    console.log("取得锁的出厂编码错误,错误码为:" + UK_Data.LastError.toString());
                    s_simnew1.Socket_UK.close();
                    return false;
                  }
                  let id = UK_Data.return_value;//获得返回的UK的出厂编码
                  // context.commit('changeId', id);
                  this.changeUKeyId(id);
                  //所有工作处理完成后,关掉Socket
                  s_simnew1.Socket_UK.close();
                }
                break;
              default:
                {
                  //所有工作处理完成后,关掉Socket
                  s_simnew1.Socket_UK.close();
                }
                break;
            }
          }
          s_simnew1.Socket_UK.onclose =  () => {
            console.log('设置uKey唯一编号监控程序关闭');
          }
          return true;
        } catch (e) {
          console.log('设置uKey唯一编号监控程序报错');
          console.log(error.name + ": " + error.message);
          return false;
        }
      } else {
        // context.commit("changeId", "");
        this.changeUKeyId("");
      }
    }
  }
});
src/store/modules/user.js
@@ -1,6 +1,6 @@
import { defineStore } from 'pinia';
import { login as apiLogin, logout as apiLogout } from '@/api/user';
import { getToken, removeToken, setToken } from '@/utils/auth';
import { getToken, removeToken, setToken, getUname, setUname, removeUname, getUrole, setUrole, removeUrole } from '@/utils/auth';
import router, { resetRouter } from '@/router';
import tagsViewStore from './tagsView';
import permissionStore from './permission';
@@ -10,8 +10,9 @@
  state: () => ({
    token: getToken(),
    userId: '',
    name: '',
    name: getUname(),
    avatar: '',
    urole: getUrole(),
    introduction: '',
    roles: []
  }),
@@ -23,16 +24,23 @@
      return new Promise((resolve, reject) => {
        apiLogin(username.trim(), password).then((response) => {
          if (response.code === 1 && response.data) {
            let name = response.data2.uname;
            let role = response.data2.urole;
            this.name = name;
            setUname(name);
            this.token = 'admin';
            setToken('admin');
            this.urole = role;
            setUrole(role);
            resolve(response);
          } else {
            reject('用户名/密码错误');
            // reject('用户名/密码错误');
            reject(response.msg);
          }
        }).catch(error => {
          reject(error);
        });
      });
      });u
    },
    // get user info
@@ -42,9 +50,10 @@
          roles: ['admin'],
          introduction: 'I am a super administrator',
          avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
          name: 'Super Admin'
          // name: 'Super Admin'
          name: getUname()
        };
        const { roles, name, avatar, introduction } = data;
        const { roles, name,  avatar, introduction } = data;
        this.roles = roles;
        this.name = name;
        this.avatar = avatar;
@@ -60,6 +69,8 @@
          this.token = '';
          this.roles = [];
          removeToken();
          removeUname();
          removeUrole();
          resetRouter();
          // reset visited views and cached views
src/utils/SoftUKey.js
New file
@@ -0,0 +1,284 @@
function SoftUKey(SoftKey3W) {
    this.SoftKey3W = SoftKey3W;
}
/**
 * uKey写入用户名
 * @param userName 用户名
 * @param callback 回调函数
 */
SoftUKey.prototype.setUserName = function (userName, callback) {
    try {
        let s_simnew1 = new this.SoftKey3W();     //创建UK类
        let DevicePath, PriKey, PubKeyX, PubKeyY;
        // 开启监听
        s_simnew1.Socket_UK.onopen = function() {
            s_simnew1.ResetOrder();//这里调用ResetOrder将计数清零,这样,消息处理处就会收到0序号的消息,通过计数及序号的方式,从而生产流程
        }
        // 监听获取到的信息
        s_simnew1.Socket_UK.onmessage = function(Msg) {
            let UK_Data = JSON.parse(Msg.data);
            if(UK_Data.type!="Process")return ;//如果不是流程处理消息,则跳过
            // 根据收到的序号执行函数
            switch(UK_Data.order) {
                case 0:
                    s_simnew1.FindPort(0); //发送命令取UK的路径
                    break;
                case 1:
                    if(UK_Data.LastError!=0) {
                        window.alert ( "未发现加密锁,请插入加密锁");
                        s_simnew1.Socket_UK.close();
                        return false;
                    }
                    // 设置路径
                    DevicePath=UK_Data.return_value;//获得返回的UK的路径
                    //产生密钥对
                    s_simnew1.StarGenKeyPair(DevicePath);
                    break;
                case 2:
                    if(UK_Data.LastError!=0){
                        window.alert("产生密钥对出现错误,错误码为:"+UK_Data.LastError.toString());
                        s_simnew1.Socket_UK.close();
                        return false;
                    }
                    //获取生成的私钥
                    s_simnew1.GenPriKey();
                    break;
                case 3:
                    if(UK_Data.LastError!=0){
                        window.alert("获取生成的私钥时错误,错误码为:"+UK_Data.LastError.toString());
                        s_simnew1.Socket_UK.close();
                        return false;
                    }
                    PriKey = UK_Data.return_value;
                    //获取生成的公钥X
                    s_simnew1.GenPubKeyX();
                    break;
                case 4:
                    if(UK_Data.LastError!=0){
                        window.alert("获取生成的公钥X时错误,错误码为:"+UK_Data.LastError.toString());
                        s_simnew1.Socket_UK.close();
                        return false;
                    }
                    PubKeyX = UK_Data.return_value;
                    //获取生成的公钥Y
                    s_simnew1.GenPubKeyY();
                    break;
                case 5:
                    if(UK_Data.LastError!=0){
                        window.alert("获取生成的公钥Y时错误,错误码为:"+UK_Data.LastError.toString());
                        s_simnew1.Socket_UK.close();
                        return false;
                    }
                    PubKeyY = UK_Data.return_value;
                    //设置密钥对及用户身份到锁中
                    s_simnew1.Set_SM2_KeyPair(PriKey,PubKeyX,PubKeyY,userName,DevicePath);
                    break;
                case 6:
                    if(UK_Data.LastError!=0){
                        window.alert("设置密钥对及用户身份时出现错误,错误码为:"+UK_Data.LastError.toString());
                        s_simnew1.Socket_UK.close();
                        return false;
                    }
                    // 执行回调函数
                    if(typeof callback == 'function') {
                        callback({
                            PriKey,
                            PubKeyX,
                            PubKeyY,
                            userName,
                            DevicePath
                        });
                    }
                    //所有工作处理完成后,关掉Socket
                    s_simnew1.Socket_UK.close();
                    break;
            }
        }
        // 关闭监听
        s_simnew1.Socket_UK.onclose = function() {
            console.log("关闭设置用户名称")
        }
        return true;
    }catch(e) {
        console.log("设置用户名称异常:"+e);
    }
}
/**
 * 读取uKey中的用户名
 * @param callback
 * @return {boolean}
 */
SoftUKey.prototype.getUserName = function(callback) {
    try {
        let s_simnew1 = new this.SoftKey3W();     //创建UK类
        let DevicePath, userName;
        // 开启监听
        s_simnew1.Socket_UK.onopen = function() {
            s_simnew1.ResetOrder();//这里调用ResetOrder将计数清零,这样,消息处理处就会收到0序号的消息,通过计数及序号的方式,从而生产流程
        }
        // 监听获取到的信息
        s_simnew1.Socket_UK.onmessage = function(Msg) {
            let UK_Data = JSON.parse(Msg.data);
            if(UK_Data.type!="Process")return ;//如果不是流程处理消息,则跳过
            // 根据收到的序号执行函数
            switch(UK_Data.order) {
                case 0:
                    s_simnew1.FindPort(0); //发送命令取UK的路径
                    break;
                case 1:
                    if(UK_Data.LastError!=0) {
                        window.alert ( "未发现加密锁,请插入加密锁");
                        s_simnew1.Socket_UK.close();
                        return false;
                    }
                    // 设置路径
                    DevicePath=UK_Data.return_value;//获得返回的UK的路径
                    //返回设置在锁中的身份
                    s_simnew1.GetSm2UserName(DevicePath);
                    break;
                case 2:
                    if(UK_Data.LastError!=0){
                        window.alert("返回设置在锁中的用户身份时出现错误,错误码为:"+UK_Data.LastError.toString());
                        s_simnew1.Socket_UK.close();
                        return false;
                    }
                    userName = UK_Data.return_value;
                    // 执行回调函数
                    if(typeof callback == 'function') {
                        callback({
                            userName,
                        });
                    }
                    //所有工作处理完成后,关掉Socket
                    s_simnew1.Socket_UK.close();
                    break;
            }
        }
        // 关闭监听
        s_simnew1.Socket_UK.onclose = function() {
            console.log("关闭读取用户名称")
        }
        return true;
    }catch (e) {
        console.log("读取用户名称异常:"+e);
    }
}
/**
 * 获取ukey的签名
 * @param randomNumber
 * @param callback
 */
SoftUKey.prototype.getSign = function(randomNumber, callback) {
    try {
        let s_simnew1 = new this.SoftKey3W();     //创建UK类
        let inPath, sign, uKeyId, publicX, publicY;
        // 开启监听
        s_simnew1.Socket_UK.onopen = function() {
            s_simnew1.ResetOrder();//这里调用ResetOrder将计数清零,这样,消息处理处就会收到0序号的消息,通过计数及序号的方式,从而生产流程
        }
        // 监听获取到的信息
        s_simnew1.Socket_UK.onmessage = function(Msg) {
            let UK_Data = JSON.parse(Msg.data);
            if(UK_Data.type!="Process")return ;//如果不是流程处理消息,则跳过
            // 根据收到的序号执行函数
            switch(UK_Data.order) {
                case 0:
                    s_simnew1.FindPort(0); //发送命令取UK的路径
                    break;
                case 1:
                    if(UK_Data.LastError!=0) {
                        window.alert ( "未发现加密锁,请插入加密锁");
                        s_simnew1.Socket_UK.close();
                        return false;
                    }
                    // 设置路径
                    inPath=UK_Data.return_value;//获得返回的UK的路径
                    s_simnew1.GetProduceDate(inPath);//返回芯片唯一ID
                    break;
                case 2:
                    if(UK_Data.LastError!=0){
                        window.alert("返回芯片唯一ID时出现错误,错误码为:"+UK_Data.LastError.toString());
                        s_simnew1.Socket_UK.close();
                        return false;
                    }
                    uKeyId = UK_Data.return_value;
                    //返回设置在锁中的公钥X
                    s_simnew1.GetPubKeyX(inPath);
                    break;
                case 3:
                    if(UK_Data.LastError!=0){
                        window.alert("返回设置在锁中的公钥X时出现错误,错误码为:"+UK_Data.LastError.toString());
                        s_simnew1.Socket_UK.close();
                        return false;
                    }
                    publicX = UK_Data.return_value;
                    // 返回设置在锁中的公钥Y
                    s_simnew1.GetPubKeyY(inPath);
                    break;
                case 4:
                    if(UK_Data.LastError!=0){
                        window.alert("返回设置在锁中的公钥Y时出现错误,错误码为:"+UK_Data.LastError.toString());
                        s_simnew1.Socket_UK.close();
                        return false;
                    }
                    publicY = UK_Data.return_value;
                    // 对数据进行签名
                    s_simnew1.YtSign(randomNumber,"123", inPath);
                    break;
                case 5:
                    if(UK_Data.LastError!=0){
                        window.alert("对数据进行签名时出现错误,错误码为:"+UK_Data.LastError.toString());
                        s_simnew1.Socket_UK.close();
                        return false;
                    }
                    sign = UK_Data.return_value;
                    // 执行回调函数
                    if(typeof callback == 'function') {
                        callback({
                            inPath,
                            sign,
                            uKeyId,
                            publicX,
                            publicY,
                        });
                    }
                    //所有工作处理完成后,关掉Socket
                    s_simnew1.Socket_UK.close();
                    break;
            }
        }
        // 关闭监听
        s_simnew1.Socket_UK.onclose = function() {
            console.log("关闭获取签名")
        }
    }catch (e) {
        console.log("获取签名异常:"+e)
    }
}
export default SoftUKey;
src/utils/Syunew3.js
New file
@@ -0,0 +1,592 @@
function SoftKey3W() {
  var isIE11 =
    navigator.userAgent.indexOf("Trident") > -1 &&
    navigator.userAgent.indexOf("rv:11.0") > -1;
  var isEDGE = navigator.userAgent.indexOf("Edge") > -1;
  var u = document.URL;
  var url;
  if (u.substring(0, 5) == "https") {
    if (isIE11 || isEDGE) {
      if (isIE11) url = "wss://127.0.0.1:4006/xxx";
      else url = "ws://127.0.0.1:4006/xxx";
    } else {
      url = "ws://localhost:4006/xxx";
    }
  } else {
    url = "ws://127.0.0.1:4006/xxx";
  }
  var Socket_UK;
  if (typeof MozWebSocket != "undefined") {
    Socket_UK = new MozWebSocket(url, "usbkey-protocol");
  } else {
    this.Socket_UK = new WebSocket(url, "usbkey-protocol");
  }
  this.FindPort = function (start) {
    var msg = {
      FunName: "FindPort",
      start: start,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.FindPort_2 = function (start, in_data, verf_data) {
    var msg = {
      FunName: "FindPort_2",
      start: start,
      in_data: in_data,
      verf_data: verf_data,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.FindPort_3 = function (start, in_data, verf_data) {
    var msg = {
      FunName: "FindPort_3",
      start: start,
      in_data: in_data,
      verf_data: verf_data,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.GetVersion = function (Path) {
    var msg = {
      FunName: "GetVersion",
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.GetVersionEx = function (Path) {
    var msg = {
      FunName: "GetVersionEx",
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.GetID_1 = function (Path) {
    var msg = {
      FunName: "GetID_1",
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.GetID_2 = function (Path) {
    var msg = {
      FunName: "GetID_2",
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.sRead = function (Path) {
    var msg = {
      FunName: "sRead",
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.sWrite = function (InData, Path) {
    var msg = {
      FunName: "sWrite",
      InData: InData,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.sWrite_2 = function (InData, Path) {
    var msg = {
      FunName: "sWrite_2",
      InData: InData,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.sWrite_2Ex = function (InData, Path) {
    var msg = {
      FunName: "sWrite_2Ex",
      InData: InData,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.sWriteEx = function (InData, Path) {
    var msg = {
      FunName: "sWriteEx",
      InData: InData,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.sWriteEx_New = function (InData, Path) {
    var msg = {
      FunName: "sWriteEx_New",
      InData: InData,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.sWrite_2Ex_New = function (InData, Path) {
    var msg = {
      FunName: "sWrite_2Ex_New",
      InData: InData,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.SetCal = function (Hkey, Lkey, new_Hkey, new_Lkey, Path) {
    var msg = {
      FunName: "SetCal",
      Hkey: Hkey,
      Lkey: Lkey,
      new_Hkey: new_Hkey,
      new_Lkey: new_Lkey,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.SetBuf = function (InData, pos) {
    var msg = {
      FunName: "SetBuf",
      InData: InData,
      pos: pos,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.GetBuf = function (pos) {
    var msg = {
      FunName: "GetBuf",
      pos: pos,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.YRead = function (Address, HKey, LKey, Path) {
    var msg = {
      FunName: "YRead",
      Address: Address,
      HKey: HKey,
      LKey: LKey,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.YWrite = function (InData, Address, HKey, LKey, Path) {
    var msg = {
      FunName: "YWrite",
      InData: InData,
      Address: Address,
      HKey: HKey,
      LKey: LKey,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.YReadEx = function (Address, len, HKey, LKey, Path) {
    var msg = {
      FunName: "YReadEx",
      Address: Address,
      len: len,
      HKey: HKey,
      LKey: LKey,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.YWriteEx = function (Address, len, HKey, LKey, Path) {
    var msg = {
      FunName: "YWriteEx",
      Address: Address,
      len: len,
      HKey: HKey,
      LKey: LKey,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.YReadString = function (Address, len, HKey, LKey, Path) {
    var msg = {
      FunName: "YReadString",
      Address: Address,
      len: len,
      HKey: HKey,
      LKey: LKey,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.YWriteString = function (InString, Address, HKey, LKey, Path) {
    var msg = {
      FunName: "YWriteString",
      InString: InString,
      Address: Address,
      HKey: HKey,
      LKey: LKey,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.SetWritePassword = function (W_Hkey, W_Lkey, new_Hkey, new_Lkey, Path) {
    var msg = {
      FunName: "SetWritePassword",
      W_Hkey: W_Hkey,
      W_Lkey: W_Lkey,
      new_Hkey: new_Hkey,
      new_Lkey: new_Lkey,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.SetReadPassword = function (W_Hkey, W_Lkey, new_Hkey, new_Lkey, Path) {
    var msg = {
      FunName: "SetReadPassword",
      W_Hkey: W_Hkey,
      W_Lkey: W_Lkey,
      new_Hkey: new_Hkey,
      new_Lkey: new_Lkey,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.DecString = function (InString, Key) {
    var msg = {
      FunName: "DecString",
      InString: InString,
      Key: Key,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.EncString = function (InString, Path) {
    var msg = {
      FunName: "EncString",
      InString: InString,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.EncString_New = function (InString, Path) {
    var msg = {
      FunName: "EncString_New",
      InString: InString,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.Cal = function (Path) {
    var msg = {
      FunName: "Cal",
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.Cal_New = function (Path) {
    var msg = {
      FunName: "Cal_New",
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.SetCal_2 = function (Key, Path) {
    var msg = {
      FunName: "SetCal_2",
      Key: Key,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.SetCal_New = function (Key, Path) {
    var msg = {
      FunName: "SetCal_New",
      Key: Key,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.SetEncBuf = function (InData, pos) {
    var msg = {
      FunName: "SetEncBuf",
      InData: InData,
      pos: pos,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.GetEncBuf = function (pos) {
    var msg = {
      FunName: "GetEncBuf",
      pos: pos,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.ReSet = function (Path) {
    var msg = {
      FunName: "ReSet",
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.SetID = function (Seed, Path) {
    var msg = {
      FunName: "SetID",
      Seed: Seed,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.GetProduceDate = function (Path) {
    var msg = {
      FunName: "GetProduceDate",
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.MacAddr = function () {
    var msg = {
      FunName: "MacAddr",
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.GetChipID = function (Path) {
    var msg = {
      FunName: "GetChipID",
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.StarGenKeyPair = function (Path) {
    var msg = {
      FunName: "StarGenKeyPair",
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.GenPubKeyY = function () {
    var msg = {
      FunName: "GenPubKeyY",
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.GenPubKeyX = function () {
    var msg = {
      FunName: "GenPubKeyX",
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.GenPriKey = function () {
    var msg = {
      FunName: "GenPriKey",
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.GetPubKeyY = function (Path) {
    var msg = {
      FunName: "GetPubKeyY",
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.GetPubKeyX = function (Path) {
    var msg = {
      FunName: "GetPubKeyX",
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.GetSm2UserName = function (Path) {
    var msg = {
      FunName: "GetSm2UserName",
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.Set_SM2_KeyPair = function (
    PriKey,
    PubKeyX,
    PubKeyY,
    sm2UserName,
    Path
  ) {
    var msg = {
      FunName: "Set_SM2_KeyPair",
      PriKey: PriKey,
      PubKeyX: PubKeyX,
      PubKeyY: PubKeyY,
      sm2UserName: sm2UserName,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.YtSign = function (SignMsg, Pin, Path) {
    var msg = {
      FunName: "YtSign",
      SignMsg: SignMsg,
      Pin: Pin,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.YtSign_2 = function (SignMsg, Pin, Path) {
    var msg = {
      FunName: "YtSign_2",
      SignMsg: SignMsg,
      Pin: Pin,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.YtVerfiy = function (id, SignMsg, PubKeyX, PubKeyY, VerfiySign, Path) {
    var msg = {
      FunName: "YtVerfiy",
      id: id,
      SignMsg: SignMsg,
      PubKeyX: PubKeyX,
      PubKeyY: PubKeyY,
      VerfiySign: VerfiySign,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.SM2_DecString = function (InString, Pin, Path) {
    var msg = {
      FunName: "SM2_DecString",
      InString: InString,
      Pin: Pin,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.SM2_EncString = function (InString, Path) {
    var msg = {
      FunName: "SM2_EncString",
      InString: InString,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.YtSetPin = function (OldPin, NewPin, Path) {
    var msg = {
      FunName: "YtSetPin",
      OldPin: OldPin,
      NewPin: NewPin,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.FindU = function (start) {
    var msg = {
      FunName: "FindU",
      start: start,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.FindU_2 = function (start, in_data, verf_data) {
    var msg = {
      FunName: "FindU_2",
      start: start,
      in_data: in_data,
      verf_data: verf_data,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.FindU_3 = function (start, in_data, verf_data) {
    var msg = {
      FunName: "FindU_3",
      start: start,
      in_data: in_data,
      verf_data: verf_data,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.IsUReadOnly = function (Path) {
    var msg = {
      FunName: "IsUReadOnly",
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.SetUReadOnly = function (Path) {
    var msg = {
      FunName: "SetUReadOnly",
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.SetHidOnly = function (IsHidOnly, Path) {
    var msg = {
      FunName: "SetHidOnly",
      IsHidOnly: IsHidOnly,
      Path: Path,
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.ResetOrder = function () {
    var msg = {
      FunName: "ResetOrder",
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
  this.ContinueOrder = function () {
    var msg = {
      FunName: "ContinueOrder",
    };
    this.Socket_UK.send(JSON.stringify(msg));
  };
}
export default SoftKey3W;
src/utils/auth.js
@@ -13,3 +13,27 @@
export function removeToken() {
  return Cookies.remove(TokenKey);
}
export function getUname() {
  return Cookies.get('uname');
}
export function setUname(uname) {
  return Cookies.set('uname', uname);
}
export function removeUname() {
  return Cookies.remove('uname');
}
export function getUrole() {
  return Cookies.get('urole');
}
export function setUrole(urole) {
  return Cookies.set('urole', urole);
}
export function removeUrole() {
  return Cookies.remove('urole');
}
src/utils/config.js
New file
@@ -0,0 +1,7 @@
export default {
  // 平台是否需要ukey 才能登录
  ukey: true,
  // ukey: false,
  // 是否需要人脸识别
  face: true,
}
src/utils/getWsUrl.js
New file
@@ -0,0 +1,23 @@
/**
 * 获取Websocket的连接
 * @param action
 * @returns {string}
 */
function getWsUrl(action, port) {
  let _port = port ? port : 8100;
  let hostname = window.location.hostname;
  let wsProtocol = "ws://";
  if (window.location.protocol == "https:") {
    wsProtocol = "wss://";
  }
  if (process.env.NODE_ENV == "development") {
    hostname = "localhost";
  } else {
    _port = window.location.port;
  }
  // 处理端口为80
  _port = _port == 80 ? "" : ":" + _port;
  return wsProtocol + hostname + _port + "/bl/" + action;
}
export default getWsUrl;
src/utils/request.js
@@ -10,7 +10,7 @@
const service = axios.create({
  baseURL: baseURL, // url = base url + request url
  withCredentials: true, // send cookies when cross-domain requests
  timeout: 10000 // request timeout
  // timeout: 20000 // request timeout
});
// request interceptor
src/utils/throttle.js
New file
@@ -0,0 +1,19 @@
// 节流
export const throttle = function (fn, delay = 300) {
  var lastTime, timer;
  return function () {
    var _this = this;
    var args = arguments;
    var nowTime = Date.now();
    if (lastTime && nowTime - lastTime < delay) {
      if (timer) clearTimeout(timer);
      timer = setTimeout(function () {
        lastTime = nowTime;
        fn.apply(_this, args);
      }, delay);
    } else {
      lastTime = nowTime;
      fn.apply(_this, args);
    }
  };
};
src/utils/tree.js
@@ -1,10 +1,14 @@
export function formatAreaTree(item, list) {
  if (item.parentId === 0) {
export function formatAreaTree(item, ids, list) {
  // parentId 不在id列表中;
  // if (item.parentId === 0) {
  if (ids.indexOf(item.parentId) === -1) {
    list.push({
      label: item.areaName,
      id: item.id,
      data: item,
      children: []
      areaDescript: item.areaDescript,
      charger: item.areaUsers.map((v) => v.uname).join(","),
      children: [],
    });
  } else {
    let isCurrentChild = false;
@@ -16,7 +20,9 @@
          label: item.areaName,
          id: item.id,
          data: item,
          children: []
          areaDescript: item.areaDescript,
          charger: item.areaUsers.map((v) => v.uname).join(","),
          children: [],
        });
      }
    }
@@ -24,7 +30,7 @@
    for (let i = 0; i < list.length; i++) {
      const listItem = list[i];
      if (!isCurrentChild && listItem.children !== 0) {
        formatAreaTree(item, listItem.children);
        formatAreaTree(item, ids, listItem.children);
      }
    }
  }
src/views/device/key/index.vue
File was deleted
src/views/device/keys/addEdit.vue
New file
@@ -0,0 +1,192 @@
<script setup>
import { ref, reactive, computed, onMounted } from "vue";
import { addKey, updateKey } from "@/api/keys";
import useElement from "@/hooks/useElement.js";
import { getAllUserName } from "@/api/user";
const { $loading, $message, $confirm } = useElement();
const props = defineProps({
  info: {
    type: Object,
  },
});
const formRef = ref();
const areaList = ref([]);
const userList = ref([]);
const form1 = reactive({
  uname: "",
  keyType: "",
  uid: "",
  keyNumber: "",
  keyName: "",
});
const rules = {
  uname: [
    {
      required: true,
      message: "不能为空",
      trigger: ["blur", "change"],
    },
  ],
  keyType: [
    {
      required: true,
      message: "不能为空",
      trigger: ["blur", "change"],
    },
  ],
  keyName: [
    {
      required: true,
      message: "不能为空",
      trigger: ["blur", "change"],
    },
  ],
  keyNumber: [
    {
      required: true,
      message: "不能为空",
      trigger: ["change", "blur"],
    },
  ],
};
const isEdit = computed(() => !!props.info?.keyName);
const $emit = defineEmits(["cancel", "success"]);
function close() {
  $emit("cancel");
}
// 查询所有用户
async function getAllUser() {
  let res = await getAllUserName();
  let { code, data, data2 } = res;
  let _list = [];
  if (code && data) {
    _list = data2;
  }
  userList.value = _list;
}
async function update() {
  let valid = await formRef.value.validate(() => { });
  // console.log('valid', valid, '=============');
  if (!valid) {
    $message.error("表单验证失败");
    return false;
  }
  let params = {
    // uid: form1.uid,
    uname: form1.uname,
    keyName: form1.keyName?.trim(),
    keyNumber: form1.keyNumber?.trim() || undefined,
    keyId: props.info.keyId,
    keyType: form1.keyType
  };
  console.log("params update", params, "=============");
  let loading = $loading();
  updateKey(params)
    .then((res) => {
      let { code, data } = res;
      if (code && data) {
        $emit("success");
        $message.success("操作成功");
      } else {
        $message.error("操作失败");
      }
      loading.close();
    })
    .catch((err) => {
      console.log(err);
      loading.close();
    });
}
async function onSubmit() {
  let valid = await formRef.value.validate(() => { });
  // console.log('valid', valid, '=============');
  if (!valid) {
    $message.error("表单验证失败");
    return false;
  }
  let params = {
    // uid: form1.uid,
    uname: form1.uname,
    keyName: form1.keyName?.trim(),
    keyNumber: form1.keyNumber?.trim() || undefined,
    keyType: form1.keyType
  };
  console.log("params", params, "=============");
  let loading = $loading();
  addKey(params)
    .then((res) => {
      let { code, data } = res;
      if (code && data) {
        $emit("success");
        $message.success("操作成功");
      } else {
        $message.error("操作失败");
      }
      loading.close();
    })
    .catch((err) => {
      console.log(err);
      loading.close();
    });
}
onMounted(() => {
  let info = props.info;
  if (info) {
    form1.uname = info.uname;
    form1.uid = info.uid;
    form1.keyType = info.keyType;
    form1.keyName = info.keyName;
    form1.keyNumber = info.keyNumber;
  }
  getAllUser();
});
</script>
<template>
  <div class="">
    <el-form ref="formRef" :model="form1" label-width="80px" :rules="rules">
      <el-form-item label="归属人" prop="uname">
        <el-select v-model="form1.uname">
          <el-option v-for="(item, idx) in userList" :key="'key_' + idx" :label="item" :value="item" />
        </el-select>
      </el-form-item>
      <el-form-item label="钥匙类型" prop="keyType">
        <el-select v-model="form1.keyType">
          <el-option label="ID钥匙" :value="1" />
          <el-option label="机械钥匙" :value="2" />
        </el-select>
      </el-form-item>
      <el-form-item label="钥匙名称" prop="keyName">
        <el-input v-model="form1.keyName"></el-input>
      </el-form-item>
      <el-form-item label="钥匙编号" prop="keyNumber" v-if="form1.keyType == 1">
        <el-input v-model="form1.keyNumber"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button v-if="isEdit" type="primary" @click="update">修改</el-button>
        <el-button v-else type="primary" @click="onSubmit">新增</el-button>
        <el-button @click="close">取消</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>
<style scoped lang="scss">
:deep(.select) {
  width: 100%;
}
</style>
src/views/device/keys/index.vue
New file
@@ -0,0 +1,283 @@
<script setup name="KeyManage">
import { ref, onMounted, reactive, computed } from 'vue';
import addEdit from './addEdit.vue';
import useElement from "@/hooks/useElement.js";
import store from '@/store';
import { Search, Plus } from "@element-plus/icons-vue";
import {
  getAllKeyList,
  addKey,
  delKey,
} from '@/api/keys.js';
const dialogTitle = ref('添加钥匙');
const addEditVisible = ref(false);
const background = ref(true);
const disabled = ref(false);
const isAdd = ref(false);
const { $loading, $message, $confirm } = useElement();
const pageCurr = ref(1);
const pageSize = ref(10);
const total = ref(0);
const datas = reactive({
  tableData: [],
  keyInfo: {},
});
const urole = computed(() => {
  return store.user().urole;
});
function onOk() {
  addEditVisible.value = false;
  getList();
}
function onCanel() {
  addEditVisible.value = false;
}
const filter = reactive({
  keyName: '',
  userName: ''
});
function add() {
  dialogTitle.value = '添加钥匙';
  datas.keyInfo = {};
  addEditVisible.value = true;
}
function del(scope) {
  $confirm('删除该钥匙', () => {
    let loading = $loading();
    delKey(scope.row.keyId).then((res) => {
      let { code, data } = res;
      if (code && data) {
        $message({
          type: 'success',
          message: '删除成功!'
        });
        getList();
      } else {
        $message({
          type: 'error',
          message: '删除失败!'
        });
      }
      loading.close();
    });
  });
}
function edit(scope) {
  dialogTitle.value = '编辑钥匙';
  datas.keyInfo = scope.row;
  // console.log('edit', scope.row, '=============');
  addEditVisible.value = true;
}
function getList() {
  //  keyName, pageNum, pageSize, uname
  let params = {
    pageNum: pageCurr.value,
    pageSize: pageSize.value,
    keyName: filter.keyName || undefined,
    uname: filter.userName || undefined
  };
  let loading = $loading();
  getAllKeyList(params).then((res) => {
    let { code, data, data2 } = res;
    let _total = 0;
    let _list = [];
    if (code && data) {
      let { list, total } = data2;
      _list = list.map(v => {
        v.type = { 1: 'ID钥匙', 2: '机械钥匙' }[v.keyType];
        return v;
      });
      _total = total;
    }
    datas.tableData = _list;
    total.value = _total;
    loading.close();
  })
    .catch((err) => {
      console.log(err);
      loading.close();
    });
}
function handleSizeChange(val) {
}
function handleCurrentChange(val) {
}
onMounted(() => {
  getList();
});
</script>
<template>
  <div class="page-wrapper">
    <div class="page-header">
    </div>
    <div class="page-content">
      <hdw-card is-full>
        <div class="page-content-wrapper">
          <div class="page-content-tools">
            <el-button type="primary" size="small" :icon="Plus" @click="add">添加钥匙</el-button>
            |
            <div class="tools-filter">
              <div class="tools-filter-item">
                <div class="filter-label">钥匙名称:</div>
                <div class="filter-content">
                  <el-input v-model="filter.keyName" size="small" @input="getList"></el-input>
                </div>
              </div>
              <div class="tools-filter-item">
                <div class="filter-label">员工名称:</div>
                <div class="filter-content">
                  <el-input v-model="filter.userName" size="small" @input="getList"></el-input>
                </div>
              </div>
            </div>
            <el-button type="primary" size="small" :icon="Search" @click="getList">查询</el-button>
          </div>
          <div class="page-content-table">
            <div class="pos-rel">
              <div class="pos-abs">
                <el-table :data="datas.tableData" border style="width: 100%; height: 100%">
                  <el-table-column type="index" width="50" />
                  <el-table-column prop="type" align="center" label="钥匙类型" width="120" />
                  <el-table-column prop="keyName" align="center" label="钥匙名称" width="180" />
                  <el-table-column prop="keyNumber" align="center" label="钥匙编号" width="180" />
                  <el-table-column prop="uname" align="center" label="保管人" width="180" />
                  <el-table-column prop="createTime" label="创建时间" />
                  <el-table-column align="center" fixed="right" label="操作" width="180" v-if="urole > 0">
                    <template #default="scope">
                      <el-button type="primary" size="small" @click="edit(scope)">编辑</el-button>
                      <el-button type="danger" size="small" @click="del(scope)">删除</el-button>
                    </template>
                  </el-table-column>
                </el-table>
              </div>
            </div>
          </div>
          <div class="page-content-page">
            <div class="page-tool"></div>
            <div class="el-page-container">
              <el-pagination v-model:current-page="pageCurr" v-model:page-size="pageSize"
                :page-sizes="[20, 40, 60, 80, 100, 200, 300, 400]" size="small" :disabled="disabled"
                :background="background" layout="total, sizes, prev, pager, next, jumper" :total="total"
                @size-change="handleSizeChange" @current-change="handleCurrentChange" />
            </div>
            <div class="page-tool"></div>
          </div>
        </div>
      </hdw-card>
    </div>
    <div class="page-footer"></div>
    <!-- 弹窗 -->
    <el-dialog :title="dialogTitle" v-model="addEditVisible" top="0" :close-on-click-modal="false" class="dialog-center"
      width="700px" center>
      <add-edit v-if="addEditVisible" @success="onOk" :info="datas.keyInfo" @cancel="onCanel"></add-edit>
    </el-dialog>
  </div>
</template>
<style scoped lang="scss">
.page-wrapper {
  display: flex;
  flex-direction: row;
  padding: 8px;
  height: 100%;
  .page-content {
    flex: 1;
  }
}
.page-content-wrapper {
  display: flex;
  flex-direction: column;
  height: 100%;
  .page-content-tools {
    padding-bottom: 8px;
  }
  .page-content-table {
    flex: 1;
    margin-left: -8px;
    margin-right: -8px;
  }
  .page-content-page {
    padding: 8px 8px 0 8px;
    text-align: center;
    .el-page-container {
      display: inline-block;
      padding: 0 16px;
    }
    .page-tool {
      display: inline-block;
    }
  }
}
.hdw-card-container {
  width: 240px;
  padding-right: 8px;
  height: 100%;
}
.pos-rel {
  position: relative;
  width: 100%;
  height: 100%;
  .pos-abs {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    height: 100%;
  }
}
.tools-filter {
  display: inline-block;
  font-size: 14px;
  .tools-filter-item {
    display: inline-block;
    margin-right: 8px;
    .filter-label {
      display: inline-block;
    }
    .filter-content {
      display: inline-block;
    }
  }
}
</style>
src/views/device/lock/index.vue
File was deleted
src/views/device/locks/addEdit.vue
New file
@@ -0,0 +1,245 @@
<script setup>
import { ref, onMounted } from "vue";
import { getAreaTreeApi } from "@/api/area";
import { formatAreaTree } from "@/utils/tree";
import { updateLock, addLock } from "@/api/lockManager.js";
const areaList = ref([]);
const emit = defineEmits(["close", "ok"]);
const rules = {
  count: [
    {
      required: true,
      message: "不能为空",
      trigger: ["change", "blur"],
    },
    {
      validator: (rule, value, callback) => {
        const reg = /^\d+$/;
        if (!reg.test(value)) {
          callback(new Error("只能为数字"));
        } else {
          if (value > 1000) {
            callback(new Error("最大支持批量新增1000套"));
          } else if (value < 1) {
            callback(new Error("输入不能小于1"));
          } else {
            callback();
          }
        }
      },
      trigger: ["change", "blur"],
    },
  ],
  areaId: [
    {
      required: true,
      message: "不能为空",
      trigger: ["change", "blur"],
    },
    {
      validator: (rule, value, callback) => {
        if (!value.length) {
          callback(new Error("请选择区域"));
        } else {
          callback();
        }
      },
      trigger: ["change", "blur"],
    },
  ],
  lockName: [
    {
      required: true,
      message: "不能为空",
      trigger: ["change", "blur"],
    },
  ],
};
const formLock = ref({
  areaId: "",
  lockId: "",
  lockType: 3,
  lockName: "",
  count: 1,
});
const lockTypes = ref([
  {
    label: "实体锁",
    value: 3,
  },
]);
const props = defineProps({
  info: {
    type: Object,
  },
  isAdd: {
    type: Boolean,
    default: false,
  },
  isBatch: {
    type: Boolean,
    default: false,
  },
});
function ok() {
  if (props.isAdd) {
    add();
  } else {
    update();
  }
}
function add() {
  let areaId;
  if (formLock.value.areaId && formLock.value.areaId.length) {
    areaId = formLock.value.areaId[formLock.value.areaId.length - 1];
  }
  let params = {
    lockName: formLock.value.lockName,
    areaId,
    // 目前固定
    lockType: formLock.value.lockType,
  };
  if (props.isBatch) {
    params["num"] = formLock.value.count;
  }
  addLock(params)
    .then((res) => {
      let { code, data } = res;
      if (code && data) {
        emit("ok");
      }
    })
    .catch((err) => {
      console.log(err);
    });
}
function update() {
  // lockId lockName areaId lockType
  let areaId;
  if (formLock.value.areaId && formLock.value.areaId.length) {
    areaId = formLock.value.areaId[formLock.value.areaId.length - 1];
  }
  let params = {
    lockId: formLock.value.lockId,
    lockName: formLock.value.lockName,
    areaId,
    // 目前固定
    lockType: formLock.value.lockType,
  };
  updateLock(params)
    .then((res) => {
      let { code, data } = res;
      if (code && data) {
        emit("ok");
      }
    })
    .catch((err) => {
      console.log(err);
    });
}
function close() {
  emit("close");
}
async function getAreaTree() {
  try {
    const res = await getAreaTreeApi();
    let _data = [];
    if (res.code === 1 && res.data) {
      _data = res.data2;
    }
    const treeList = [];
    let ids = _data.map((v) => v.id);
    for (let i = 0; i < _data.length; i++) {
      formatAreaTree(_data[i], ids, treeList);
    }
    // console.log(_data, 'data');
    // console.log(treeList, 'treeList');
    areaList.value = treeList;
  } catch (e) {
    console.log(e);
  }
}
onMounted(() => {
  if (!props.isAdd) {
    console.log("info ", props.info, "=============");
    formLock.value = props.info;
  }
  getAreaTree();
});
</script>
<template>
  <el-form
    label-position="left"
    label-width="auto"
    :model="formLock"
    :rules="rules"
    style="max-width: 600px"
  >
    <!-- 所属区域 -->
    <el-form-item label="所属区域" label-position="right" prop="areaId">
      <el-cascader
        class="select"
        filterable
        clearable
        v-model="formLock.areaId"
        :props="{ value: 'id', checkStrictly: true }"
        :options="areaList"
        ><template #default="{ node, data }">
          <span>{{ data.label }}</span>
          <span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
        </template></el-cascader
      >
    </el-form-item>
    <!-- 类型 -->
    <el-form-item label="类型" label-position="right" prop="lockType">
      <el-select
        v-model="formLock.lockType"
        placeholder="Select"
        style="width: 240px"
      >
        <el-option
          v-for="item in lockTypes"
          :key="item.value"
          :label="item.label"
          :value="item.value"
        />
      </el-select>
    </el-form-item>
    <!-- 锁具名称 -->
    <el-form-item label="锁具名称" label-position="right" prop="lockName">
      <el-input v-model="formLock.lockName" />
    </el-form-item>
    <!-- 批量添加数量  -->
    <el-form-item
      label="批量添加数量"
      label-position="right"
      prop="count"
      v-if="isBatch"
    >
      <el-input v-model="formLock.count" />
    </el-form-item>
  </el-form>
  <div class="footer">
    <el-button size="small" @click="close">取消</el-button>
    <el-button type="primary" size="small" @click="ok">确定</el-button>
  </div>
</template>
<style scoped lang="scss">
:deep(.select) {
  width: 100%;
}
.footer {
  text-align: right;
}
</style>
src/views/device/locks/index.vue
New file
@@ -0,0 +1,401 @@
<script setup name="LockManage">
    import { ref, onMounted, reactive, computed, watchEffect } from "vue";
    // import const_theme from "@/utils/const/const_theme";
    import store from "@/store";
    import { Search, Plus } from "@element-plus/icons-vue";
    // import HdwTree from "@/components/HdwTree/index.vue";
    import {
        getAllLockInf,
        delLock,
        lockOpen,
    } from '@/api/lockManager.js';
    import useWebSocket from "@/hooks/useWebsocket.js";
    import addEdit from './addEdit.vue';
    import useElement from "@/hooks/useElement.js";
    const { $loading, $message, $confirm } = useElement();
    const { sendData, message: listMessage } = useWebSocket("lockRt");
    const disabled = ref(false);
    const background = ref(false);
    const addEditVisible = ref(false);
    const isAdd = ref(false);
    const isBatch = ref(false);
    const currentAreaId = ref();
    // const themes = ref(const_theme);
    // const theme = store.settings().theme;
    const tableData = ref([]);
    const addEditTitle = ref('');
    const filter = reactive({
        name: "",
        state: undefined,
        type: undefined,
    });
    const page = reactive({
        pageNum: 1,
        pageSize: 20,
        total: 0,
    });
    // 当前编辑的锁对象
    const editLock = ref();
    const isAdmin = computed(() => store.user().urole > 0);
    onMounted(() => {
    });
    // function handleChangeTheme(val) {
    //   console.log(val);
    //   store.settings().changeSetting({
    //     key: "theme",
    //     value: val,
    //   });
    // }
    function sendMessage() {
        let params = {
            areaId: currentAreaId.value,
            lockName: filter.name.trim() || undefined,
            lockState: filter.state,
            lockType: filter.type,
            pageNum: page.pageNum,
            pageSize: page.pageSize,
        };
        sendData(
            JSON.stringify(params)
        );
    }
    function itemClickHandler(item) {
        console.log(item, '====item', item.data);
        // areaId lockName lockState lockType pageNum pageSize
        currentAreaId.value = item.data.id;
        sendMessage();
    }
    watchEffect(() => {
        let _total = 0;
        let _list = [];
        if (listMessage.value) {
            const {
                code,
        data,
                data2
            } = JSON.parse(listMessage.value);
    if (code && data) {
      let { list, total } = data2;
      _list = list.map(v => {
        v.state = { '-1': '未安装', 0: '闭锁', 1: '开锁' }[v.lockState];
        v.onlineState = { 0: '离线', 1: '在线' }[v.lockOnline];
        v.type = { 1: '蓝牙锁', 2: 'ID锁', 3: '实体锁' }[v.lockType];
        return v;
      });
      _total = total;
    }
        }
    tableData.value = _list;
    page.total = _total;
    });
    function handleSizeChange(val) {
        page.pageSize = val;
        sendMessage();
    }
    function handleCurrentChange(val) {
        page.pageNum = val;
        sendMessage();
    }
    function edit(scope) {
        console.log('scope', scope, '=============');
        let {
            row
        } = scope;
        editLock.value = row;
        addEditTitle.value = '修改锁具';
        isBatch.value = false;
        isAdd.value = false;
        addEditVisible.value = true;
    }
    function okHandle() {
        addEditVisible.value = false;
        sendMessage();
    }
    function add() {
        isAdd.value = true;
        isBatch.value = false;
        addEditVisible.value = true;
    }
    function batchAdd() {
        isAdd.value = true;
        isBatch.value = true;
        addEditVisible.value = true;
    }
    function confirmDel(params) {
        $confirm("删除该锁具", () => {
            del(params.row.lockId);
        });
    }
    function open(scope) {
        console.log('scope', scope, '=============');
        let {
            row
        } = scope;
        $confirm("远程打开该锁具", () => {
            let loading = $loading();
            lockOpen(scope.row.lockId).then((res) => {
                let { code, data } = res;
                loading.close();
                if (code && data) {
                    $message.success("操作成功");
                    sendMessage();
                } else {
                    $message.error("操作失败");
                }
            })
                .catch((err) => {
                    console.log(err);
                    loading.close();
                });
        });
    }
    function del(id) {
        let loading = $loading();
        delLock(id).then((res) => {
            let { code, data } = res;
            loading.close();
            if (code && data) {
                $message.success("操作成功");
                sendMessage();
            } else {
                $message.success("操作失败");
            }
        })
            .catch((err) => {
                console.log(err);
                loading.close();
            });
    }
</script>
<template>
  <div class="page-wrapper">
    <div class="page-header">
      <div class="hdw-card-container">
        <hdw-card title="区域列表" is-full>
          <hdw-tree @item-click="itemClickHandler"></hdw-tree>
        </hdw-card>
      </div>
    </div>
    <div class="page-content">
      <hdw-card is-full>
        <div class="page-content-wrapper">
          <div class="page-content-tools">
            <el-button type="primary" size="small" :icon="Plus" @click="add">添加</el-button>
            <el-button type="primary" size="small" :icon="Plus" @click="batchAdd">批量添加</el-button>
            |
            <div class="tools-filter">
              <div class="tools-filter-item">
                <div class="filter-label">锁具名称:</div>
                <div class="filter-content">
                  <el-input @input="sendMessage" placeholder="请输入锁具名称..." v-model="filter.name" size="small"></el-input>
                </div>
              </div>
              <div class="tools-filter-item">
                <div class="filter-label">锁具状态:</div>
                <div class="filter-content">
                  <el-select v-model="filter.state" placeholder="请选择" @change="sendMessage" size="small"
                    style="width: 180px">
                    <el-option label="--请选择--" value="" />
                    <el-option label="未安装" :value="-1" />
                    <el-option label="闭锁" :value="0" />
                    <el-option label="开锁" :value="1" />
                  </el-select>
                </div>
              </div>
              <div class="tools-filter-item">
                <div class="filter-label">锁具类型:</div>
                <div class="filter-content">
                  <el-select v-model="filter.type" @change="sendMessage" placeholder="请选择" size="small"
                    style="width: 180px">
                    <el-option label="--请选择--" value="" />
                    <el-option label="蓝牙锁" :value="1" />
                    <el-option label="ID锁" :value="2" />
                    <el-option label="实体锁" :value="3" />
                  </el-select>
                </div>
              </div>
              <!-- <div class="tools-filter-item">
                      <div class="filter-label">主题:</div>
                      <div class="filter-content">
                        <el-select
                          v-model="theme"
                          placeholder="请选择"
                          size="small"
                          @change="handleChangeTheme"
                          style="width: 180px"
                        >
                          <el-option
                            v-for="item in themes"
                            :key="item.label"
                            :label="item.label"
                            :value="item.value"
                          />
                        </el-select>
                      </div>
                    </div> -->
            </div>
            <el-button type="primary" size="small" :icon="Search" @click="sendMessage">查询</el-button>
          </div>
          <div class="page-content-table">
            <div class="pos-rel">
              <div class="pos-abs">
                <el-table stripe :data="tableData" border style="width: 100%; height: 100%">
                  <el-table-column type="index" width="50" />
                  <el-table-column prop="type" label="类型" width="180" />
                  <el-table-column prop="lockName" label="锁具名称" width="180" />
                  <el-table-column prop="areaPath" label="关联区域" />
                  <el-table-column prop="onlineState" label="在线状态" width="180" />
                  <el-table-column prop="state" label="安装状态" width="180" />
                  <!-- <el-table-column prop="lockImg" label="锁具图片" width="180" /> -->
                  <!-- <el-table-column prop="address" label="地址" /> -->
                  <el-table-column align="center" fixed="right" label="操作" width="240">
                    <template #default="scope">
                      <el-button type="danger" size="small"
                        v-if="isAdmin && scope.row.lockOnline == 1 && scope.row.lockState == 0"
                        @click="open(scope)">远程开锁</el-button>
                      <el-button type="primary" size="small" @click="edit(scope)">编辑</el-button>
                      <el-button type="danger" size="small" @click="confirmDel(scope)">删除</el-button>
                    </template>
                  </el-table-column>
                </el-table>
              </div>
            </div>
          </div>
          <div class="page-content-page">
            <div class="page-tool"></div>
            <div class="el-page-container">
              <el-pagination v-model:current-page="page.pageNum" v-model:page-size="page.pageSize"
                :page-sizes="[20, 40, 60, 80, 100, 200, 300, 400]" size="small" :disabled="disabled"
                :background="background" layout="total, sizes, prev, pager, next, jumper" :total="page.total"
                @size-change="handleSizeChange" @current-change="handleCurrentChange" />
            </div>
            <div class="page-tool"></div>
          </div>
        </div>
      </hdw-card>
    </div>
    <div class="page-footer"></div>
    <!-- 编辑 新建 -->
    <el-dialog v-model="addEditVisible" :title="addEditTitle" width="500" align-center :close-on-click-modal="false">
      <add-edit v-if="addEditVisible" :isBatch="isBatch" :info="editLock" :isAdd="isAdd" @close="addEditVisible = false"
        @ok="okHandle"></add-edit>
    </el-dialog>
  </div>
</template>
<style scoped lang="scss">
.page-wrapper {
  display: flex;
  flex-direction: row;
  padding: 8px;
  height: 100%;
  .page-content {
    flex: 1;
  }
}
.page-content-wrapper {
  display: flex;
  flex-direction: column;
  height: 100%;
  .page-content-tools {
    padding-bottom: 8px;
  }
  .page-content-table {
    border-top: 1px solid var(--border-light-color);
    box-sizing: border-box;
    flex: 1;
    margin-left: -8px;
    margin-right: -8px;
  }
  .page-content-page {
    padding: 8px 8px 0 8px;
    text-align: center;
    .el-page-container {
      display: inline-block;
      padding: 0 16px;
    }
    .page-tool {
      display: inline-block;
    }
  }
}
.hdw-card-container {
  width: 240px;
  padding-right: 8px;
  height: 100%;
}
.pos-rel {
  position: relative;
  width: 100%;
  height: 100%;
  .pos-abs {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    height: 100%;
  }
}
.tools-filter {
  display: inline-block;
  font-size: 14px;
  .tools-filter-item {
    display: inline-block;
    margin-right: 8px;
    .filter-label {
      display: inline-block;
    }
    .filter-content {
      display: inline-block;
    }
  }
}
</style>
src/views/general/authorize/addEdit.vue
New file
@@ -0,0 +1,223 @@
<script setup>
    import { ref, reactive, computed, onMounted } from "vue";
    import { getAllUserName } from "@/api/user";
    import { addAuth, updateAuth } from "@/api/auth";
    import useElement from "@/hooks/useElement.js";
    import { getKeyInfAuth } from '@/api/keys.js';
    import { getAreaUserLock } from '@/api/lockManager.js';
    const { $loading, $message, $confirm } = useElement();
    const props = defineProps({
        info: {
            type: Object,
        },
    });
    const formRef = ref();
    const userList = ref([]);
    const keyList = ref([]);
    const lockList = ref([]);
    const form1 = reactive({
        uname: "",
        uid: "",
        keyId: "",
        lockIds: [],
        descript: "",
    });
    const rules = {
        uname: [
            {
                required: true,
                message: "不能为空",
                trigger: ["blur", "change"],
            },
        ],
        keyId: [
            {
                required: true,
                message: "不能为空",
                trigger: ["blur", "change"],
            },
        ],
        lockIds: [
            {
                required: true,
                message: "不能为空",
                trigger: ["blur", "change"],
            },
            {
                validator: (rule, value, callback) => {
                    if (!value.length) {
                        callback(new Error("请选择锁具"));
                    } else {
                        callback();
                    }
                },
                trigger: ["change", "blur"],
            },
        ],
    };
    const isEdit = computed(() => !!props.info?.uname);
    const $emit = defineEmits(["close", "ok"]);
    function close() {
        $emit("close");
    }
    // 查询所有用户
    async function getAllUser() {
        let res = await getAllUserName();
        let { code, data, data2 } = res;
        let _list = [];
        if (code && data) {
            _list = data2;
        }
        userList.value = _list;
    }
    // 查询所有钥匙
    async function getKeyList() {
        let res = await getKeyInfAuth();
        let { code, data, data2 } = res;
        let _list = [];
        if (code && data) {
            _list = data2;
        }
        // _list.unshift({ keyId: 0, keyName: "蓝牙钥匙" });
        keyList.value = _list;
    }
    // 查询所有锁具
    async function getLockList() {
        let res = await getAreaUserLock();
        let { code, data, data2 } = res;
        let _list = [];
        if (code && data) {
            _list = data2;
        }
        lockList.value = _list;
    }
    async function update() {
        let valid = await formRef.value.validate(() => { });
        // console.log('valid', valid, '=============');
        if (!valid) {
            $message.error("表单验证失败");
            return false;
        }
        let params = {
            // uname: form1.uname.trim(),
            uname: form1.uname,
      keyId: form1.keyId,
      keyName: keyList.value.find(item => item.keyId === form1.keyId)?.keyName,
            lockInfs: form1.lockIds.map(v => ({ lockId: v, lockName: lockList.value.find(item => item.lockId === v)?.lockName })),
            descript: form1.descript.trim(),
        };
        console.log("params update", params, "=============");
        let loading = $loading();
        updateAuth(props.info.id, params)
            .then((res) => {
                let { code, data } = res;
                if (code && data) {
                    $emit("ok");
                    $message.success("操作成功");
                } else {
                    $message.error("操作失败");
                }
                loading.close();
            })
            .catch((err) => {
                console.log(err);
                loading.close();
            });
    }
    async function onSubmit() {
        let valid = await formRef.value.validate(() => { });
        // console.log('valid', valid, '=============');
        if (!valid) {
            $message.error("表单验证失败");
            return false;
        }
        let params = {
            // uname: form1.uname.trim(),
            uname: form1.uname,
            keyId: form1.keyId,
      keyName: keyList.value.find(item => item.keyId === form1.keyId)?.keyName,
            lockInfs: form1.lockIds.map(v => ({ lockId: v, lockName: lockList.value.find(item => item.lockId === v)?.lockName })),
            descript: form1.descript.trim(),
        };
        console.log("params", params, "=============");
        let loading = $loading();
        addAuth(params)
            .then((res) => {
                let { code, data } = res;
                if (code && data) {
                    $emit("ok");
                    $message.success("操作成功");
                } else {
                    $message.error("操作失败");
                }
                loading.close();
            })
            .catch((err) => {
                console.log(err);
                loading.close();
            });
    }
    onMounted(() => {
        let info = props.info;
    console.log('info', info, '=============');
        if (info) {
            form1.uname = info.uname;
            // form1.uid = info.uid;
            form1.keyId = info.keyId;
            form1.lockIds = [info.lockId];
            form1.descript = info.descript;
        }
        getAllUser();
        getKeyList();
        getLockList();
    });
</script>
<template>
  <div class="">
    <el-form ref="formRef" :model="form1" label-width="80px" :rules="rules">
      <el-form-item label="授权人员" prop="uname">
        <el-select v-model="form1.uname">
          <el-option v-for="(item, idx) in userList" :key="'user_' + idx" :label="item" :value="item" />
        </el-select>
      </el-form-item>
      <el-form-item label="授权钥匙" prop="keyId">
        <el-select v-model="form1.keyId">
          <el-option v-for="item in keyList" :key="'key_' + item.keyId" :label="item.keyName" :value="item.keyId" />
        </el-select>
      </el-form-item>
      <el-form-item label="授权锁具" prop="lockIds">
        <el-select v-model="form1.lockIds" clearable multiple collapse-tags collapse-tags-tooltip>
          <el-option v-for="item in lockList" :key="'lock_' + item.lockId" :label="item.lockName" :value="item.lockId" />
        </el-select>
      </el-form-item>
      <el-form-item label="任务说明" prop="descript">
        <el-input v-model="form1.descript" :rows="2" type="textarea"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button v-if="isEdit" type="primary" @click="update">修改</el-button>
        <el-button v-else type="primary" @click="onSubmit">新增</el-button>
        <el-button @click="close">取消</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>
<style scoped lang="scss">
:deep(.select) {
  width: 100%;
}
</style>
src/views/general/authorize/index.vue
@@ -1,47 +1,128 @@
<script lang="ts">
import { defineComponent } from 'vue';
import HdwCard from '@/components/HdwCard/index.vue';
export default defineComponent({
  name: 'AuthManage',
  components: { HdwCard },
  data() {
    return {
      tableData: [
        {
          userName: 'whyc',
          authKey: '四楼蓝牙锁1',
          authLock: '机柜蓝牙锁',
          authTimeStart: '2024-10-11 14:25:51',
          authTimeEnd: '2024-10-12 14:25:51',
          jobDes: '测试',
          state: '通过'
        }
      ],
      filter: {
        name: '',
        state: 0,
        type: 0
      },
      page: {
        currentPage: 1,
        pageSize: 20,
        total: 0
      }
    };
  },
  methods: {
  }
});
</script>
<script lang="ts" setup>
<script lang="js" setup name="AuthManage">
import {  reactive, ref, onMounted } from 'vue';
import addEdit from './addEdit.vue';
import {
  Search,
  Plus
} from '@element-plus/icons-vue';
import HdwTree from '@/components/HdwTree/index.vue';
import useElement from "@/hooks/useElement.js";
import {
  getAllAuthInf,
  delAuth,
} from '@/api/auth.js';
const { $loading, $message, $confirm } = useElement();
const currentAreaId = ref();
const tableData = reactive([]);
const filter = reactive({
  name: '',
  state: 0,
  type: 0
});
const page = reactive({
  currentPage: 1,
  pageSize: 20,
  total: 0
});
const info = ref({});
const disabled = ref(false);
const background = ref(false);
const addEditVisible = ref(false);
const addEditTitle = ref('');
function  itemClickHandler(item) {
  console.log(item, '====item', item.data);
  // areaId lockName lockState lockType pageNum pageSize
  currentAreaId.value = item.data.id;
  getList();
}
function okHandle() {
  addEditVisible.value = false;
  getList();
}
function handleSizeChange(val) {
  page.pageSize = val;
  getList();
}
function handleCurrentChange(val) {
  page.currentPage = val;
  getList();
}
function edit(scope) {
  addEditTitle.value = '编辑授权';
  info.value = scope.row;
  console.log('edit', scope.row, '=============');
  addEditVisible.value = true;
}
function add() {
  addEditTitle.value = '添加授权';
  info.value = undefined;
  addEditVisible.value = true;
}
function del(scope) {
  $confirm('删除该授权', () => {
    let loading = $loading();
    delAuth([scope.row.id]).then((res) => {
      let { code, data } = res;
      if (code && data) {
        $message({
          type: 'success',
          message: '删除成功!'
        });
        getList();
      } else {
        $message({
          type: 'error',
          message: '删除失败!'
        });
      }
      loading.close();
    });
  });
}
function getList() {
  // areaId, pageNum, pageSize, state, uname
  let params = {
    pageNum: page.currentPage,
    pageSize: page.pageSize,
    state: filter.state,
    uname: filter.name,
    areaId: currentAreaId.value,
  }
  getAllAuthInf(params).then((res) => {
    let { code, data, data2 } = res;
    let _total = 0;
    let _list = [];
    if (code && data) {
      let { list, total } = data2;
      _list = list;
      _total = total;
    }
    tableData.length = 0;
    tableData.push(..._list);
    page.total = _total;
  })
}
onMounted(() => {
})
</script>
<template>
@@ -49,7 +130,7 @@
    <div class="page-header">
      <div class="hdw-card-container">
        <hdw-card title="区域列表" is-full>
          <hdw-tree></hdw-tree>
          <hdw-tree @item-click="itemClickHandler"></hdw-tree>
        </hdw-card>
      </div>
    </div>
@@ -57,7 +138,9 @@
      <hdw-card is-full>
        <div class="page-content-wrapper">
          <div class="page-content-tools">
            <el-button type="primary" size="small" :icon="Plus">添加授权</el-button>
            <el-button type="primary" size="small" :icon="Plus" @click="add"
              >添加授权</el-button
            >
            |
            <div class="tools-filter">
              <div class="tools-filter-item">
@@ -66,40 +149,74 @@
                  <el-input v-model="filter.name" size="small"></el-input>
                </div>
              </div>
              <div class="tools-filter-item">
              <!-- <div class="tools-filter-item">
                <div class="filter-label">授权状态:</div>
                <div class="filter-content">
                  <el-select
                      v-model="filter.state"
                      placeholder="请选择"
                      size="small"
                      style="width: 180px"
                    v-model="filter.state"
                    placeholder="请选择"
                    size="small"
                    style="width: 180px"
                  >
                    <el-option label="--请选择--" :value="0"/>
                    <el-option label="未安装" :value="1"/>
                    <el-option label="已安装" :value="2"/>
                    <el-option label="--请选择--" :value="0" />
                    <el-option label="未安装" :value="1" />
                    <el-option label="已安装" :value="2" />
                  </el-select>
                </div>
              </div>
              </div> -->
            </div>
            <el-button type="primary" size="small" :icon="Search">查询</el-button>
            <el-button type="primary" size="small" :icon="Search"
              >查询</el-button
            >
          </div>
          <div class="page-content-table">
            <div class="pos-rel">
              <div class="pos-abs">
                <el-table :data="tableData" border style="width: 100%; height: 100%">
                <el-table
                  :data="tableData"
                  border
                  style="width: 100%; height: 100%"
                >
                  <el-table-column type="index" width="50" />
                  <el-table-column prop="userName" align="center" label="授权人员" width="120" />
                  <el-table-column prop="authKey" align="center" label="授权钥匙" width="120" />
                  <el-table-column prop="authLock" align="center" label="授权锁具" width="120" />
                  <el-table-column prop="authTimeStart" align="center" label="授权时限开始" width="180" />
                  <el-table-column prop="authTimeEnd" align="center" label="授权时限结束" width="180" />
                  <el-table-column prop="jobDes" align="center" min-width="180" label="任务说明" />
                  <el-table-column prop="state" align="center" label="状态" width="80" />
                  <el-table-column align="center" fixed="right" label="操作" width="180">
                    <template #default>
                      <el-button type="primary" size="small">编辑</el-button>
                      <el-button type="danger" size="small">删除</el-button>
                  <el-table-column
                    prop="uname"
                    align="center"
                    label="授权人员"
                    width="120"
                  />
                  <el-table-column
                    prop="keyName"
                    align="center"
                    label="授权钥匙"
                    width="120"
                  />
                  <el-table-column
                    prop="lockName"
                    align="center"
                    label="授权锁具"
                    width="120"
                  />
                  <el-table-column
                    prop="descript"
                    align="center"
                    min-width="180"
                    label="任务说明"
                  />
                  <el-table-column
                    prop="state"
                    align="center"
                    label="状态"
                    width="80"
                  />
                  <el-table-column
                    align="center"
                    fixed="right"
                    label="操作"
                    width="180"
                  >
                    <template #default="scope">
                      <el-button type="primary" size="small" @click="edit(scope)">编辑</el-button>
                      <el-button type="danger" size="small" @click="del(scope)">删除</el-button>
                    </template>
                  </el-table-column>
                </el-table>
@@ -110,16 +227,16 @@
            <div class="page-tool"></div>
            <div class="el-page-container">
              <el-pagination
                  v-model:current-page="page.currentPage"
                  v-model:page-size="page.pageSize"
                  :page-sizes="[20, 40, 60, 80, 100, 200, 300, 400]"
                  size="small"
                  :disabled="disabled"
                  :background="background"
                  layout="total, sizes, prev, pager, next, jumper"
                  :total="page.total"
                  @size-change="handleSizeChange"
                  @current-change="handleCurrentChange"
                v-model:current-page="page.currentPage"
                v-model:page-size="page.pageSize"
                :page-sizes="[20, 40, 60, 80, 100, 200, 300, 400]"
                size="small"
                :disabled="disabled"
                :background="background"
                layout="total, sizes, prev, pager, next, jumper"
                :total="page.total"
                @size-change="handleSizeChange"
                @current-change="handleCurrentChange"
              />
            </div>
            <div class="page-tool"></div>
@@ -128,6 +245,21 @@
      </hdw-card>
    </div>
    <div class="page-footer"></div>
    <!-- 编辑 新建 -->
        <el-dialog
            v-model="addEditVisible"
            :title="addEditTitle"
            width="500"
            align-center
            :close-on-click-modal="false"
        >
            <add-edit
                v-if="addEditVisible"
                :info="info"
                @close="addEditVisible = false"
                @ok="okHandle"
            ></add-edit>
        </el-dialog>
  </div>
</template>
src/views/general/log/index.vue
@@ -1,47 +1,117 @@
<script lang="ts">
import { defineComponent } from 'vue';
import HdwCard from '@/components/HdwCard/index.vue';
<script setup name="LogManage">
    import { ref, onMounted } from 'vue';
    import { getLockLog } from "@/api/loginfo";
    import { getLinfById } from '@/api/lockManager.js';
    import { getUinfById } from '@/api/user';
    import {
        Search
    } from '@element-plus/icons-vue';
export default defineComponent({
  name: 'LogManage',
  components: { HdwCard },
  data() {
    return {
      tableData: [
        {
          areaName: '武汉源畅科技',
          userName: '源畅科技',
          keyName: '测试',
          lockName: '机柜蓝牙锁',
          type: '开锁操作',
          state: '成功',
          openType: '蓝牙',
          time: '2024-08-14 17:13:09'
        }
      ],
      filter: {
        userName: '',
        timeRange: '',
        type: 0
      },
      page: {
        currentPage: 1,
        pageSize: 20,
        total: 0
      }
    };
  },
  methods: {
    import useElement from "@/hooks/useElement.js";
    const { $loading, $message, $confirm } = useElement();
    const tableData = ref([]);
    const ctlUname = ref('');
    const ctlResult = ref('');
    const startTime = ref('');
    const endTime = ref('');
    const lockId = ref('');
    const ctlType = ref('');
    const pageNum = ref(1);
    const pageSize = ref(10);
    const total = ref(0);
    const currentAreaId = ref();
  }
});
</script>
    const lockList = ref([]);
<script lang="ts" setup>
import {
  Search
} from '@element-plus/icons-vue';
import HdwTree from '@/components/HdwTree/index.vue';
    const userList = ref([]);
    const logList = ref([]);
    // 操作类型[1-清空所有授权ID卡 2-远程开锁  3-远程重启 4-设置锁具地址 5-添加授权ID 6-移除授权ID  7-蓝牙开锁  8-ID开锁]
    const ctlTypeList = [
        '',
        '清空所有授权ID卡',
        '远程开锁',
        '远程重启',
        '设置锁具地址',
        '添加授权ID',
        '移除授权ID',
        '蓝牙开锁',
        'ID开锁'
    ];
    function itemClickHandler(item) {
        console.log(item, '====item', item.data);
        // areaId lockName lockState lockType pageNum pageSize
        currentAreaId.value = item.data.id;
        ctlUname.value = '';
        lockId.value = '';
        getUsers();
        getLockList();
        getList();
    }
    async function getLockList() {
        let res = await getLinfById(currentAreaId.value);
        let { code, data, data2 } = res;
        let _list = [];
        if (code && data) {
            _list = data2;
        }
        lockList.value = _list;
    }
    async function getList() {
        let params = {
            areaId: currentAreaId.value,
            ctlUname: ctlUname.value || undefined,
            ctlResult: ctlResult.value || undefined,
            startTime: startTime.value || undefined,
            endTime: endTime.value || undefined,
            ctlType: ctlType.value || undefined,
            lockId: lockId.value || undefined
        };
        let res = await getLockLog(pageNum.value, pageSize.value, params);
        let { code, data, data2 } = res;
        let _total = 0;
        let _list = [];
        if (code && data) {
            let { list, total } = data2;
            _total = total;
            _list = list.map(v => ({
                ...v,
                ctlResultStr: v.ctlResult ? '成功' : '失败',
                ctlTypeStr: ctlTypeList[v.ctlType] || '未知',
                openTypeStr: [2, 7, 8].includes(v.ctlType) ? ctlTypeList[v.ctlType] : '--',
            }));
        }
        total.value = _total;
        logList.value = _list;
    }
    async function getUsers() {
        let res = await getUinfById(currentAreaId.value);
        let { code, data, data2 } = res;
        let _list = [];
        if (code && data) {
            _list = data2;
        }
        userList.value = _list;
    }
    function handleSizeChange(params) {
        pageSize.value = params
        getList();
    }
    function handleCurrentChange(params) {
        pageNum.value = params
        getList();
    }
    onMounted(() => {
    });
</script>
<template>
@@ -49,7 +119,7 @@
    <div class="page-header">
      <div class="hdw-card-container">
        <hdw-card title="区域列表" is-full>
          <hdw-tree></hdw-tree>
          <hdw-tree @item-click="itemClickHandler"></hdw-tree>
        </hdw-card>
      </div>
    </div>
@@ -59,55 +129,58 @@
          <div class="page-content-tools">
            <div class="tools-filter">
              <div class="tools-filter-item">
                <div class="filter-label">操作人:</div>
                <div class="filter-label">操作人</div>
                <div class="filter-content">
                  <el-input v-model="filter.userName" size="small"></el-input>
                  <el-select v-model="ctlUname" clearable placeholder="请选择" size="small" filterable style="width: 180px"
                    @change="getList">
                    <el-option v-for="(item, idx) in userList" size="small" :key="'user_' + idx" :label="item"
                      :value="item" />
                  </el-select>
                </div>
              </div>
              <div class="tools-filter-item">
                <div class="filter-label">操作时间:</div>
                <div class="filter-label">操作时间</div>
                <div class="filter-content">
                  <el-date-picker
                      v-model="filter.timeRange"
                      type="daterange"
                      range-separator="至"
                      start-placeholder="开始时间"
                      end-placeholder="结束时间"
                      size="small"
                  />
                  <el-date-picker v-model="startTime" @change="getList" value-format="YYYY-MM-DD HH:mm:ss" type="datetime"
                    size="small" placeholder="" />至<el-date-picker v-model="endTime" type="datetime"
                    value-format="YYYY-MM-DD HH:mm:ss" size="small" @change="getList" placeholder="" />
                </div>
              </div>
              <div class="tools-filter-item">
                <div class="filter-label">操作类型:</div>
                <div class="filter-label">操作类型</div>
                <div class="filter-content">
                  <el-select
                      v-model="filter.type"
                      placeholder="请选择"
                      size="small"
                      style="width: 180px"
                  >
                    <el-option label="--请选择--" :value="0"/>
                    <el-option label="开关门日志" :value="1"/>
                    <el-option label="报警日志" :value="2"/>
                  <el-select v-model="ctlType" @change="getList" placeholder="请选择" size="small" style="width: 180px">
                    <el-option v-for="(item, idx) in ctlTypeList" :key="'type_' + idx" :label="item || '全部'"
                      :value="idx" />
                  </el-select>
                </div>
              </div>
              <div class="tools-filter-item">
                <div class="filter-label">锁具</div>
                <div class="filter-content">
                  <el-select v-model="lockId" @change="getList" clearable filterable placeholder="请选择" size="small"
                    style="width: 180px">
                    <el-option v-for="item in lockList" :key="'lock_' + item.lockId" :label="item.lockName"
                      :value="item.lockId" />
                  </el-select>
                </div>
              </div>
            </div>
            <el-button type="primary" size="small" :icon="Search">查询</el-button>
            <el-button type="primary" size="small" :icon="Search" @click="getList">查询</el-button>
          </div>
          <div class="page-content-table">
            <div class="pos-rel">
              <div class="pos-abs">
                <el-table :data="tableData" border style="width: 100%; height: 100%">
                <el-table :data="logList" border style="width: 100%; height: 100%">
                  <el-table-column type="index" width="50" />
                  <el-table-column prop="areaName" align="center" label="区域名称" width="180" />
                  <el-table-column prop="userName" align="center" label="操作人" width="120" />
                  <el-table-column prop="keyName" align="center" label="钥匙名称" width="120" />
                  <!-- <el-table-column prop="areaName" align="center" label="区域名称" width="180" /> -->
                  <el-table-column prop="ctlUname" align="center" label="操作人" width="120" />
                  <!-- <el-table-column prop="keyName" align="center" label="钥匙名称" width="120" /> -->
                  <el-table-column prop="lockName" align="center" label="锁具名称" min-width="180" />
                  <el-table-column prop="type" align="center" label="操作类型" width="180" />
                  <el-table-column prop="state" align="center" width="120" label="状态" />
                  <el-table-column prop="openType" align="center" label="开锁方式" width="120" />
                  <el-table-column prop="time" align="center" label="操作时间" width="180" />
                  <el-table-column prop="ctlTypeStr" align="center" label="操作类型" width="180" />
                  <el-table-column prop="ctlResultStr" align="center" width="120" label="状态" />
                  <el-table-column prop="openTypeStr" align="center" label="开锁方式" width="120" />
                  <el-table-column prop="ctlTime" align="center" label="操作时间" width="180" />
                </el-table>
              </div>
            </div>
@@ -115,18 +188,10 @@
          <div class="page-content-page">
            <div class="page-tool"></div>
            <div class="el-page-container">
              <el-pagination
                  v-model:current-page="page.currentPage"
                  v-model:page-size="page.pageSize"
                  :page-sizes="[20, 40, 60, 80, 100, 200, 300, 400]"
                  size="small"
                  :disabled="disabled"
                  :background="background"
                  layout="total, sizes, prev, pager, next, jumper"
                  :total="page.total"
                  @size-change="handleSizeChange"
                  @current-change="handleCurrentChange"
              />
              <el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize"
                :page-sizes="[20, 40, 60, 80, 100, 200, 300, 400]" size="small"
                layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handleSizeChange"
                @current-change="handleCurrentChange" />
            </div>
            <div class="page-tool"></div>
          </div>
@@ -143,6 +208,7 @@
  flex-direction: row;
  padding: 8px;
  height: 100%;
  .page-content {
    flex: 1;
  }
@@ -152,21 +218,26 @@
  display: flex;
  flex-direction: column;
  height: 100%;
  .page-content-tools {
    padding-bottom: 8px;
  }
  .page-content-table {
    flex: 1;
    margin-left: -8px;
    margin-right: -8px;
  }
  .page-content-page {
    padding: 8px 8px 0 8px;
    text-align: center;
    .el-page-container {
      display: inline-block;
      padding: 0 16px;
    }
    .page-tool {
      display: inline-block;
    }
@@ -178,10 +249,12 @@
  padding-right: 8px;
  height: 100%;
}
.pos-rel {
  position: relative;
  width: 100%;
  height: 100%;
  .pos-abs {
    position: absolute;
    top: 0;
@@ -192,15 +265,23 @@
    height: 100%;
  }
}
.tools-filter {
  display: inline-block;
  font-size: 14px;
  .tools-filter-item {
    display: inline-block;
    margin-right: 8px;
    .filter-label {
      display: inline-block;
      &::after {
        content: ":";
      }
    }
    .filter-content {
      display: inline-block;
    }
src/views/login/components/UKeyBind/BindStep2.vue
New file
@@ -0,0 +1,112 @@
<template>
  <div class="params-dialog">
    <div class="form-content">
      {{ message }}
    </div>
    <div class="form-footer">
      <el-button type="primary" :disabled="bindState" @click="next">下一步</el-button>
    </div>
  </div>
</template>
<script>
    import { getUNameByUKey } from "@/api/user";
    import useElement from '@/hooks/useElement.js';
    const { $loading } = useElement();
    export default {
        name: "BindStep2",
        props: {
            id: {
                type: String,
                default: ""
            }
        },
        watch: {
            id(id) {
                this.checkUKey(id);
            }
        },
        data() {
            return {
                bindState: false, // uKey绑定的状态
                userName: "", // 绑定的用户名
                state: 0
            };
        },
        methods: {
            /**
             * 检测uKey是否已经绑定用户
             */
            checkUKey(id) {
                if (id) {
                    let loading = $loading();
                    getUNameByUKey(id.trim())
                        .then(res => {
                            let { code, data, data2 } = res;
                            if (code && data) {
                                this.state = 1;
                                this.bindState = true;
                                this.userName = data2.uname;
                            } else {
                                this.state = 0;
                                this.bindState = false;
                            }
                            // 关闭等待框
                            loading.close();
                        })
                        .catch(error => {
                            // 请求网络失败
                            this.state = -1;
                            this.bindState = true;
                            // 关闭等待框
                            loading.close();
                            console.log(error);
                        });
                } else {
                    this.state = -2;
                    this.bindState = true;
                }
            },
            next() {
                this.$emit("next");
            }
        },
        computed: {
            message() {
                let msg = "";
                switch (this.state) {
                    case -2:
                        msg = "未获取到ukey的信息";
                        break;
                    case -1:
                        msg = "请求网络失败,无法检测ukey绑定状态!";
                        break;
                    case 0:
                        msg = "ukey未绑定用户";
                        break;
                    case 1:
                        msg = "ukey已绑定" + this.userName + "用户";
                        break;
                }
                return msg;
            }
        },
        mounted() {
            this.checkUKey(this.id);
        }
    };
</script>
<style scoped>
.form-content {
  padding: 8px;
  font-size: 18px;
  text-align: center;
}
.form-footer {
  padding: 8px;
  text-align: right;
}
</style>
src/views/login/components/UKeyBind/BindStep3.vue
New file
@@ -0,0 +1,151 @@
<template>
  <el-form label-position="top" class="params-dialog">
    <el-row>
      <el-col :span="6">
        <div style="visibility: hidden">1</div>
      </el-col>
      <el-col :span="12">
        <el-form-item class="label" label="用户名">
          <el-input type="text" placeholder="请输入用户名" resize="none" v-model="userName"></el-input>
        </el-form-item>
      </el-col>
      <el-col :span="6"></el-col>
    </el-row>
    <div class="form-footer">
      <el-button type="primary"  @click="confirmBindUser">绑定用户</el-button>
    </div>
  </el-form>
</template>
<script>
    import SoftUKey from "@/utils/SoftUKey";
    import SoftKey3W from "@/utils/Syunew3";
    import useElement from '@/hooks/useElement.js';
    const { $loading, $confirm, $message } = useElement();
    import {
        getUKeyByUName,
        bindUKey,
    } from "@/api/user";
    export default {
        name: "BindStep3",
        props: {
            id: {
                type: String,
                default: ""
            }
        },
        data() {
            return {
                userName: "",
                softUKey: new SoftUKey(SoftKey3W)
            }
        },
        methods: {
            confirmBindUser() {
                let userName = this.userName.trim();
                let id = this.id.trim();
                if (!userName) {
                    $message("用户名不能为空");
                    return;
                }
                if (!id) {
                    $message("uKey的信息不能为空");
                    return;
                }
                $confirm('绑定' + this.userName, () => {
                    this.checkUser(userName, id);
                });
            },
            /**
             * 校验用户是否已经绑定用户
             * @param name  用户名
             * @param id    ukey的信息
             */
            checkUser(name, id) {
                let loading = $loading();
                getUKeyByUName(name).then(res => {
                    // 1:已经绑定ukey,0:用户不存在 -1:可以绑定
                    let { code, msg } = res;
                    loading.close();
                    if (code == 1) {
                        $message(msg);
                    } else if (code == 0) {
                        $message(msg);
                    } else {
                        this.softUKey.setUserName(name, this.setUserNameRs);
                    }
                }).catch(error => {
                    // 关闭等待框
                    loading.close();
                    console.log(error);
                });
            },
            setUserNameRs(result) {
                let name = result.userName;
                let id = this.id.trim();
                let publicX = result.PubKeyX;
                let publicY = result.PubKeyY;
                let loading = $loading();
                this.bindUkey(name, id, publicX, publicY, loading);
            },
            /**
             * 绑定用户
             * @param name 用户名
             * @param id    ukey的信息
             * @param loading 加载等待框
             */
            bindUkey(name, id, publicX, publicY, loading) {
                if (!loading) {
                    loading = $loading();
                }
                bindUKey(name, id, publicX, publicY).then(res => {
                    let { code, data } = res;
                    if (code && data) {
                        $message('绑定成功');
                        this.$emit('next');
                    } else {
                        $message('绑定失败');
                    }
                    // 关闭等待框
                    loading.close();
                }).catch(error => {
                    // 关闭等待框
                    loading.close();
                    console.log(error);
                });
            }
        },
    }
</script>
<style scoped lang="scss">
.label {
  padding: 4px;
  color: #153953;
  background: transparent;
  font-size: 12px;
  font-weight: bold;
  :deep(.el-input) {
    width: 100%;
  }
  :deep(.el-input__wrapper) {
    padding: 0;
  }
  :deep(.el-input__inner) {
    border-color: #dcdfe6;
    color: #153953;
    background-color: #cccccc;
  }
}
.form-footer {
  text-align: right;
}
</style>
src/views/login/components/UKeyBind/index.vue
New file
@@ -0,0 +1,87 @@
<template>
  <div class="params-container">
    <el-steps :active="active" finish-status="success" simple>
      <el-step title="插入ukey">123</el-step>
      <el-step title="ukey验证"></el-step>
      <el-step title="用户验证"></el-step>
      <el-step title="绑定状态"></el-step>
    </el-steps>
    <div class="el-step" v-if="active == 1">
      <div class="el-step-text-content">未插入ukey,请插入ukey</div>
    </div>
    <bind-step2 :id="id" @next="next" v-if="active == 2"></bind-step2>
    <bind-step3 :id="id" @next="next" v-if="active == 3"></bind-step3>
    <div class="params-dialog" v-if="active == 4">
      <div class="el-step-text-content">已完成绑定</div>
      <div class="form-footer">
        <el-button type="primary" @click="close">关闭</el-button>
      </div>
    </div>
  </div>
</template>
<script>
    import BindStep2 from "./BindStep2";
    import BindStep3 from "./BindStep3";
    import store from "@/store";
    import { mapState } from 'pinia';
    export default {
        name: "UkeyBind",
        components: { BindStep3, BindStep2 },
        props: {
            visible: {
                type: Boolean,
                default: false,
            },
        },
        computed: {
            ...mapState(store.ukey, ['id', 'isIn']),
        },
        watch: {
            id: {
                handler(id) {
                    if (id) {
                        this.active = 2;
                    } else {
                        this.active = 1;
                    }
                },
                immediate: true
            },
        },
        data() {
            let active = this.isIn ? 2 : 1;
            return {
                active: active,
                // id: "",
            };
        },
        methods: {
            next() {
                this.active++;
            },
            close() {
                this.$emit("update:visible", false);
            },
        },
        mounted() {
            // this.id = this.$store.state.ukey.id;
        },
    };
</script>
<style scoped>
.params-container {
  padding: 8px 8px 0 8px;
  background-color: #ececec;
}
.form-footer {
  text-align: right;
}
.el-step-text-content {
  padding: 8px;
  text-align: center;
  font-size: 16px;
}
</style>
src/views/login/components/face/FaceLogin.vue
New file
@@ -0,0 +1,263 @@
<template>
  <!-- 人脸登陆弹窗 -->
  <div>
    <video ref="video" width="480" height="320" style="display: none"></video>
    <canvas ref="canvas" width="480" height="320"></canvas>
    <p class="text">{{ faceDetect }}</p>
  </div>
</template>
<script>
    import faceManager from "./faceManager.js";
    import { uKeyLogin } from "@/api/user";
    import store from '@/store';
    import { mapState } from 'pinia';
    import useElement from '@/hooks/useElement.js';
    import { getToken, removeToken, setToken, getUname, setUname, removeUname, getUrole, setUrole, removeUrole } from '@/utils/auth';
    const { $loading, $confirm, $message } = useElement();
    // import const_num from "@/assets/js/const/const_num";
    // import RSA from "@/assets/js/tools/RSA";
    export default {
        props: {
            faceShow: {
                type: Boolean,
                default: false,
            },
        },
        data() {
            return {
                status: true,
                faceDetect: "请将正脸对准摄像头",
                imageBase64: "",
                newstream: "",
                intTime: null,
                postTime: null,
                setTime: null,
        redirect: undefined,
                otherQuery: {}
            };
        },
        watch: {
            $route: {
                handler: function (route) {
                    const query = route.query;
                    if (query) {
                        this.redirect = query.redirect;
                        this.otherQuery = this.getOtherQuery(query);
                    }
                },
                immediate: true
            }
        },
        computed: {
            ...mapState(store.ukey, ['isIn', 'connect', 'id']),
        },
        mounted() {
            this.faceChange();
        },
        beforeUnmount() {
            // console.log('beforeUnmount ', '=============');
            this.status = false;
            this.clearVideo();
        },
        methods: {
      getOtherQuery(query) {
                return Object.keys(query).reduce((acc, cur) => {
                    if (cur !== 'redirect') {
                        acc[cur] = query[cur];
                    }
                    return acc;
                }, {});
            },
            // 关闭摄像头
            clearVideo: function () {
                // 关闭定时器
                clearInterval(this.intTime);
                clearInterval(this.postTime);
                clearTimeout(this.setTime);
                // 关闭摄像头
                for (let track of this.newstream.getTracks()) {
                    track.stop();
                }
            },
            // 获取图片
            getImg: function () {
                let vm = this;
                let video = vm.$refs.video;
                let canvas = vm.$refs.canvas;
                let context = canvas.getContext("2d");
                context.drawImage(video, 0, 0, 480, 320);
                // 获取图片base64链接
                vm.imageBase64 = canvas.toDataURL("image/png");
            },
            // 人脸检测
            faceChange: function () {
                let vm = this;
                // 恢复提示语
                vm.faceDetect = "请将正脸对准摄像头";
                // vm.faceShow = true;
                if (
                    navigator.mediaDevices.getUserMedia ||
                    navigator.getUserMedia ||
                    navigator.webkitGetUserMedia ||
                    navigator.mozGetUserMedia
                ) {
                    //调用用户媒体设备, 访问摄像头
                    this.getUserMedia(
                        { video: { width: 480, height: 320 } },
                        this.success,
                        this.error
                    );
                } else {
                    alert("不支持访问用户媒体");
                }
            },
            //访问用户媒体设备的兼容方法
            getUserMedia: function (constraints, success, error) {
                if (navigator.mediaDevices.getUserMedia) {
                    //最新的标准API
                    navigator.mediaDevices
                        .getUserMedia(constraints)
                        .then(success)
                        .catch(error);
                } else if (navigator.webkitGetUserMedia) {
                    //webkit核心浏览器
                    navigator.webkitGetUserMedia(constraints, success, error);
                } else if (navigator.mozGetUserMedia) {
                    //firfox浏览器
                    navigator.mozGetUserMedia(constraints, success, error);
                } else if (navigator.getUserMedia) {
                    //旧版API
                    navigator.getUserMedia(constraints, success, error);
                }
            },
            // 成功回调
            success: function (stream) {
                let vm = this;
                //兼容webkit核心浏览器
                let CompatibleURL = window.URL || window.webkitURL;
                //将视频流设置为video元素的源
                // console.log(stream);
                // stream = stream;
                this.newstream = stream;
                //video.src = CompatibleURL.createObjectURL(stream);
                let video = this.$refs.video;
                video.srcObject = stream;
                video.play();
                this.intTime = setInterval(() => {
                    vm.getImg();
                }, 0);
                setTimeout(function () {
                    vm.facePost();
                }, 0);
            },
            error: function (error) {
                console.log(error);
                console.log(`访问用户媒体设备失败${error.name}, ${error.message}`);
            },
            // 请求后台验证人脸
            facePost: function () {
                if (!this.isIn) {
                    this.faceDetect = "请先插入ukey";
                    this.setTime = setTimeout(() => {
                        this.facePost();
                    }, 500);
                } else if (!this.imageBase64) {
                    this.faceDetect = "人脸识别中...";
                    this.setTime = setTimeout(() => {
                        this.facePost();
                    }, 500);
                } else {
                    let vm = this;
                    faceManager
                        .faceVerify('"' + vm.imageBase64 + '"', this.id)
                        .then((result) => {
                            let res = result;
                            console.log(res);
                            if (res.code === 1 && res.data) {
                                vm.faceDetect = "匹配成功";
                let user = res.data2[0];
                                setTimeout(() => {
                                    // vm.onLogin();
                                    // $message.success("登录成功");
                                    setUname(user.uname);
                                    setToken('admin');
                                    setUrole(user.urole);
                  this.$router.push({ path: this.redirect || '/', query: this.otherQuery });
                                    // this.$router.push("/home");
                                    // // // 设置用户的权限
                                    // // this.$store.dispatch("user/getPermits");
                                }, 1000);
                            } else {
                                vm.faceDetect = res.msg;
                                if (vm.status == true) {
                                    this.setTime = setTimeout(() => {
                                        this.facePost();
                                    }, 3000);
                                }
                            }
                        })
                        .catch((err) => {
                            vm.faceDetect = "网络链接失败";
                            if (vm.status == true) {
                                this.setTime = setTimeout(() => {
                                    this.facePost();
                                }, 3000);
                            }
                        });
                }
            },
            // 登陆设置sessionStorage
            onLogin: function () {
                faceManager
                    .getUserName()
                    .then((res) => {
                        let { code, data } = res;
                        if (code == 1) {
                            this.uKeyLogin(data.uname, data.usnid);
                        }
                    })
                    .catch((err) => {
                        console.log(err);
                    });
            },
            uKeyLogin(username, password) {
                // ukey登录
                uKeyLogin(username, password, this.id, true)
                    .then((res) => {
                        console.log('res', res, '=============');
                        // 对结果进行处理
                        this.handleLogin(res, username);
                    })
                    .catch((error) => {
                        // 关闭等待
                        this.loading = false;
                        this.faceDetect = "网络异常";
                        this.facePost();
                    });
            },
            handleLogin(res, username) {
                let rs = res.data;
                if (rs.code == 1) {
                    this.$message.success("登录成功");
                    sessionStorage.setItem("username", username);
                    sessionStorage.setItem("userId", rs.data);
                    this.$router.push("/home");
                    // 设置用户的权限
                    this.$store.dispatch("user/getPermits");
                } else {
                    this.faceDetect = rs.msg;
                    this.facePost();
                }
            },
        },
    };
</script>
<style lang="scss" scoped>
.text {
  text-align: center;
  padding: 10px 0;
}
</style>
src/views/login/components/face/faceManager.js
New file
@@ -0,0 +1,28 @@
import request from '@/utils/request';
export default {
  /**
  * 人脸验证
  * @param json:base64Image
  * @returns {AxiosPromise}
  */
  faceVerify(data, param) {
    return request({
      method: 'post',
      url: `/face/faceCompare2N?ukeyId=${param}`,
      headers: { 'content-type': 'application/json' },
      data
    });
  },
  /**
  * 获取用户名
  * @param  无参
  * @returns {AxiosPromise}
  */
  getUserName() {
    return request({
      method: 'GET',
      url: '/face/info',
      data: null
    });
  },
}
src/views/login/index.vue
@@ -2,11 +2,11 @@
  <div class="login-container">
    <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" autocomplete="on"
      label-position="left">
      <div class="title-container">
        <h3 class="title">鸿蒙智能电子锁管理平台</h3>
      </div>
      <el-form-item prop="username">
        <span class="svg-container">
          <svg-icon icon-class="user" />
@@ -14,7 +14,7 @@
        <el-input ref="username" v-model="loginForm.username" placeholder="Username" name="username" type="text"
          tabindex="1" autocomplete="on" />
      </el-form-item>
      <el-tooltip v-model="capsTooltip" :content="passwordType === 'password' ?'显示密码':'隐藏密码'" placement="right" manual>
        <el-form-item prop="password">
          <span class="svg-container">
@@ -28,167 +28,308 @@
          </span>
        </el-form-item>
      </el-tooltip>
      <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:40px;" @click.prevent="handleLogin">登录</el-button>
      <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:40px;"
        @click.prevent="handleLogin">登录</el-button>
    </el-form>
    <div class="tools-container">
      <div class="tools-item" :class="uKeyState" v-if="uKey" @click="uKeyVisible = true">
        <span class="ukey"></span>
      </div>
      <div class="tools-item" v-if="faceCfg" @click="faceVisible = true">
        <span class="face"></span>
      </div>
    </div>
    <!--  uKey的验证 -->
    <el-dialog title="uKey绑定" width="750px" v-model="uKeyVisible" :close-on-click-modal="false" top="0"
      class="dialog-center" :modal-append-to-body="false">
      <ukey-bind v-if="uKeyVisible" v-model:visible="uKeyVisible"></ukey-bind>
    </el-dialog>
    <!-- 人脸登陆 -->
    <el-dialog title="人脸登陆" width="480px" v-model="faceVisible" :close-on-click-modal="false" top="0"
      class="dialog-center" :modal-append-to-body="false">
      <face-login v-if="faceVisible" ></face-login>
    </el-dialog>
  </div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { ElMessage, type FormItemRule } from 'element-plus';
import type { IForm } from '@/types/element-plus';
import store from '@/store';
    import { defineComponent } from 'vue';
    import { ElMessage, type FormItemRule } from 'element-plus';
    import type { IForm } from '@/types/element-plus';
    import store from '@/store';
    import { mapState } from 'pinia';
    import config from '@/utils/config.js';
    import UkeyBind from './components/UKeyBind/index.vue';
    import useElement from '@/hooks/useElement.js';
    import { uKeyLogin } from '@/api/user';
  import FaceLogin from './components/face/FaceLogin.vue';
    import { getToken, removeToken, setToken, getUname, setUname, removeUname, getUrole, setUrole, removeUrole } from '@/utils/auth';
interface QueryType {
  // 自定义key 任意字符串
  [propname:string]:string
}
    const { $loading, $confirm, $message } = useElement();
    const { ukey, face } = config;
export default defineComponent({
  name: 'Login',
  data() {
    const validateUsername: FormItemRule['validator'] = (_rule, value, callback) => {
      if (!value.trim()) {
        callback(new Error('请输入用户名'));
      } else {
        callback();
      }
    };
    const validatePassword: FormItemRule['validator'] = (_rule, value, callback) => {
      if (value.length < 1) {
        callback(new Error('请输入密码,至少1位'));
      } else {
        callback();
      }
    };
    return {
      loginForm: {
        username: 'admin',
        password: '1'
      },
      loginRules: {
        username: [{ required: true, trigger: 'blur', validator: validateUsername }],
        password: [{ required: true, trigger: 'blur', validator: validatePassword }]
      },
      passwordType: 'password',
      capsTooltip: false,
      loading: false,
      showDialog: false,
      redirect: undefined,
      otherQuery: {}
    };
  },
  watch: {
    $route: {
      handler: function(route) {
        const query = route.query;
        if (query) {
          this.redirect = query.redirect;
          this.otherQuery = this.getOtherQuery(query);
        }
      },
      immediate: true
    }
  },
  created() {
    // window.addEventListener('storage', this.afterQRScan)
  },
  mounted() {
    if (this.loginForm.username === '') {
      (this.$refs.username as HTMLElement).focus();
    } else if (this.loginForm.password === '') {
      (this.$refs.password as HTMLElement).focus();
    }
    this.themeChange('blue-theme');
  },
  unmounted() {
    // window.removeEventListener('storage', this.afterQRScan)
  },
  methods: {
    themeChange(val) {
      store.settings().changeSetting({
        key: 'theme',
        value: val
      });
    },
    checkCapslock(e) {
      const { key } = e;
      this.capsTooltip = key && key.length === 1 && (key >= 'A' && key <= 'Z');
    },
    showPwd() {
      if (this.passwordType === 'password') {
        this.passwordType = '';
      } else {
        this.passwordType = 'password';
      }
      this.$nextTick(() => {
        (this.$refs.password as HTMLElement).focus();
      });
    },
    handleLogin() {
      (this.$refs.loginForm as IForm).validate(valid => {
        return new Promise((resolve, reject) => {
          if (valid) {
            this.loading = true;
            store.user().login(this.loginForm)
              .then(() => {
                this.loading = false;
                this.$router.push({ path: this.redirect || '/', query: this.otherQuery });
              }).catch((error) => {
                this.loading = false;
                // 请求失败,处理错误
                if (error.message === 'Network Error') {
                  // 如果是网络错误,显示中文消息
                  ElMessage({
                    message: '网络错误,请检查您的网络连接或服务器状态。',
                    type: 'error'
                  });
                } else {
                  // 其他类型的错误,可以根据需要处理
                  ElMessage({
                    message: error,
                    type: 'warning'
                  });
                }
              }).finally(() => {
                resolve();
              });
          } else {
            console.log('error submit!!');
            reject();
          }
        });
      });
    },
    getOtherQuery(query:QueryType) {
      return Object.keys(query).reduce((acc:QueryType, cur) => {
        if (cur !== 'redirect') {
          acc[cur] = query[cur];
        }
        return acc;
      }, {});
    }
    // afterQRScan() {
    //   if (e.key === 'x-admin-oauth-code') {
    //     const code = getQueryObject(e.newValue)
    //     const codeMap = {
    //       wechat: 'code',
    //       tencent: 'code'
    //     }
    //     const type = codeMap[this.auth_type]
    //     const codeName = code[type]
    //     if (codeName) {
    //       store.user().LoginByThirdparty(codeName).then(() => {
    //         this.$router.push({ path: this.redirect || '/' })
    //       })
    //     } else {
    //       alert('第三方登录失败')
    //     }
    //   }
    // }
  }
});
    interface QueryType {
        // 自定义key 任意字符串
        [propname: string]: string
    }
    export default defineComponent({
        name: 'Login',
        data() {
            const validateUsername: FormItemRule['validator'] = (_rule, value, callback) => {
                if (!value.trim()) {
                    callback(new Error('请输入用户名'));
                } else {
                    callback();
                }
            };
            const validatePassword: FormItemRule['validator'] = (_rule, value, callback) => {
                if (value.length < 1) {
                    callback(new Error('请输入密码,至少1位'));
                } else {
                    callback();
                }
            };
            return {
                uKey: ukey,
                faceCfg: face,
                uKeyVisible: false,
                faceVisible: false,
                loginForm: {
                    username: '',
                    password: ''
                },
                loginRules: {
                    username: [{ required: true, trigger: 'blur', validator: validateUsername }],
                    password: [{ required: true, trigger: 'blur', validator: validatePassword }]
                },
                passwordType: 'password',
                capsTooltip: false,
                loading: false,
                showDialog: false,
                redirect: undefined,
                otherQuery: {}
            };
        },
        components: {
            UkeyBind,
      FaceLogin,
        },
        computed: {
            uKeyState() {
                let state = -1;
                let uKey = store.ukey();
                let cls = "state-error";
                // 设置uKey的状态
                if (uKey.connect) {
                    if (uKey.isIn) {
                        state = 1;
                    } else {
                        state = 0;
                    }
                } else {
                    state = -1;
                }
                // 根据状态确定uKey图表的颜色
                switch (state) {
                    case 0:
                        cls = "state-default";
                        break;
                    case 1:
                        cls = "";
                        break;
                    default:
                        cls = "state-error";
                        break;
                }
                return cls;
            },
            ...mapState(store.ukey, ['isIn', 'connect', 'id'])
        },
        watch: {
            $route: {
                handler: function (route) {
                    const query = route.query;
                    if (query) {
                        this.redirect = query.redirect;
                        this.otherQuery = this.getOtherQuery(query);
                    }
                },
                immediate: true
            }
        },
        created() {
            // window.addEventListener('storage', this.afterQRScan)
        },
        mounted() {
            if (this.loginForm.username === '') {
                (this.$refs.username as HTMLElement).focus();
            } else if (this.loginForm.password === '') {
                (this.$refs.password as HTMLElement).focus();
            }
            this.themeChange('blue-theme');
        },
        unmounted() {
            // window.removeEventListener('storage', this.afterQRScan)
        },
        methods: {
            themeChange(val) {
                store.settings().changeSetting({
                    key: 'theme',
                    value: val
                });
            },
            checkCapslock(e) {
                const { key } = e;
                this.capsTooltip = key && key.length === 1 && (key >= 'A' && key <= 'Z');
            },
            showPwd() {
                if (this.passwordType === 'password') {
                    this.passwordType = '';
                } else {
                    this.passwordType = 'password';
                }
                this.$nextTick(() => {
                    (this.$refs.password as HTMLElement).focus();
                });
            },
            handleLogin() {
                if (ukey) {
                    this.uKeyLoginFn();
                } else {
                    this.normalLogin();
                }
            },
            uKeyLoginFn() {
                //
                if (!this.connect) {
                    $message.error("请先安装uKey客户端服务");
                    return;
                }
                if (!this.isIn) {
                    $message.warning("请先插入uKey");
                    return;
                }
                // 校验用户
                if (this.loginForm.username && this.loginForm.password) {
                    // 开启等待框
                    this.loading = true;
                    uKeyLogin(
                        this.loginForm.username,
                        this.loginForm.password,
                        this.id
                    )
                        .then((res) => {
                            // 对结果进行处理
                            console.log('res', res, '=============');
                            let { code, data, data2, msg } = res;
                            this.loading = false;
                            if (code && data) {
                                setUname(data2.uname);
                                setToken('admin');
                                setUrole(data2.urole);
                                ElMessage({
                                    message: msg,
                                    type: 'success'
                                });
                                this.$router.push({ path: this.redirect || '/', query: this.otherQuery });
                            } else {
                                ElMessage({
                                    message: msg,
                                    type: 'error'
                                });
                            }
                        })
                        .catch((error) => {
                            // 关闭等待
                            this.loading = false;
                            console.log(error);
                            $message.error("网络异常");
                        });
                } else {
                    $message.error("用户名或密码不能为空");
                }
            },
            normalLogin() {
                (this.$refs.loginForm as IForm).validate(valid => {
                    return new Promise((resolve, reject) => {
                        if (valid) {
                            this.loading = true;
                            store.user().login(this.loginForm)
                                .then((res) => {
                                    this.loading = false;
                                    let { code, data, msg } = res;
                                    if (code && data) {
                                        ElMessage({
                                            message: msg,
                                            type: 'success'
                                        });
                                        this.$router.push({ path: this.redirect || '/', query: this.otherQuery });
                                    } else {
                                        ElMessage({
                                            message: msg,
                                            type: 'error'
                                        });
                                    }
                                }).catch((error) => {
                                    this.loading = false;
                                    // 请求失败,处理错误
                                    if (error.message === 'Network Error') {
                                        // 如果是网络错误,显示中文消息
                                        ElMessage({
                                            message: '网络错误,请检查您的网络连接或服务器状态。',
                                            type: 'error'
                                        });
                                    } else {
                                        // 其他类型的错误,可以根据需要处理
                                        ElMessage({
                                            message: error,
                                            type: 'warning'
                                        });
                                    }
                                }).finally(() => {
                                    resolve();
                                });
                        } else {
                            console.log('error submit!!');
                            reject();
                        }
                    });
                });
            },
            getOtherQuery(query: QueryType) {
                return Object.keys(query).reduce((acc: QueryType, cur) => {
                    if (cur !== 'redirect') {
                        acc[cur] = query[cur];
                    }
                    return acc;
                }, {});
            }
            // afterQRScan() {
            //   if (e.key === 'x-admin-oauth-code') {
            //     const code = getQueryObject(e.newValue)
            //     const codeMap = {
            //       wechat: 'code',
            //       tencent: 'code'
            //     }
            //     const type = codeMap[this.auth_type]
            //     const codeName = code[type]
            //     if (codeName) {
            //       store.user().LoginByThirdparty(codeName).then(() => {
            //         this.$router.push({ path: this.redirect || '/' })
            //       })
            //     } else {
            //       alert('第三方登录失败')
            //     }
            //   }
            // }
        }
    });
</script>
<style lang="scss">
@@ -228,7 +369,7 @@
      &:-webkit-autofill {
        box-shadow: 0 0 0px 1000px $bg inset !important;
        -webkit-text-fill-color: $cursor  !important;
        -webkit-text-fill-color: $cursor !important;
      }
    }
  }
@@ -313,6 +454,63 @@
    bottom: 6px;
  }
  .tools-container {
    position: absolute;
    bottom: 0;
    right: 0;
    background-color: #ffffff;
  }
  .tools-container .tools-item {
    padding: 8px;
    width: 40px;
    height: 40px;
    cursor: pointer;
    .face {
      display: inline-block;
      width: 100%;
      height: 100%;
      background: url("data:image/svg+xml,%3csvg viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' %3e%3cpath d='M161.6 153.1h128.7V73.3H99.7c-8.3 0-15.1 6.7-15.1 15.1V286h76.9V153.1z m692.7 132.8h76.9V88.3c0-8.3-6.7-15.1-15.1-15.1H725.9V153h128.3v132.9zM605 449.2s140.1 88.3 161.6 22.2c0-148.1-115.8-268-258.5-268s-258.5 119.9-258.5 268C386.9 521.9 605 449.2 605 449.2z m161.6 134.1h-43.2s1 59.4-53.8 78C668.4 693.9 615 829 508.7 829c-106.2 0-160.9-135.1-162.2-167.7-55-18.9-52.5-78-52.5-78h-44.5s-1.6 78 64.6 100.6c1.6 38.2 66.2 189.9 193.9 189.9s192.6-151.4 193.9-189.9c66.3-22.5 64.7-100.6 64.7-100.6zM161.9 738.1H84.6v197.6c0 8.3 6.7 15.1 15.1 15.1h190.2V871h-128V738.1z m692.4 132.8H725.9v79.8h190.2c8.3 0 15.1-6.7 15.1-15.1V738.1h-76.9v132.8zM945 520H79c-8.2 0-15 6.7-15 15v2.2c0 8.2 6.7 15 15 15h866c8.2 0 15-6.7 15-15V535c0-8.3-6.7-15-15-15z' fill='%23333333'%3e%3c/path%3e%3c/svg%3e") center center / contain no-repeat;
    }
    .ukey {
      display: inline-block;
      width: 100%;
      height: 100%;
      background: url("data:image/svg+xml,%3csvg viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' %3e%3cpath d='M456.26 247.11a8 8 0 0 1 8 7.75v48.25a8 8 0 0 1-7.75 8h-48.25a8 8 0 0 1-8-7.75v-48.25a8 8 0 0 1 7.76-8h48.24z m160 0a8 8 0 0 1 8 7.75v48.25a8 8 0 0 1-7.75 8h-48.25a8 8 0 0 1-8-7.75v-48.25a8 8 0 0 1 7.76-8h48.24z m72-64h-352v248h352z m79.9 357.5h-53.7a8 8 0 0 0-6.6 3.5l-81 118.2a157.35 157.35 0 0 0-8.2 15.6h-0.9v-129.2a8 8 0 0 0-8-8h-45.9a8 8 0 0 0-8 8v274.4a8 8 0 0 0 8 8h45.9a8 8 0 0 0 8-8V689h0.9a90.25 90.25 0 0 0 7.7 15.2L712 827.71a8.19 8.19 0 0 0 6.6 3.4h58.3a7.84 7.84 0 0 0 4.7-1.6 8 8 0 0 0 1.7-11.2l-103-139.1 94.3-125.8a8.37 8.37 0 0 0 1.6-4.8 8 8 0 0 0-8.04-8z m-459.3 0.1h-46.1a8 8 0 0 0-8 8v160.8q0 126.6 116.6 126.6 120.9 0 120.8-129.9v-157.5a8 8 0 0 0-8-8h-45.9a8 8 0 0 0-8 8v164q0 69.9-56.2 69.9-57.15 0-57.2-72.6v-161.3a8 8 0 0 0-8-8z m419.4-429.6a32 32 0 0 1 32 31.47v288.53c65.54 0 118.93 51.06 120 114.09v357.91a8 8 0 0 1-7.75 8H152.26a8 8 0 0 1-8-7.75V547.11c0-63.26 52.73-115 118-116h2v-288a32 32 0 0 1 31.47-32h432.53z' fill='%230000ff'%3e%3c/path%3e%3c/svg%3e") center center / contain no-repeat;
    }
    &.state-default .ukey {
      background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' %3e%3cpath d='M456.26 247.11a8 8 0 0 1 8 7.75v48.25a8 8 0 0 1-7.75 8h-48.25a8 8 0 0 1-8-7.75v-48.25a8 8 0 0 1 7.76-8h48.24z m160 0a8 8 0 0 1 8 7.75v48.25a8 8 0 0 1-7.75 8h-48.25a8 8 0 0 1-8-7.75v-48.25a8 8 0 0 1 7.76-8h48.24z m72-64h-352v248h352z m79.9 357.5h-53.7a8 8 0 0 0-6.6 3.5l-81 118.2a157.35 157.35 0 0 0-8.2 15.6h-0.9v-129.2a8 8 0 0 0-8-8h-45.9a8 8 0 0 0-8 8v274.4a8 8 0 0 0 8 8h45.9a8 8 0 0 0 8-8V689h0.9a90.25 90.25 0 0 0 7.7 15.2L712 827.71a8.19 8.19 0 0 0 6.6 3.4h58.3a7.84 7.84 0 0 0 4.7-1.6 8 8 0 0 0 1.7-11.2l-103-139.1 94.3-125.8a8.37 8.37 0 0 0 1.6-4.8 8 8 0 0 0-8.04-8z m-459.3 0.1h-46.1a8 8 0 0 0-8 8v160.8q0 126.6 116.6 126.6 120.9 0 120.8-129.9v-157.5a8 8 0 0 0-8-8h-45.9a8 8 0 0 0-8 8v164q0 69.9-56.2 69.9-57.15 0-57.2-72.6v-161.3a8 8 0 0 0-8-8z m419.4-429.6a32 32 0 0 1 32 31.47v288.53c65.54 0 118.93 51.06 120 114.09v357.91a8 8 0 0 1-7.75 8H152.26a8 8 0 0 1-8-7.75V547.11c0-63.26 52.73-115 118-116h2v-288a32 32 0 0 1 31.47-32h432.53z' fill='%23808080'%3e%3c/path%3e%3c/svg%3e")
    }
    &.state-error .ukey {
      background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' %3e%3cpath d='M456.26 247.11a8 8 0 0 1 8 7.75v48.25a8 8 0 0 1-7.75 8h-48.25a8 8 0 0 1-8-7.75v-48.25a8 8 0 0 1 7.76-8h48.24z m160 0a8 8 0 0 1 8 7.75v48.25a8 8 0 0 1-7.75 8h-48.25a8 8 0 0 1-8-7.75v-48.25a8 8 0 0 1 7.76-8h48.24z m72-64h-352v248h352z m79.9 357.5h-53.7a8 8 0 0 0-6.6 3.5l-81 118.2a157.35 157.35 0 0 0-8.2 15.6h-0.9v-129.2a8 8 0 0 0-8-8h-45.9a8 8 0 0 0-8 8v274.4a8 8 0 0 0 8 8h45.9a8 8 0 0 0 8-8V689h0.9a90.25 90.25 0 0 0 7.7 15.2L712 827.71a8.19 8.19 0 0 0 6.6 3.4h58.3a7.84 7.84 0 0 0 4.7-1.6 8 8 0 0 0 1.7-11.2l-103-139.1 94.3-125.8a8.37 8.37 0 0 0 1.6-4.8 8 8 0 0 0-8.04-8z m-459.3 0.1h-46.1a8 8 0 0 0-8 8v160.8q0 126.6 116.6 126.6 120.9 0 120.8-129.9v-157.5a8 8 0 0 0-8-8h-45.9a8 8 0 0 0-8 8v164q0 69.9-56.2 69.9-57.15 0-57.2-72.6v-161.3a8 8 0 0 0-8-8z m419.4-429.6a32 32 0 0 1 32 31.47v288.53c65.54 0 118.93 51.06 120 114.09v357.91a8 8 0 0 1-7.75 8H152.26a8 8 0 0 1-8-7.75V547.11c0-63.26 52.73-115 118-116h2v-288a32 32 0 0 1 31.47-32h432.53z' fill='%23ff0000'%3e%3c/path%3e%3c/svg%3e");
    }
  }
  .tools-container .tools-item:hover {
    background-color: #e4e4e4;
  }
  .tools-container .iconfont {
    font-size: 22px;
    color: #0080ff;
  }
  .tools-container .state-default .iconfont {
    color: #808080;
  }
  .tools-container .state-error .iconfont {
    color: #ff0000;
  }
  :deep(.dialog-center) {
    box-sizing: content-box;
  }
  @media only screen and (max-width: 470px) {
    .thirdparty-button {
      display: none;
src/views/system/logs.vue
New file
@@ -0,0 +1,246 @@
<script setup>
    import { ref, onMounted } from "vue";
    import { getAllUserName } from "@/api/user";
    import { getAreaUserLock } from '@/api/lockManager.js';
    import { getSysLog, getTypes } from '@/api/loginfo.js';
    const userName = ref('');
    const startTime = ref('');
    const endTime = ref('');
    const userList = ref([]);
    const logList = ref([]);
    const lockList = ref([]);
    const pageCurr = ref(1);
    const pageSize = ref(10);
    const total = ref(0);
    const typeList = ref([]);
    async function getList() {
        let params = {
            pageNum: pageCurr.value,
            pageSize: pageSize.value,
            startTime: startTime.value || undefined,
            endTime: endTime.value || undefined,
            userName: userName.value || undefined,
        };
        let res = await getSysLog(params);
        let { code, data } = res;
        let _total = 0;
        let _list = [];
        if (code && data) {
            _list = data.list;
            _total = data.total
        }
        logList.value = _list;
    }
    const handleSizeChange = (params) => {
        pageSize.value = params;
        getList();
    }
    function handleCurrentChange(params) {
        pageCurr.value = params;
        getList();
    }
    async function getTypeList() {
        let res = await getTypes();
        let { code, data } = res;
        let _list = [];
        if (code && data) {
            // console.log(data);
            Object.keys(data).forEach((v) => {
                data[v].forEach((vv) => {
                    _list.push({ label: vv.type2Name, value: vv.type2 });
                });
            });
        }
        typeList.value = _list;
    }
    async function getLockList() {
        let res = await getAreaUserLock();
        let { code, data, data2 } = res;
        let _list = [];
        if (code && data) {
            _list = data2;
        }
        lockList.value = _list;
    }
    async function getUsers() {
        let res = await getAllUserName();
        let { code, data, data2 } = res;
        let _list = [];
        if (code && data) {
            _list = data2;
        }
        userList.value = _list;
    }
    onMounted(() => {
        getUsers();
        getLockList();
        getTypeList();
        getList();
    });
</script>
<template>
  <div class="page-wrapper">
    <div class="page-header">
    </div>
    <div class="page-content">
      <hdw-card is-full>
        <div class="page-content-wrapper">
          <div class="page-content-tools">
            <div class="tools-filter">
              <div class="tools-filter-item">
                <div class="filter-label">操作人</div>
                <div class="filter-content">
                  <el-select v-model="userName" clearable placeholder="请选择" size="small" filterable style="width: 180px"
                    @change="getList">
                    <el-option v-for="(item, idx) in userList" size="small" :key="'user_' + idx" :label="item"
                      :value="item" />
                  </el-select>
                </div>
              </div>
              <div class="tools-filter-item">
                <div class="filter-label">操作时间</div>
                <div class="filter-content">
                  <el-date-picker v-model="startTime" @change="getList" value-format="YYYY-MM-DD HH:mm:ss" type="datetime"
                    size="small" placeholder="" />至<el-date-picker v-model="endTime" type="datetime"
                    value-format="YYYY-MM-DD HH:mm:ss" size="small" @change="getList" placeholder="" />
                </div>
              </div>
              <div class="tools-filter-item">
                <div class="filter-label">操作类型</div>
                <div class="filter-content">
                  <el-select v-model="ctlType" @change="getList" placeholder="请选择" size="small" style="width: 180px">
                    <el-option v-for="(item, idx) in typeList" :key="'type_' + idx" :label="item.label"
                      :value="item.value" />
                  </el-select>
                </div>
              </div>
            </div>
            <el-button type="primary" size="small" :icon="Search" @click="getList">查询</el-button>
          </div>
          <div class="page-content-table">
            <div class="pos-rel">
              <div class="pos-abs">
                <el-table :data="logList" border style="width: 100%; height: 100%">
                  <el-table-column type="index" width="50" />
                  <el-table-column prop="userName" align="center" label="操作人" width="120" />
                  <el-table-column prop="msg" align="center" label="操作类型" width="180" />
                  <el-table-column prop="detail" align="center" width="" label="详情" />
                  <el-table-column prop="ip" align="center" label="ip" width="120" />
                  <el-table-column prop="createTime" align="center" label="操作时间" width="180" />
                </el-table>
              </div>
            </div>
          </div>
          <div class="page-content-page">
            <div class="page-tool"></div>
            <div class="el-page-container">
              <el-pagination v-model:current-page="pageCurr" v-model:page-size="pageSize"
                :page-sizes="[20, 40, 60, 80, 100, 200, 300, 400]" size="small"
                layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handleSizeChange"
                @current-change="handleCurrentChange" />
            </div>
            <div class="page-tool"></div>
          </div>
        </div>
      </hdw-card>
    </div>
    <div class="page-footer"></div>
  </div>
</template>
<style scoped lang="scss">
.page-wrapper {
  display: flex;
  flex-direction: row;
  padding: 8px;
  height: 100%;
  .page-content {
    flex: 1;
  }
}
.page-content-wrapper {
  display: flex;
  flex-direction: column;
  height: 100%;
  .page-content-tools {
    padding-bottom: 8px;
  }
  .page-content-table {
    flex: 1;
    margin-left: -8px;
    margin-right: -8px;
  }
  .page-content-page {
    padding: 8px 8px 0 8px;
    text-align: center;
    .el-page-container {
      display: inline-block;
      padding: 0 16px;
    }
    .page-tool {
      display: inline-block;
    }
  }
}
.hdw-card-container {
  width: 240px;
  padding-right: 8px;
  height: 100%;
}
.pos-rel {
  position: relative;
  width: 100%;
  height: 100%;
  .pos-abs {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    height: 100%;
  }
}
.tools-filter {
  display: inline-block;
  font-size: 14px;
  .tools-filter-item {
    display: inline-block;
    margin-right: 8px;
    .filter-label {
      display: inline-block;
      &::after {
        content: ":";
      }
    }
    .filter-content {
      display: inline-block;
    }
  }
}
</style>
src/views/system/region/addEdit.vue
New file
@@ -0,0 +1,281 @@
<script setup>
    import { ref, reactive, computed, onMounted } from "vue";
  import { getUinfByAreaIds } from "@/api/user";
    import useElement from "@/hooks/useElement.js";
    import { getAreaTreeApi, addArea, updateArea } from "@/api/area";
    import { formatAreaTree } from "@/utils/tree";
  import store from "@/store";
    const { $loading, $message, $confirm } = useElement();
  const uname = computed(() => store.user().name);
    const props = defineProps({
        info: {
            type: Object,
        },
    isEdit: {
      type: Boolean,
      required: true
    },
    });
    const props1 = { value: 'id', checkStrictly: true, multiple: true };
    const formRef = ref();
    const areaList = ref([]);
  const userList = ref([]);
    const form1 = reactive({
        areaName: "",
    areaId: '',
        parentId: "",
        unames: "",
        areaDescript: "",
    });
  const otherIdList = ref([]);
    const rules = {
        areaName: [
            {
                required: true,
                message: "不能为空",
                trigger: ["blur", "change"],
            },
        ],
    };
    const $emit = defineEmits(["cancel", "success"]);
    function close() {
        $emit("cancel");
    }
  // 查询所有用户
    async function getAllUser() {
        let res = await getUinfByAreaIds();
        let { code, data, data2 } = res;
        let _list = [];
        if (code && data) {
            _list = data2;
        }
        userList.value = _list;
    }
    async function update() {
        let valid = await formRef.value.validate(() => { });
        // console.log('valid', valid, '=============');
        if (!valid) {
            $message.error("表单验证失败");
            return false;
        }
        let params = {
            areaName: form1.areaName.trim(),
            areaDescript: form1.areaDescript.trim(),
      id: form1.areaId,
        };
    // 编辑用户时, 区域中不在管理员管理内的区域要挑出来 最后更新时再追加进去
        console.log("params update", params, "=============");
    let list = [];
    form1.chargers.forEach((v) => {
      list.push({
        uid: v,
        uname: userList.value.find(item => item.uid === v)?.uname
      });
    });
        let loading = $loading();
        updateArea(params, list)
            .then((res) => {
                let { code, data } = res;
                if (code && data) {
                    $emit("success");
                    $message.success("操作成功");
                } else {
                    $message.error("操作失败");
                }
                loading.close();
            })
            .catch((err) => {
                console.log(err);
                loading.close();
            });
    }
    async function onSubmit() {
        let valid = await formRef.value.validate(() => { });
        // console.log('valid', valid, '=============');
        if (!valid) {
            $message.error("表单验证失败");
            return false;
        }
        let params = {
            areaName: form1.areaName.trim(),
            areaDescript: form1.areaDescript.trim(),
      parentId: form1.parentId,
        };
        console.log("params", params, "=============");
    let list = [];
    form1.chargers.forEach((v) => {
      list.push({
        uid: v,
        uname: userList.value.find(item => item.uid === v)?.uname
      });
    });
        let loading = $loading();
        addArea(params, list)
            .then((res) => {
                let { code, data } = res;
                if (code && data) {
                    $emit("success");
                    $message.success("操作成功");
                } else {
                    $message.error("操作失败");
                }
                loading.close();
            })
            .catch((err) => {
                console.log(err);
                loading.close();
            });
    }
    async function getAreaTree() {
        try {
            const res = await getAreaTreeApi();
            let _data = [];
            if (res.code === 1 && res.data) {
                _data = res.data2;
            }
            const treeList = [];
            let ids = _data.map((v) => v.id);
            for (let i = 0; i < _data.length; i++) {
                formatAreaTree(_data[i], ids, treeList);
            }
            // console.log(_data, 'data');
            console.log(treeList, 'treeList');
            areaList.value = treeList;
      formatAreaSelected(treeList);
        } catch (e) {
            console.log(e);
        }
    }
    // 区域树 根节点
    // const areaRoots = computed(() => {
    //     if (!areaList.value) {
    //         return [];
    //     } else {
    //         return areaList.value.map(v => v.id);
    //     }
    // });
  function formatAreaSelected(treeList) {
    let roots = treeList.map(v => v.id);
    let otherAreas = form1.areaId.filter(v=>!v.some((vv) => roots.includes(vv)));
    let otherIds = otherAreas.map(v => v[v.length - 1]);
    otherIdList.value = otherIds;
    form1.areaId = form1.areaId.filter(v=>v.some((vv) => roots.includes(vv))).map(v => {
      let idx = 0;
      for (let i = 0; i < roots.length; i++) {
        if (v.includes(roots[i])) {
          idx = v.indexOf(roots[i]);
          break;
        }
      }
      return v.slice(idx);
    });
    console.log('form1.areaId', form1.areaId, '=============');
  }
    function handleChange(value) {
    let oldV = value.slice(0, value.length - 1);
    let newV = value[value.length - 1];
    console.log('value', value, '=============', oldV, newV);
        // 检查新选中的项是否与已选中的项有包含关系
        const hasInclusion = checkInclusion(newV, oldV);
        if (hasInclusion) {
            // 弹出对话框询问用户是否取消之前的项
      $message.error('取消之前的项(新选中的项与已选中的项存在包含关系)');
      form1.areaId = value.slice(0, value.length - 1);
            // $confirm('取消之前的项(新选中的项与已选中的项存在包含关系)', () => {
            //     form1.areaId = value;
            // });
        }
    };
    function checkInclusion(newValue, oldValue) {
    // 遍历新选中的项
            for (let j = 0; j < oldValue.length; j++) {
        const oldItem = oldValue[j];
                if (isIncluded(newValue, oldItem) || isIncluded(oldItem, newValue)) {
          return true;
                }
            }
        return false;
    };
    function isIncluded(item1, item2) {
    // 新元素是一个选项 取数组的最后一个元素
    const id = item1[item1.length - 1];
    return item2.includes(id);
    };
    onMounted(() => {
        let info = props.info.value;
    console.log('info', info, '===0000', props, '=============');
        if (!props.isEdit) {
            form1.parentId = info.id;
      form1.parentName = info.areaName;
        } else {
      let path = info.areaPath.split("_");
      let pName = path.length > 1 ? path[path.length - 2] : '--';
      form1.parentId = info.parentId;
      form1.areaName = info.areaName;
      form1.areaId = info.id;
      form1.parentName = pName;
      form1.chargers = info.areaUsers.map(v => v.uid);
      form1.areaDescript = info.areaDescript;
    }
        // getAreaTree();
    getAllUser();
    });
</script>
<template>
  <div class="">
    <el-form ref="formRef" :model="form1" label-width="100px" :rules="rules">
      <el-form-item label="上级区域">
        <el-input v-model="form1.parentName" disabled></el-input>
      </el-form-item>
      <el-form-item label="区域名称" prop="areaName">
        <el-input v-model="form1.areaName"></el-input>
      </el-form-item>
      <el-form-item label="区域负责人" prop="chargers">
        <el-select v-model="form1.chargers" multiple clearable>
          <el-option v-for="(item, idx) in userList" :disabled="item.uname == uname" :key="'key_' + idx" :label="item.uname" :value="item.uid" />
        </el-select>
      </el-form-item>
      <el-form-item label="区域描述" prop="areaDescript">
        <el-input v-model="form1.areaDescript" :rows="2" type="textarea"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button v-if="isEdit" type="primary" @click="update">修改</el-button>
        <el-button v-else type="primary" @click="onSubmit">新增</el-button>
        <el-button @click="close">取消</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>
<style scoped lang="scss">
:deep(.select) {
  width: 100%;
}
</style>
src/views/system/region/index.vue
@@ -1,15 +1,256 @@
<script lang="ts">
import { defineComponent } from 'vue';
<script setup name="RegionManage">
    import { ref, reactive, onMounted } from "vue";
    import { getAreaTreeApi } from "@/api/area";
    import { formatAreaTree } from "@/utils/tree";
  import { Search, Plus } from "@element-plus/icons-vue";
  import { delArea } from "@/api/area";
  import addEdit from "./addEdit.vue";
  import useElement from "@/hooks/useElement.js";
    const tableData = ref([]);
    const tableRef = ref();
      const { $loading, $message, $confirm } = useElement();
export default defineComponent({
  name: 'RegionManage'
});
    onMounted(() => {
        getAreaTree();
    });
  const dialogTitle = ref('添加区域');
  const addEditVisible = ref(false);
  const areaInfo = reactive({});
  const isEdit = ref(false);
  function onOk () {
    addEditVisible.value = false;
    getAreaTree();
  }
  function onCanel () {
    addEditVisible.value = false;
  }
    // 递归函数,用于展开或折叠所有行及其子行
    const toggleRowsExpansion = (rows, expand, ref) => {
        rows.forEach((row) => {
            ref.toggleRowExpansion(row, expand);
            if (row.children && row.children.length) {
                // 如果当前行有子行,则递归调用
                toggleRowsExpansion(row.children, expand, ref);
            }
        });
    };
    const expandAll = (expand) => {
        toggleRowsExpansion(tableData.value, expand, tableRef.value);
    };
    async function getAreaTree() {
        try {
            const res = await getAreaTreeApi();
            let _data = [];
            if (res.code === 1 && res.data) {
                _data = res.data2;
            }
            const treeList = [];
      let ids = _data.map((v) => v.id);
            for (let i = 0; i < _data.length; i++) {
                formatAreaTree(_data[i], ids, treeList);
            }
            // console.log(_data, 'data');
            console.log(treeList, "treeList");
            tableData.value = treeList;
        } catch (e) {
            console.log(e);
        }
    }
  // 添加
  function add(scope) {
    dialogTitle.value = '添加区域';
    areaInfo.value = scope.row.data;
    isEdit.value = false;
    addEditVisible.value = true;
    // addArea().then((res) => {
    //   let { code, data } = res.data;
    //   if (code && data) {
    //     console.log(data);
    //   }
    // })
    // .catch((err) => {
    //   console.log(err);
    // });
  }
  function edit(scope) {
    dialogTitle.value = '编辑区域';
    areaInfo.value = scope.row.data;
    isEdit.value = true;
    addEditVisible.value = true;
  }
  function del(scope) {
    $confirm('删除该区域', () =>{
      let loading = $loading();
      delArea(scope.row.id).then((res) => {
        let { code, data, msg } = res;
        if (code && data) {
          $message({
            type: 'success',
            message: '删除成功!'
          });
          getAreaTree();
        } else {
          $message.error(msg);
        }
        loading.close();
      });
    });
  }
</script>
<template>
  <div>区域管理</div>
    <div class="page-wrapper">
        <div class="page-content">
            <hdw-card is-full>
                <div class="page-content-wrapper">
                    <div class="page-content-tools">
                        <!-- <el-button type="primary" size="small" :icon="Plus" @click="add"
                            >添加</el-button
                        >
                        <el-button
                            type="primary"
                            size="small"
                            :icon="Plus"
                            @click="test"
                            >修改</el-button
                        >
                        <el-button type="danger" size="small" icon="del" @click="test"
                            >删除</el-button
                        > -->
                        <el-button
                            type="primary"
                            size="small"
                            :icon="Plus"
                            @click="expandAll(true)"
                            >全部展开</el-button
                        >
                        <el-button
                            type="primary"
                            size="small"
                            :icon="Search"
                            @click="expandAll(false)"
                            >全部折叠</el-button
                        >
                    </div>
                    <div class="page-content-table">
                        <div class="pos-rel">
                            <div class="pos-abs">
                                <el-table
                                    stripe
                                    ref="tableRef"
                                    :data="tableData"
                                    :tree-props="{ children: 'children', checkStrictly: true }"
                                    row-key="id"
                                    default-expand-all
                                >
                                    <!-- <el-table-column type="selection" width="55" /> -->
                                    <el-table-column prop="label" label="区域名称" />
                                    <el-table-column prop="charger" label="区域负责人" />
                                    <el-table-column prop="areaDescript" label="区域描述" />
                   <el-table-column align="center" fixed="right" label="操作" width="240">
                    <template #default="scope">
                      <el-button type="primary" size="small" :disabled="scope.row.data.areaLevel >= 4" @click="add(scope)">添加</el-button>
                      <el-button type="primary" size="small" @click="edit(scope)">编辑</el-button>
                      <el-button type="danger" size="small" @click="del(scope)">删除</el-button>
                    </template>
                  </el-table-column>
                                </el-table>
                            </div>
                        </div>
                    </div>
                </div>
            </hdw-card>
        </div>
        <div class="page-footer"></div>
    <!-- 弹窗 -->
    <el-dialog :title="dialogTitle" v-model="addEditVisible" top="0" :close-on-click-modal="false" class="dialog-center"
      width="700px" center>
      <add-edit v-if="addEditVisible" @success="onOk" :info="areaInfo" :is-edit="isEdit" @cancel="onCanel"></add-edit>
    </el-dialog>
    </div>
</template>
<style scoped lang="scss">
    .page-wrapper {
        display: flex;
        flex-direction: row;
        padding: 8px;
        height: 100%;
        .page-content {
            flex: 1;
        }
    }
    .page-content-wrapper {
        display: flex;
        flex-direction: column;
        height: 100%;
        .page-content-tools {
            padding-bottom: 8px;
        }
        .page-content-table {
            border-top: 1px solid var(--border-light-color);
            box-sizing: border-box;
            flex: 1;
            margin-left: -8px;
            margin-right: -8px;
        }
        .page-content-page {
            padding: 8px 8px 0 8px;
            text-align: center;
            .el-page-container {
                display: inline-block;
                padding: 0 16px;
            }
            .page-tool {
                display: inline-block;
            }
        }
    }
    .hdw-card-container {
        width: 240px;
        padding-right: 8px;
        height: 100%;
    }
    .pos-rel {
        position: relative;
        width: 100%;
        height: 100%;
        .pos-abs {
            position: absolute;
            top: 0;
            bottom: 0;
            left: 0;
            right: 0;
            width: 100%;
            height: 100%;
        }
    }
    .tools-filter {
        display: inline-block;
        font-size: 14px;
        .tools-filter-item {
            display: inline-block;
            margin-right: 8px;
            .filter-label {
                display: inline-block;
            }
            .filter-content {
                display: inline-block;
            }
        }
    }
</style>
src/views/system/user/addEdit.vue
New file
@@ -0,0 +1,292 @@
<script setup>
    import { ref, reactive, computed, onMounted } from "vue";
    import { addUser, updateUser } from "@/api/user";
    import useElement from "@/hooks/useElement.js";
    import { getAreaTreeApi } from "@/api/area";
    import { formatAreaTree } from "@/utils/tree";
    const { $loading, $message, $confirm } = useElement();
    const props = defineProps({
        info: {
            type: Object,
        },
    });
    const props1 = { value: 'id', checkStrictly: true, multiple: true };
    const formRef = ref();
    const areaList = ref([]);
    const form1 = reactive({
        uname: "",
        uid: "",
        urole: "",
        realName: "",
        phoneNumber: "",
        address: "",
        areaId: 0,
        permission: false,
    });
  const otherIdList = ref([]);
    const rules = {
        uname: [
            {
                required: true,
                message: "不能为空",
                trigger: ["blur", "change"],
            },
        ],
        realName: [
            {
                required: true,
                message: "不能为空",
                trigger: ["blur", "change"],
            },
        ],
        urole: [
            {
                required: true,
                message: "不能为空",
                trigger: ["blur", "change"],
            },
        ],
        areaId: [
            {
                required: true,
                message: "不能为空",
                trigger: ["change", "blur"],
            },
            {
                validator: (rule, value, callback) => {
                    if (!value.length) {
                        callback(new Error("请选择区域"));
                    } else {
                        callback();
                    }
                },
                trigger: ["change", "blur"],
            },
        ],
    };
    const isEdit = computed(() => !!props.info?.uid);
    const $emit = defineEmits(["cancel", "success"]);
    function close() {
        $emit("cancel");
    }
    async function update() {
        let valid = await formRef.value.validate(() => { });
        // console.log('valid', valid, '=============');
        if (!valid) {
            $message.error("表单验证失败");
            return false;
        }
        let params = {
            uname: form1.uname.trim(),
            uid: form1.uid || undefined,
            realName: form1.realName.trim(),
            phoneNumber: form1.phoneNumber?.trim(),
            address: form1.address?.trim(),
            idList: form1.areaId.map(v => v[v.length - 1]).concat(otherIdList.value),
            urole: form1.urole,
        };
    // 编辑用户时, 区域中不在管理员管理内的区域要挑出来 最后更新时再追加进去
        console.log("params update", params, "=============");
        let loading = $loading();
        updateUser(params)
            .then((res) => {
                let { code, data } = res;
                if (code && data) {
                    $emit("success");
                    $message.success("操作成功");
                } else {
                    $message.error("操作失败");
                }
                loading.close();
            })
            .catch((err) => {
                console.log(err);
                loading.close();
            });
    }
    async function onSubmit() {
        let valid = await formRef.value.validate(() => { });
        // console.log('valid', valid, '=============');
        if (!valid) {
            $message.error("表单验证失败");
            return false;
        }
        let params = {
            uname: form1.uname.trim(),
            uid: form1.uid || undefined,
            realName: form1.realName.trim(),
            phoneNumber: form1.phoneNumber?.trim(),
            address: form1.address?.trim(),
            idList: form1.areaId.map(v => v[v.length - 1]),
            urole: form1.urole,
        };
        console.log("params", params, "=============");
        let loading = $loading();
        addUser(params)
            .then((res) => {
                let { code, data } = res;
                if (code && data) {
                    $emit("success");
                    $message.success("操作成功");
                } else {
                    $message.error("操作失败");
                }
                loading.close();
            })
            .catch((err) => {
                console.log(err);
                loading.close();
            });
    }
    async function getAreaTree() {
        try {
            const res = await getAreaTreeApi();
            let _data = [];
            if (res.code === 1 && res.data) {
                _data = res.data2;
            }
            const treeList = [];
            let ids = _data.map((v) => v.id);
            for (let i = 0; i < _data.length; i++) {
                formatAreaTree(_data[i], ids, treeList);
            }
            // console.log(_data, 'data');
            console.log(treeList, 'treeList');
            areaList.value = treeList;
      formatAreaSelected(treeList);
        } catch (e) {
            console.log(e);
        }
    }
    // 区域树 根节点
    // const areaRoots = computed(() => {
    //     if (!areaList.value) {
    //         return [];
    //     } else {
    //         return areaList.value.map(v => v.id);
    //     }
    // });
  function formatAreaSelected(treeList) {
    let roots = treeList.map(v => v.id);
    let otherAreas = form1.areaId.filter(v=>!v.some((vv) => roots.includes(vv)));
    let otherIds = otherAreas.map(v => v[v.length - 1]);
    otherIdList.value = otherIds;
    form1.areaId = form1.areaId.filter(v=>v.some((vv) => roots.includes(vv))).map(v => {
      let idx = 0;
      for (let i = 0; i < roots.length; i++) {
        if (v.includes(roots[i])) {
          idx = v.indexOf(roots[i]);
          break;
        }
      }
      return v.slice(idx);
    });
    console.log('form1.areaId', form1.areaId, '=============');
  }
    function handleChange(value) {
    let oldV = value.slice(0, value.length - 1);
    let newV = value[value.length - 1];
    console.log('value', value, '=============', oldV, newV);
        // 检查新选中的项是否与已选中的项有包含关系
        const hasInclusion = checkInclusion(newV, oldV);
        if (hasInclusion) {
            // 弹出对话框询问用户是否取消之前的项
      $message.error('取消之前的项(新选中的项与已选中的项存在包含关系)');
      form1.areaId = value.slice(0, value.length - 1);
            // $confirm('取消之前的项(新选中的项与已选中的项存在包含关系)', () => {
            //     form1.areaId = value;
            // });
        }
    };
    function checkInclusion(newValue, oldValue) {
    // 遍历新选中的项
            for (let j = 0; j < oldValue.length; j++) {
        const oldItem = oldValue[j];
                if (isIncluded(newValue, oldItem) || isIncluded(oldItem, newValue)) {
          return true;
                }
            }
        return false;
    };
    function isIncluded(item1, item2) {
    // 新元素是一个选项 取数组的最后一个元素
    const id = item1[item1.length - 1];
    return item2.includes(id);
    };
    onMounted(() => {
        let info = props.info;
        if (info) {
            form1.uname = info.uname;
            form1.uid = info.uid;
            form1.realName = info.realName;
            form1.phoneNumber = info.phoneNumber;
            form1.address = info.address;
            form1.areaId = info.areaId;
            form1.urole = info.urole;
        }
        getAreaTree();
    });
</script>
<template>
  <div class="">
    <el-form ref="formRef" :model="form1" label-width="80px" :rules="rules">
      <el-form-item label="用户名" prop="uname">
        <el-input v-model="form1.uname" :disabled="isEdit"></el-input>
      </el-form-item>
      <el-form-item label="真实姓名" prop="realName">
        <el-input v-model="form1.realName"></el-input>
      </el-form-item>
      <el-form-item label="手机号" prop="phoneNumber">
        <el-input v-model="form1.phoneNumber"></el-input>
      </el-form-item>
      <el-form-item label="所属区域" prop="areaId">
        <el-cascader class="select" filterable clearable v-model="form1.areaId" :props="props1" @change="handleChange"
          :options="areaList"><template #default="{ node, data }">
            <span>{{ data.label }}</span>
            <span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
          </template></el-cascader>
      </el-form-item>
      <el-form-item label="所属角色" prop="urole">
        <el-select v-model="form1.urole">
          <el-option label="普通用户" :value="0" />
          <el-option label="区域管理员" :value="1" />
        </el-select>
      </el-form-item>
      <el-form-item label="通讯地址" prop="address">
        <el-input v-model="form1.address"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button v-if="isEdit" type="primary" @click="update">修改</el-button>
        <el-button v-else type="primary" @click="onSubmit">新增</el-button>
        <el-button @click="close">取消</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>
<style scoped lang="scss">
:deep(.select) {
  width: 100%;
}
</style>
src/views/system/user/index.vue
@@ -1,15 +1,369 @@
<script lang="ts">
import { defineComponent } from 'vue';
<script setup name="UserManage">
    import { ref, reactive, onMounted, computed } from "vue";
    import { storeToRefs } from "pinia";
    import { Search, Plus } from "@element-plus/icons-vue";
    import HdwCard from "@/components/HdwCard/index.vue";
    import addEdit from "./addEdit.vue";
    import { ElMessage } from "element-plus";
    import useElement from "@/hooks/useElement.js";
    import store from "@/store";
    import {
        getAllUser,
        deleteUser,
        dropRole,
        improveRole,
        resetSnId,
    } from "@/api/user";
export default defineComponent({
  name: 'UserManage'
});
    const { $loading, $message, $confirm } = useElement();
    // const $message = ElMessage;
    const headers = [
        {
            prop: "uname",
            label: "姓名",
            width: "",
        },
        {
            prop: "createTime",
            label: "创建时间",
            width: "",
        },
        // {
        //   prop: "canDownload",
        //   label: "控制权限",
        //   width: "",
        // },
    ];
    const background = ref(true);
    const disabled = ref(false);
    const pageCurr = ref(1);
    const pageSize = ref(10);
    const total = ref(0);
    const addEditVisible = ref(false);
    const dialogTitle = ref("");
    const currentAreaId = ref();
    const currentAreaIds = ref([]);
    const datas = reactive({
        tableData: [],
        userInfo: {},
    });
    const uname = computed(() => store.user().name);
    // const tableData = reactive([]);
    // const userInfo = reactive({});
    // const userStore = useUserStore();
    // const { uid, uname } = storeToRefs(userStore);
    function getList() {
        let loading = $loading();
        getAllUser(currentAreaId.value, pageCurr.value, pageSize.value)
            .then((res) => {
                let { code, data, data2 } = res;
                let list = [];
                let _total = 0;
                if (code && data) {
                    // console.log(data);
                    list = data2.list;
                    _total = data2.total;
                }
                loading.close();
                // tableData.length = 0;
                // tableData.push(...list);
                datas.tableData = list;
                total.value = _total;
            })
            .catch((err) => {
                console.log(err);
                loading.close();
            });
    }
    // 展示数据数量
    function handleSizeChange(val) {
        pageSize.value = val;
        getList();
    }
    // 翻页
    function handleCurrentChange(val) {
        pageCurr.value = val;
        getList();
    }
    function addUser() {
        dialogTitle.value = "添加用户";
        datas.userInfo = { areaId: currentAreaIds.value };
        addEditVisible.value = true;
    }
    function edit(record) {
        dialogTitle.value = "编辑用户";
        addEditVisible.value = true;
        console.log(record);
        let ids = record.ainfList.map(v => {
            let res = v.idPath.split("_").map((vv) => vv * 1);
            res.push(v.id);
            return res
        });
        // if (record.ainfList.idPath) {
        //     ids = record.ainfList.idPath.split("_").map((v) => v * 1);
        // }
        // ids.push(record.areaId);
        console.log('ids', ids, '=============');
        datas.userInfo = { ...record, areaId: ids };
    }
    function confirmRemove(record) {
        $confirm("删除该用户", () => {
            remove(record.uname);
        });
    }
    function remove(uname) {
        let loading = $loading();
        deleteUser(uname)
            .then((res) => {
                let { code, data } = res;
                loading.close();
                if (code && data) {
                    $message.success("操作成功");
                    handleCurrentChange(1);
                } else {
                    $message.success("操作失败");
                }
            })
            .catch((err) => {
                loading.close();
                console.log(err);
            });
    }
    function onOk() {
        addEditVisible.value = false;
        handleCurrentChange(1);
    }
    function onCanel() {
        addEditVisible.value = false;
    }
    function improveRolefn(record) {
        let loading = $loading();
        improveRole(record.uid)
            .then((res) => {
                let { code, data, msg } = res;
                loading.close();
                if (code && data) {
                    $message.success(msg);
                    getList();
                } else {
                    $message.error(msg);
                }
            })
            .catch((err) => {
                console.log(err);
                loading.close();
            });
    }
    function dropRolefn(record) {
        let loading = $loading();
        dropRole(record.uid)
            .then((res) => {
                let { code, data, msg } = res;
                loading.close();
                if (code && data) {
                    $message.success(msg);
                    getList();
                } else {
                    $message.error(msg);
                }
            })
            .catch((err) => {
                loading.close();
                console.log(err);
            });
    }
    function resetSnIdfn(record) {
        $confirm("重置该用户密码", () => {
            let loading = $loading();
            resetSnId(record.uid).then((res) => {
                let { code, data, msg } = res;
                if (code && data) {
                    $message.success(msg);
                } else {
                    $message.error(msg);
                }
                loading.close();
            });
        });
    }
    function itemClickHandler(item) {
        console.log(item, "====item", item.data);
        // areaId lockName lockState lockType pageNum pageSize
        currentAreaId.value = item.data.id;
        currentAreaIds.value = item.data.idPath
            ? item.data.idPath.split("_").map((v) => v * 1)
            : [];
        currentAreaIds.value.push(item.data.id);
        getList();
    }
    onMounted(() => {
        // getList();
    });
</script>
<template>
  <div>用户管理</div>
  <div class="page-wrapper">
    <div class="page-header">
      <div class="hdw-card-container">
        <hdw-card title="区域列表" is-full>
          <hdw-tree @item-click="itemClickHandler"></hdw-tree>
        </hdw-card>
      </div>
    </div>
    <div class="page-content">
      <hdw-card is-full>
        <div class="page-content-wrapper">
          <div class="page-content-tools">
            <el-button type="primary" round size="small" @click="addUser" :icon="Plus">添加</el-button>
            <el-button type="primary" round size="small" @click="getList" :icon="Search">查询</el-button>
          </div>
          <div class="page-content-table">
            <div class="pos-rel">
              <div class="pos-abs">
                <el-table class="yc-table" stripe height="100%" size="small" :data="datas.tableData" style="width: 100%">
                  <el-table-column type="index" label="序号" width="80"></el-table-column>
                  <el-table-column v-for="header in headers" :key="header.prop" :prop="header.prop" :label="header.label"
                    :min-width="header.width" align="center"></el-table-column>
                  <el-table-column label="操作" width="360" align="center">
                    <template #default="scope">
                      <el-button type="primary" size="small" :disabled="scope.row.uname == uname"
                        @click="edit(scope.row)">编辑</el-button>
                      <el-button type="primary" size="small" :disabled="scope.row.uname == uname"
                        @click="resetSnIdfn(scope.row)">重置密码</el-button>
                      <el-button type="danger" size="small" :disabled="scope.row.uname == uname"
                        @click="confirmRemove(scope.row)">删除</el-button>
                      <!-- <el-button
                                                    type="success"
                                                    v-if="scope.row.uid > 1000"
                                                    size="small"
                                                    @click="improveRolefn(scope.row)"
                                                    >加入管理员组</el-button
                                                >
                                                <el-button
                                                    type="danger"
                                                    v-else
                                                    :disabled="scope.row.uid == uid"
                                                    size="small"
                                                    @click="dropRolefn(scope.row)"
                                                    >移出管理员组</el-button
                                                > -->
                    </template>
                  </el-table-column>
                </el-table>
              </div>
            </div>
          </div>
          <div class="page-content-page">
            <div class="page-tool"></div>
            <div class="el-page-container">
              <el-pagination v-model:current-page="pageCurr" v-model:page-size="pageSize"
                :page-sizes="[20, 40, 60, 80, 100, 200, 300, 400]" size="small" :disabled="disabled"
                :background="background" layout="total, sizes, prev, pager, next, jumper" :total="total"
                @size-change="handleSizeChange" @current-change="handleCurrentChange" />
            </div>
            <div class="page-tool"></div>
          </div>
        </div>
      </hdw-card>
    </div>
    <div class="page-footer"></div>
    <!-- 弹窗 -->
    <el-dialog :title="dialogTitle" v-model="addEditVisible" top="0" :close-on-click-modal="false" class="dialog-center"
      width="700px" center>
      <add-edit v-if="addEditVisible" @success="onOk" :info="datas.userInfo" @cancel="onCanel"></add-edit>
    </el-dialog>
  </div>
</template>
<style scoped lang="scss">
.page-wrapper {
  display: flex;
  flex-direction: row;
  padding: 8px;
  height: 100%;
  .page-content {
    flex: 1;
  }
}
.page-content-wrapper {
  display: flex;
  flex-direction: column;
  height: 100%;
  .page-content-tools {
    padding-bottom: 8px;
  }
  .page-content-table {
    border-top: 1px solid var(--border-light-color);
    box-sizing: border-box;
    flex: 1;
    margin-left: -8px;
    margin-right: -8px;
  }
  .page-content-page {
    padding: 8px 8px 0 8px;
    text-align: center;
    .el-page-container {
      display: inline-block;
      padding: 0 16px;
    }
    .page-tool {
      display: inline-block;
    }
  }
}
.hdw-card-container {
  width: 240px;
  padding-right: 8px;
  height: 100%;
}
.pos-rel {
  position: relative;
  width: 100%;
  height: 100%;
  .pos-abs {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    height: 100%;
  }
}
.tools-filter {
  display: inline-block;
  font-size: 14px;
  .tools-filter-item {
    display: inline-block;
    margin-right: 8px;
    .filter-label {
      display: inline-block;
    }
    .filter-content {
      display: inline-block;
    }
  }
}
</style>
src/views/system/user/userFaceManager.vue
New file
@@ -0,0 +1,322 @@
<script setup name="userFaceManager">
    // import userInfo from '@/assets/js/userMager/userInfo';
    // import faceManager from '../login/components/face/faceManager';
    // import faceManager from '../../assets/js/apis/faceManager/faceManager.js'
    import { faceInfo, faceMagerAdd, faceMagerChange, faceMagerDelete } from '@/api/face';
    import { ref, onMounted } from 'vue';
    import useElement from "@/hooks/useElement.js";
    const { $loading, $message, $confirm } = useElement();
    const faceVisible = ref(false);
    const dataList = ref([]);
  const imageUrl = ref('');
    const updateFace = ref({
        id: 0,
        uid: '',
        uname: '',
        msg: '只能上传jpg/png文件,且不超过2M'
    });
    onMounted(() => {
        queryList();
    });
    // 查询用户列表
    function queryList() {
        let obj = null;
        faceInfo(obj, "GET").then(result => {
            console.log(result)
            let params = [];
            if (result.code == 1) {
                params.push(result.data);
            }
            console.log('params', params, '=============');
            dataList.value = params;
        }).catch(err => {
        })
    }
    // 添加/更新
    function handleClick(row) {
        listObj(row);
        faceVisible.value = true;
    }
    // 删除
    function deleteData(row) {
        listObj(row);
        if (!row.face) {
            $message.success('用户未上传人脸!');
            return false;
        }
        let obj = {
            uId: updateFace.uid,
            uName: updateFace.uname,
            faceId: updateFace.id
        }
        $confirm('删除人脸?', { icon: 3 }, index => {
            faceMagerDelete(obj).then(res => {
                let result = res.data;
                if (result.code == 1) {
                    $message.success('删除人脸成功!');
                    queryList();
                } else {
                    $message.error('删除人脸失败!');
                }
            }).catch(err => {
                $message.error('删除人脸失败!');
            })
            // 关闭弹出框
            $layer.close(index);
        })
    }
    // 处理参数
    function listObj(row) {
        updateFace.uid = row.uid;
        updateFace.uname = row.uname;
        updateFace.id = row.face ? row.face.id : null;
    }
    // 上传图片前
    function uploadImg(file) {
        const isJPG = file.type === 'image/jpeg' || file.type === 'image/png';
        const isLt2M = file.size / 1024 / 1024 < 2;
        if (!isJPG) {
            $message.error('上传头像图片只能是 JPG或PNG 格式!');
            return false;
        }
        if (!isLt2M) {
            $message.error('上传头像图片大小不能超过 2MB!');
            return false;
        }
        // 图片转base64
        getBase64(file).then(res => {
            imageUrl.value = res;
            // //上传图片
            let uname = updateFace.uname;
            let jsontemp = {
                fileData: res,
                uName: uname
            }
            // let imgData ='"' + res + '"';
            if (!updateFace.id) {//新增
                jsontemp.uId = updateFace.uid.toString();
                faceMagerAdd(jsontemp, "POST").then(res => {
                    let result = res.data;
                    if (result.code == 1) {
                        //隐藏弹窗
                        updateFace.show = false;
                        if (result.data === false) {
                            $message.error(result.msg);
                        } else {
                            // let text = updateFace.faceId ==0?'新增人脸成功!':'更新人脸成功!';
                            $message.success("新增人脸成功!");
                            queryList();
                        }
                    } else {
                        $message.error('上传失败,请重新上传!');
                    }
                }).catch(err => {
                    $message.error('上传失败,请重新上传!');
                })
            } else {
                jsontemp.fileData = '"' + jsontemp.fileData + '"';
                faceMagerChange(jsontemp).then(res => {
                    let result = res.data;
                    if (result.code == 1) {
                        //隐藏弹窗
                        updateFace.show = false;
                        // let text = updateFace.faceId ==0?'新增人脸成功!':'更新人脸成功!';
                        if (result.data === false) {
                            $message.error(result.msg);
                        } else {
                            $message.success("更新人脸成功!");
                            queryList();
                        }
                    } else {
                        $message.error('上传失败,请重新上传!');
                    }
                }).catch(err => {
                    $message.error('上传失败,请重新上传!');
                })
            }
        })
        return false;
    }
    // 图片转base64
    function getBase64(file) {
        return new Promise(function (resolve, reject) {
            let reader = new FileReader();
            let imgResult = "";
            reader.readAsDataURL(file);
            reader.onload = function () {
                imgResult = reader.result;
            };
            reader.onerror = function (error) {
                reject(error);
            };
            reader.onloadend = function () {
                resolve(imgResult);
            };
        });
    }
</script>
<template>
  <div class="page-wrapper">
    <div class="page-header">
    </div>
    <div class="page-content">
      <hdw-card is-full>
        <div class="page-content-wrapper">
          <div class="page-content-table">
            <div class="pos-rel">
              <div class="pos-abs">
                <el-table class="yc-table" stripe height="100%" size="small" :data="dataList" style="width: 100%">
                  <el-table-column type="index" label="编号" min-width="90" align="center">
                  </el-table-column>
                  <el-table-column prop="uname" label="用户名称" min-width="160" align="center"></el-table-column>
                  <el-table-column label="状态" min-width="160" align="center">
                    <template #default="scope">
                      <span v-if="!scope.row.face">未上传人脸</span>
                      <span v-else>已上传人脸</span>
                    </template>
                  </el-table-column>
                  <el-table-column label="操作" width="250" align="center">
                    <template #default="scope">
                      <el-button @click="handleClick(scope.row)" type="primary" size="small">{{
                        !scope.row.face?'添加人脸':'更新人脸' }}</el-button>
                      <el-button type="danger" size="small" @click="deleteData(scope.row)">删除</el-button>
                    </template>
                  </el-table-column>
                </el-table>
              </div>
            </div>
          </div>
          <div class="page-content-page">
            <div class="page-tool"></div>
          </div>
        </div>
      </hdw-card>
    </div>
    <div class="page-footer"></div>
    <!-- 人脸上传 -->
    <el-dialog title="人脸上传" v-model="faceVisible" top="0" :close-on-click-modal="false" class="dialog-center"
      width="700px" center>
      <el-upload :before-upload="uploadImg"  class="upload-demo" action="#" drag multiple>
        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
        <template #tip>
          <div class="el-upload__tip">{{ updateFace.msg }}
          </div>
        </template>
      </el-upload>
    </el-dialog>
  </div>
</template>
<style scoped lang="scss">
.page-wrapper {
  display: flex;
  flex-direction: row;
  padding: 8px;
  height: 100%;
  .page-content {
    flex: 1;
  }
}
.page-content-wrapper {
  display: flex;
  flex-direction: column;
  height: 100%;
  .page-content-tools {
    padding-bottom: 8px;
  }
  .page-content-table {
    border-top: 1px solid var(--border-light-color);
    box-sizing: border-box;
    flex: 1;
    margin-left: -8px;
    margin-right: -8px;
  }
  .page-content-page {
    padding: 8px 8px 0 8px;
    text-align: center;
    .el-page-container {
      display: inline-block;
      padding: 0 16px;
    }
    .page-tool {
      display: inline-block;
    }
  }
}
.hdw-card-container {
  width: 240px;
  padding-right: 8px;
  height: 100%;
}
.pos-rel {
  position: relative;
  width: 100%;
  height: 100%;
  .pos-abs {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    height: 100%;
  }
}
.tools-filter {
  display: inline-block;
  font-size: 14px;
  .tools-filter-item {
    display: inline-block;
    margin-right: 8px;
    .filter-label {
      display: inline-block;
    }
    .filter-content {
      display: inline-block;
    }
  }
}
.face_manager {
  .upload-demo {
    text-align: center;
    padding: 10px 0;
    .el-upload {
      width: 100%;
      .el-upload-dragger {
        width: 100%;
        border: none;
      }
    }
    .el-upload__tip {
      color: red;
    }
  }
}
</style>
vite.config.ts
@@ -3,7 +3,7 @@
import { defineConfig, loadEnv } from 'vite';
import vue from '@vitejs/plugin-vue';
import VueSetupExtend from 'vite-plugin-vue-setup-extend';
import Inspect from 'vite-plugin-inspect';
// element plus 样式自动按需导入
@@ -23,7 +23,7 @@
  const prodMock = true;
  return {
    base: '/', // 注意,必须以"/"结尾,BASE_URL配置
    base: './', // 注意,必须以"/"结尾,BASE_URL配置
    build: {
      outDir: '../hm-lock-sys/public'
    },
@@ -39,6 +39,7 @@
    plugins: [
      vue(),
      Inspect(),
      VueSetupExtend(),
      AutoImport({
        resolvers: [ElementPlusResolver()]
      }),
@@ -68,6 +69,9 @@
      })
    ],
    server: {
      // https: true,
      // key: './key.pem',
      // cert: './cert.pem',
      host: 'localhost',
      port: 8001
    }
vite.config.ts.timestamp-1736237543907-c4ab2533782e7.mjs
New file
@@ -0,0 +1,75 @@
// vite.config.ts
import { fileURLToPath, URL } from "node:url";
import path from "node:path";
import { defineConfig, loadEnv } from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/vite@5.4.11_@types+node@20.17.10_sass@1.83.0/node_modules/vite/dist/node/index.js";
import vue from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/@vitejs+plugin-vue@5.2.1_vite@5.4.11_vue@3.5.13/node_modules/@vitejs/plugin-vue/dist/index.mjs";
import VueSetupExtend from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/vite-plugin-vue-setup-extend@0.4.0_vite@5.4.11/node_modules/vite-plugin-vue-setup-extend/dist/index.mjs";
import Inspect from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/vite-plugin-inspect@0.8.9_vite@5.4.11/node_modules/vite-plugin-inspect/dist/index.mjs";
import AutoImport from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/unplugin-auto-import@0.17.8/node_modules/unplugin-auto-import/dist/vite.js";
import Components from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/unplugin-vue-components@0.27.5_vue@3.5.13/node_modules/unplugin-vue-components/dist/vite.js";
import { ElementPlusResolver } from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/unplugin-vue-components@0.27.5_vue@3.5.13/node_modules/unplugin-vue-components/dist/resolvers.js";
import svgSprites from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/rollup-plugin-svg-sprites@1.2.5_@vue+compiler-sfc@3.5.13/node_modules/rollup-plugin-svg-sprites/lib/index.js";
import { viteMockServe } from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/vite-plugin-mock@2.9.8_mockjs@1.1.0_vite@5.4.11/node_modules/vite-plugin-mock/dist/index.js";
var __vite_injected_original_import_meta_url = "file:///D:/GitWorkSpace/hm-lock/vite.config.ts";
var vite_config_default = defineConfig(({ command, mode }) => {
  console.log("vite.config defineConfig", command, mode);
  const env = loadEnv(mode, process.cwd(), "");
  console.log("vite.config env.NODE_ENV=", env.NODE_ENV);
  const prodMock = true;
  return {
    base: "./",
    // 注意,必须以"/"结尾,BASE_URL配置
    build: {
      outDir: "../hm-lock-sys/public"
    },
    define: {
      "process.env": env
    },
    resolve: {
      alias: {
        "@": fileURLToPath(new URL("./src", __vite_injected_original_import_meta_url))
      },
      extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".vue"]
    },
    plugins: [
      vue(),
      Inspect(),
      VueSetupExtend(),
      AutoImport({
        resolvers: [ElementPlusResolver()]
      }),
      Components({
        resolvers: [ElementPlusResolver()]
      }),
      svgSprites({
        vueComponent: true,
        exclude: ["node_modules/**"],
        symbolId(filePath) {
          const filename = path.basename(filePath);
          return "icon-" + filename.substring(0, filename.lastIndexOf("."));
        }
      }),
      // https://openbase.com/js/vite-plugin-mock
      viteMockServe({
        ignore: /^_/,
        mockPath: "./mock/",
        supportTs: true,
        watchFiles: true,
        localEnabled: command === "serve",
        prodEnabled: command !== "serve" && prodMock,
        // configPath: './mock/index.js',
        logger: false,
        injectCode: `import { setupProdMockServer } from '../mock/_createProductionServer.js';
      setupProdMockServer();`
      })
    ],
    server: {
      host: "localhost",
      port: 8001
    }
  };
});
export {
  vite_config_default as default
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJEOlxcXFxHaXRXb3JrU3BhY2VcXFxcaG0tbG9ja1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiRDpcXFxcR2l0V29ya1NwYWNlXFxcXGhtLWxvY2tcXFxcdml0ZS5jb25maWcudHNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL0Q6L0dpdFdvcmtTcGFjZS9obS1sb2NrL3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZmlsZVVSTFRvUGF0aCwgVVJMIH0gZnJvbSAnbm9kZTp1cmwnO1xyXG5pbXBvcnQgcGF0aCBmcm9tICdub2RlOnBhdGgnO1xyXG5cclxuaW1wb3J0IHsgZGVmaW5lQ29uZmlnLCBsb2FkRW52IH0gZnJvbSAndml0ZSc7XHJcbmltcG9ydCB2dWUgZnJvbSAnQHZpdGVqcy9wbHVnaW4tdnVlJztcclxuaW1wb3J0IFZ1ZVNldHVwRXh0ZW5kIGZyb20gJ3ZpdGUtcGx1Z2luLXZ1ZS1zZXR1cC1leHRlbmQnO1xyXG5pbXBvcnQgSW5zcGVjdCBmcm9tICd2aXRlLXBsdWdpbi1pbnNwZWN0JztcclxuXHJcbi8vIGVsZW1lbnQgcGx1cyBcdTY4MzdcdTVGMEZcdTgxRUFcdTUyQThcdTYzMDlcdTk3MDBcdTVCRkNcdTUxNjVcclxuaW1wb3J0IEF1dG9JbXBvcnQgZnJvbSAndW5wbHVnaW4tYXV0by1pbXBvcnQvdml0ZSc7XHJcbmltcG9ydCBDb21wb25lbnRzIGZyb20gJ3VucGx1Z2luLXZ1ZS1jb21wb25lbnRzL3ZpdGUnO1xyXG5pbXBvcnQgeyBFbGVtZW50UGx1c1Jlc29sdmVyIH0gZnJvbSAndW5wbHVnaW4tdnVlLWNvbXBvbmVudHMvcmVzb2x2ZXJzJztcclxuXHJcbmltcG9ydCBzdmdTcHJpdGVzIGZyb20gJ3JvbGx1cC1wbHVnaW4tc3ZnLXNwcml0ZXMnO1xyXG5pbXBvcnQgeyB2aXRlTW9ja1NlcnZlIH0gZnJvbSAndml0ZS1wbHVnaW4tbW9jayc7XHJcblxyXG4vLyBodHRwczovL3ZpdGVqcy5kZXYvY29uZmlnL1xyXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVDb25maWcoKHsgY29tbWFuZCwgbW9kZSB9KSA9PiB7XHJcbiAgY29uc29sZS5sb2coJ3ZpdGUuY29uZmlnIGRlZmluZUNvbmZpZycsIGNvbW1hbmQsIG1vZGUpO1xyXG4gIGNvbnN0IGVudiA9IGxvYWRFbnYobW9kZSwgcHJvY2Vzcy5jd2QoKSwgJycpO1xyXG4gIGNvbnNvbGUubG9nKCd2aXRlLmNvbmZpZyBlbnYuTk9ERV9FTlY9JywgZW52Lk5PREVfRU5WKTtcclxuICAvLyBBY2NvcmRpbmcgdG8gdGhlIHByb2plY3QgY29uZmlndXJhdGlvbi4gQ2FuIGJlIGNvbmZpZ3VyZWQgaW4gdGhlIC5lbnYgZmlsZVxyXG4gIGNvbnN0IHByb2RNb2NrID0gdHJ1ZTtcclxuXHJcbiAgcmV0dXJuIHtcclxuICAgIGJhc2U6ICcuLycsIC8vIFx1NkNFOFx1NjEwRlx1RkYwQ1x1NUZDNVx1OTg3Qlx1NEVFNVwiL1wiXHU3RUQzXHU1QzNFXHVGRjBDQkFTRV9VUkxcdTkxNERcdTdGNkVcclxuICAgIGJ1aWxkOiB7XHJcbiAgICAgIG91dERpcjogJy4uL2htLWxvY2stc3lzL3B1YmxpYydcclxuICAgIH0sXHJcbiAgICBkZWZpbmU6IHtcclxuICAgICAgJ3Byb2Nlc3MuZW52JzogZW52XHJcbiAgICB9LFxyXG4gICAgcmVzb2x2ZToge1xyXG4gICAgICBhbGlhczoge1xyXG4gICAgICAgICdAJzogZmlsZVVSTFRvUGF0aChuZXcgVVJMKCcuL3NyYycsIGltcG9ydC5tZXRhLnVybCkpXHJcbiAgICAgIH0sXHJcbiAgICAgIGV4dGVuc2lvbnM6IFsnLm1qcycsICcuanMnLCAnLnRzJywgJy5qc3gnLCAnLnRzeCcsICcuanNvbicsICcudnVlJ11cclxuICAgIH0sXHJcbiAgICBwbHVnaW5zOiBbXHJcbiAgICAgIHZ1ZSgpLFxyXG4gICAgICBJbnNwZWN0KCksXHJcbiAgICAgIFZ1ZVNldHVwRXh0ZW5kKCksXHJcbiAgICAgIEF1dG9JbXBvcnQoe1xyXG4gICAgICAgIHJlc29sdmVyczogW0VsZW1lbnRQbHVzUmVzb2x2ZXIoKV1cclxuICAgICAgfSksXHJcbiAgICAgIENvbXBvbmVudHMoe1xyXG4gICAgICAgIHJlc29sdmVyczogW0VsZW1lbnRQbHVzUmVzb2x2ZXIoKV1cclxuICAgICAgfSksXHJcbiAgICAgIHN2Z1Nwcml0ZXMoe1xyXG4gICAgICAgIHZ1ZUNvbXBvbmVudDogdHJ1ZSxcclxuICAgICAgICBleGNsdWRlOiBbJ25vZGVfbW9kdWxlcy8qKiddLFxyXG4gICAgICAgIHN5bWJvbElkKGZpbGVQYXRoKSB7XHJcbiAgICAgICAgICBjb25zdCBmaWxlbmFtZSA9IHBhdGguYmFzZW5hbWUoZmlsZVBhdGgpO1xyXG4gICAgICAgICAgcmV0dXJuICdpY29uLScgKyBmaWxlbmFtZS5zdWJzdHJpbmcoMCwgZmlsZW5hbWUubGFzdEluZGV4T2YoJy4nKSk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9KSxcclxuICAgICAgLy8gaHR0cHM6Ly9vcGVuYmFzZS5jb20vanMvdml0ZS1wbHVnaW4tbW9ja1xyXG4gICAgICB2aXRlTW9ja1NlcnZlKHtcclxuICAgICAgICBpZ25vcmU6IC9eXy8sXHJcbiAgICAgICAgbW9ja1BhdGg6ICcuL21vY2svJyxcclxuICAgICAgICBzdXBwb3J0VHM6IHRydWUsXHJcbiAgICAgICAgd2F0Y2hGaWxlczogdHJ1ZSxcclxuICAgICAgICBsb2NhbEVuYWJsZWQ6IGNvbW1hbmQgPT09ICdzZXJ2ZScsXHJcbiAgICAgICAgcHJvZEVuYWJsZWQ6IGNvbW1hbmQgIT09ICdzZXJ2ZScgJiYgcHJvZE1vY2ssXHJcbiAgICAgICAgLy8gY29uZmlnUGF0aDogJy4vbW9jay9pbmRleC5qcycsXHJcbiAgICAgICAgbG9nZ2VyOiBmYWxzZSxcclxuICAgICAgICBpbmplY3RDb2RlOiBgaW1wb3J0IHsgc2V0dXBQcm9kTW9ja1NlcnZlciB9IGZyb20gJy4uL21vY2svX2NyZWF0ZVByb2R1Y3Rpb25TZXJ2ZXIuanMnO1xyXG4gICAgICBzZXR1cFByb2RNb2NrU2VydmVyKCk7YFxyXG4gICAgICB9KVxyXG4gICAgXSxcclxuICAgIHNlcnZlcjoge1xyXG4gICAgICBob3N0OiAnbG9jYWxob3N0JyxcclxuICAgICAgcG9ydDogODAwMVxyXG4gICAgfVxyXG4gIH07XHJcbn0pO1xyXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQTZQLFNBQVMsZUFBZSxXQUFXO0FBQ2hTLE9BQU8sVUFBVTtBQUVqQixTQUFTLGNBQWMsZUFBZTtBQUN0QyxPQUFPLFNBQVM7QUFDaEIsT0FBTyxvQkFBb0I7QUFDM0IsT0FBTyxhQUFhO0FBR3BCLE9BQU8sZ0JBQWdCO0FBQ3ZCLE9BQU8sZ0JBQWdCO0FBQ3ZCLFNBQVMsMkJBQTJCO0FBRXBDLE9BQU8sZ0JBQWdCO0FBQ3ZCLFNBQVMscUJBQXFCO0FBZDZILElBQU0sMkNBQTJDO0FBaUI1TSxJQUFPLHNCQUFRLGFBQWEsQ0FBQyxFQUFFLFNBQVMsS0FBSyxNQUFNO0FBQ2pELFVBQVEsSUFBSSw0QkFBNEIsU0FBUyxJQUFJO0FBQ3JELFFBQU0sTUFBTSxRQUFRLE1BQU0sUUFBUSxJQUFJLEdBQUcsRUFBRTtBQUMzQyxVQUFRLElBQUksNkJBQTZCLElBQUksUUFBUTtBQUVyRCxRQUFNLFdBQVc7QUFFakIsU0FBTztBQUFBLElBQ0wsTUFBTTtBQUFBO0FBQUEsSUFDTixPQUFPO0FBQUEsTUFDTCxRQUFRO0FBQUEsSUFDVjtBQUFBLElBQ0EsUUFBUTtBQUFBLE1BQ04sZUFBZTtBQUFBLElBQ2pCO0FBQUEsSUFDQSxTQUFTO0FBQUEsTUFDUCxPQUFPO0FBQUEsUUFDTCxLQUFLLGNBQWMsSUFBSSxJQUFJLFNBQVMsd0NBQWUsQ0FBQztBQUFBLE1BQ3REO0FBQUEsTUFDQSxZQUFZLENBQUMsUUFBUSxPQUFPLE9BQU8sUUFBUSxRQUFRLFNBQVMsTUFBTTtBQUFBLElBQ3BFO0FBQUEsSUFDQSxTQUFTO0FBQUEsTUFDUCxJQUFJO0FBQUEsTUFDSixRQUFRO0FBQUEsTUFDUixlQUFlO0FBQUEsTUFDZixXQUFXO0FBQUEsUUFDVCxXQUFXLENBQUMsb0JBQW9CLENBQUM7QUFBQSxNQUNuQyxDQUFDO0FBQUEsTUFDRCxXQUFXO0FBQUEsUUFDVCxXQUFXLENBQUMsb0JBQW9CLENBQUM7QUFBQSxNQUNuQyxDQUFDO0FBQUEsTUFDRCxXQUFXO0FBQUEsUUFDVCxjQUFjO0FBQUEsUUFDZCxTQUFTLENBQUMsaUJBQWlCO0FBQUEsUUFDM0IsU0FBUyxVQUFVO0FBQ2pCLGdCQUFNLFdBQVcsS0FBSyxTQUFTLFFBQVE7QUFDdkMsaUJBQU8sVUFBVSxTQUFTLFVBQVUsR0FBRyxTQUFTLFlBQVksR0FBRyxDQUFDO0FBQUEsUUFDbEU7QUFBQSxNQUNGLENBQUM7QUFBQTtBQUFBLE1BRUQsY0FBYztBQUFBLFFBQ1osUUFBUTtBQUFBLFFBQ1IsVUFBVTtBQUFBLFFBQ1YsV0FBVztBQUFBLFFBQ1gsWUFBWTtBQUFBLFFBQ1osY0FBYyxZQUFZO0FBQUEsUUFDMUIsYUFBYSxZQUFZLFdBQVc7QUFBQTtBQUFBLFFBRXBDLFFBQVE7QUFBQSxRQUNSLFlBQVk7QUFBQTtBQUFBLE1BRWQsQ0FBQztBQUFBLElBQ0g7QUFBQSxJQUNBLFFBQVE7QUFBQSxNQUNOLE1BQU07QUFBQSxNQUNOLE1BQU07QUFBQSxJQUNSO0FBQUEsRUFDRjtBQUNGLENBQUM7IiwKICAibmFtZXMiOiBbXQp9Cg==
vite.config.ts.timestamp-1736473700275-bf6b2acdf6d8d.mjs
New file
@@ -0,0 +1,75 @@
// vite.config.ts
import { fileURLToPath, URL } from "node:url";
import path from "node:path";
import { defineConfig, loadEnv } from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/vite@5.4.11_@types+node@20.17.10_sass@1.83.0/node_modules/vite/dist/node/index.js";
import vue from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/@vitejs+plugin-vue@5.2.1_vite@5.4.11_vue@3.5.13/node_modules/@vitejs/plugin-vue/dist/index.mjs";
import VueSetupExtend from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/vite-plugin-vue-setup-extend@0.4.0_vite@5.4.11/node_modules/vite-plugin-vue-setup-extend/dist/index.mjs";
import Inspect from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/vite-plugin-inspect@0.8.9_vite@5.4.11/node_modules/vite-plugin-inspect/dist/index.mjs";
import AutoImport from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/unplugin-auto-import@0.17.8/node_modules/unplugin-auto-import/dist/vite.js";
import Components from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/unplugin-vue-components@0.27.5_vue@3.5.13/node_modules/unplugin-vue-components/dist/vite.js";
import { ElementPlusResolver } from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/unplugin-vue-components@0.27.5_vue@3.5.13/node_modules/unplugin-vue-components/dist/resolvers.js";
import svgSprites from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/rollup-plugin-svg-sprites@1.2.5_@vue+compiler-sfc@3.5.13/node_modules/rollup-plugin-svg-sprites/lib/index.js";
import { viteMockServe } from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/vite-plugin-mock@2.9.8_mockjs@1.1.0_vite@5.4.11/node_modules/vite-plugin-mock/dist/index.js";
var __vite_injected_original_import_meta_url = "file:///D:/GitWorkSpace/hm-lock/vite.config.ts";
var vite_config_default = defineConfig(({ command, mode }) => {
  console.log("vite.config defineConfig", command, mode);
  const env = loadEnv(mode, process.cwd(), "");
  console.log("vite.config env.NODE_ENV=", env.NODE_ENV);
  const prodMock = true;
  return {
    base: "./",
    // 注意,必须以"/"结尾,BASE_URL配置
    build: {
      outDir: "../hm-lock-sys/public"
    },
    define: {
      "process.env": env
    },
    resolve: {
      alias: {
        "@": fileURLToPath(new URL("./src", __vite_injected_original_import_meta_url))
      },
      extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".vue"]
    },
    plugins: [
      vue(),
      Inspect(),
      VueSetupExtend(),
      AutoImport({
        resolvers: [ElementPlusResolver()]
      }),
      Components({
        resolvers: [ElementPlusResolver()]
      }),
      svgSprites({
        vueComponent: true,
        exclude: ["node_modules/**"],
        symbolId(filePath) {
          const filename = path.basename(filePath);
          return "icon-" + filename.substring(0, filename.lastIndexOf("."));
        }
      }),
      // https://openbase.com/js/vite-plugin-mock
      viteMockServe({
        ignore: /^_/,
        mockPath: "./mock/",
        supportTs: true,
        watchFiles: true,
        localEnabled: command === "serve",
        prodEnabled: command !== "serve" && prodMock,
        // configPath: './mock/index.js',
        logger: false,
        injectCode: `import { setupProdMockServer } from '../mock/_createProductionServer.js';
      setupProdMockServer();`
      })
    ],
    server: {
      host: "localhost",
      port: 8001
    }
  };
});
export {
  vite_config_default as default
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJEOlxcXFxHaXRXb3JrU3BhY2VcXFxcaG0tbG9ja1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiRDpcXFxcR2l0V29ya1NwYWNlXFxcXGhtLWxvY2tcXFxcdml0ZS5jb25maWcudHNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL0Q6L0dpdFdvcmtTcGFjZS9obS1sb2NrL3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZmlsZVVSTFRvUGF0aCwgVVJMIH0gZnJvbSAnbm9kZTp1cmwnO1xyXG5pbXBvcnQgcGF0aCBmcm9tICdub2RlOnBhdGgnO1xyXG5cclxuaW1wb3J0IHsgZGVmaW5lQ29uZmlnLCBsb2FkRW52IH0gZnJvbSAndml0ZSc7XHJcbmltcG9ydCB2dWUgZnJvbSAnQHZpdGVqcy9wbHVnaW4tdnVlJztcclxuaW1wb3J0IFZ1ZVNldHVwRXh0ZW5kIGZyb20gJ3ZpdGUtcGx1Z2luLXZ1ZS1zZXR1cC1leHRlbmQnO1xyXG5pbXBvcnQgSW5zcGVjdCBmcm9tICd2aXRlLXBsdWdpbi1pbnNwZWN0JztcclxuXHJcbi8vIGVsZW1lbnQgcGx1cyBcdTY4MzdcdTVGMEZcdTgxRUFcdTUyQThcdTYzMDlcdTk3MDBcdTVCRkNcdTUxNjVcclxuaW1wb3J0IEF1dG9JbXBvcnQgZnJvbSAndW5wbHVnaW4tYXV0by1pbXBvcnQvdml0ZSc7XHJcbmltcG9ydCBDb21wb25lbnRzIGZyb20gJ3VucGx1Z2luLXZ1ZS1jb21wb25lbnRzL3ZpdGUnO1xyXG5pbXBvcnQgeyBFbGVtZW50UGx1c1Jlc29sdmVyIH0gZnJvbSAndW5wbHVnaW4tdnVlLWNvbXBvbmVudHMvcmVzb2x2ZXJzJztcclxuXHJcbmltcG9ydCBzdmdTcHJpdGVzIGZyb20gJ3JvbGx1cC1wbHVnaW4tc3ZnLXNwcml0ZXMnO1xyXG5pbXBvcnQgeyB2aXRlTW9ja1NlcnZlIH0gZnJvbSAndml0ZS1wbHVnaW4tbW9jayc7XHJcblxyXG4vLyBodHRwczovL3ZpdGVqcy5kZXYvY29uZmlnL1xyXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVDb25maWcoKHsgY29tbWFuZCwgbW9kZSB9KSA9PiB7XHJcbiAgY29uc29sZS5sb2coJ3ZpdGUuY29uZmlnIGRlZmluZUNvbmZpZycsIGNvbW1hbmQsIG1vZGUpO1xyXG4gIGNvbnN0IGVudiA9IGxvYWRFbnYobW9kZSwgcHJvY2Vzcy5jd2QoKSwgJycpO1xyXG4gIGNvbnNvbGUubG9nKCd2aXRlLmNvbmZpZyBlbnYuTk9ERV9FTlY9JywgZW52Lk5PREVfRU5WKTtcclxuICAvLyBBY2NvcmRpbmcgdG8gdGhlIHByb2plY3QgY29uZmlndXJhdGlvbi4gQ2FuIGJlIGNvbmZpZ3VyZWQgaW4gdGhlIC5lbnYgZmlsZVxyXG4gIGNvbnN0IHByb2RNb2NrID0gdHJ1ZTtcclxuXHJcbiAgcmV0dXJuIHtcclxuICAgIGJhc2U6ICcuLycsIC8vIFx1NkNFOFx1NjEwRlx1RkYwQ1x1NUZDNVx1OTg3Qlx1NEVFNVwiL1wiXHU3RUQzXHU1QzNFXHVGRjBDQkFTRV9VUkxcdTkxNERcdTdGNkVcclxuICAgIGJ1aWxkOiB7XHJcbiAgICAgIG91dERpcjogJy4uL2htLWxvY2stc3lzL3B1YmxpYydcclxuICAgIH0sXHJcbiAgICBkZWZpbmU6IHtcclxuICAgICAgJ3Byb2Nlc3MuZW52JzogZW52XHJcbiAgICB9LFxyXG4gICAgcmVzb2x2ZToge1xyXG4gICAgICBhbGlhczoge1xyXG4gICAgICAgICdAJzogZmlsZVVSTFRvUGF0aChuZXcgVVJMKCcuL3NyYycsIGltcG9ydC5tZXRhLnVybCkpXHJcbiAgICAgIH0sXHJcbiAgICAgIGV4dGVuc2lvbnM6IFsnLm1qcycsICcuanMnLCAnLnRzJywgJy5qc3gnLCAnLnRzeCcsICcuanNvbicsICcudnVlJ11cclxuICAgIH0sXHJcbiAgICBwbHVnaW5zOiBbXHJcbiAgICAgIHZ1ZSgpLFxyXG4gICAgICBJbnNwZWN0KCksXHJcbiAgICAgIFZ1ZVNldHVwRXh0ZW5kKCksXHJcbiAgICAgIEF1dG9JbXBvcnQoe1xyXG4gICAgICAgIHJlc29sdmVyczogW0VsZW1lbnRQbHVzUmVzb2x2ZXIoKV1cclxuICAgICAgfSksXHJcbiAgICAgIENvbXBvbmVudHMoe1xyXG4gICAgICAgIHJlc29sdmVyczogW0VsZW1lbnRQbHVzUmVzb2x2ZXIoKV1cclxuICAgICAgfSksXHJcbiAgICAgIHN2Z1Nwcml0ZXMoe1xyXG4gICAgICAgIHZ1ZUNvbXBvbmVudDogdHJ1ZSxcclxuICAgICAgICBleGNsdWRlOiBbJ25vZGVfbW9kdWxlcy8qKiddLFxyXG4gICAgICAgIHN5bWJvbElkKGZpbGVQYXRoKSB7XHJcbiAgICAgICAgICBjb25zdCBmaWxlbmFtZSA9IHBhdGguYmFzZW5hbWUoZmlsZVBhdGgpO1xyXG4gICAgICAgICAgcmV0dXJuICdpY29uLScgKyBmaWxlbmFtZS5zdWJzdHJpbmcoMCwgZmlsZW5hbWUubGFzdEluZGV4T2YoJy4nKSk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9KSxcclxuICAgICAgLy8gaHR0cHM6Ly9vcGVuYmFzZS5jb20vanMvdml0ZS1wbHVnaW4tbW9ja1xyXG4gICAgICB2aXRlTW9ja1NlcnZlKHtcclxuICAgICAgICBpZ25vcmU6IC9eXy8sXHJcbiAgICAgICAgbW9ja1BhdGg6ICcuL21vY2svJyxcclxuICAgICAgICBzdXBwb3J0VHM6IHRydWUsXHJcbiAgICAgICAgd2F0Y2hGaWxlczogdHJ1ZSxcclxuICAgICAgICBsb2NhbEVuYWJsZWQ6IGNvbW1hbmQgPT09ICdzZXJ2ZScsXHJcbiAgICAgICAgcHJvZEVuYWJsZWQ6IGNvbW1hbmQgIT09ICdzZXJ2ZScgJiYgcHJvZE1vY2ssXHJcbiAgICAgICAgLy8gY29uZmlnUGF0aDogJy4vbW9jay9pbmRleC5qcycsXHJcbiAgICAgICAgbG9nZ2VyOiBmYWxzZSxcclxuICAgICAgICBpbmplY3RDb2RlOiBgaW1wb3J0IHsgc2V0dXBQcm9kTW9ja1NlcnZlciB9IGZyb20gJy4uL21vY2svX2NyZWF0ZVByb2R1Y3Rpb25TZXJ2ZXIuanMnO1xyXG4gICAgICBzZXR1cFByb2RNb2NrU2VydmVyKCk7YFxyXG4gICAgICB9KVxyXG4gICAgXSxcclxuICAgIHNlcnZlcjoge1xyXG4gICAgICBob3N0OiAnbG9jYWxob3N0JyxcclxuICAgICAgcG9ydDogODAwMVxyXG4gICAgfVxyXG4gIH07XHJcbn0pO1xyXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQTZQLFNBQVMsZUFBZSxXQUFXO0FBQ2hTLE9BQU8sVUFBVTtBQUVqQixTQUFTLGNBQWMsZUFBZTtBQUN0QyxPQUFPLFNBQVM7QUFDaEIsT0FBTyxvQkFBb0I7QUFDM0IsT0FBTyxhQUFhO0FBR3BCLE9BQU8sZ0JBQWdCO0FBQ3ZCLE9BQU8sZ0JBQWdCO0FBQ3ZCLFNBQVMsMkJBQTJCO0FBRXBDLE9BQU8sZ0JBQWdCO0FBQ3ZCLFNBQVMscUJBQXFCO0FBZDZILElBQU0sMkNBQTJDO0FBaUI1TSxJQUFPLHNCQUFRLGFBQWEsQ0FBQyxFQUFFLFNBQVMsS0FBSyxNQUFNO0FBQ2pELFVBQVEsSUFBSSw0QkFBNEIsU0FBUyxJQUFJO0FBQ3JELFFBQU0sTUFBTSxRQUFRLE1BQU0sUUFBUSxJQUFJLEdBQUcsRUFBRTtBQUMzQyxVQUFRLElBQUksNkJBQTZCLElBQUksUUFBUTtBQUVyRCxRQUFNLFdBQVc7QUFFakIsU0FBTztBQUFBLElBQ0wsTUFBTTtBQUFBO0FBQUEsSUFDTixPQUFPO0FBQUEsTUFDTCxRQUFRO0FBQUEsSUFDVjtBQUFBLElBQ0EsUUFBUTtBQUFBLE1BQ04sZUFBZTtBQUFBLElBQ2pCO0FBQUEsSUFDQSxTQUFTO0FBQUEsTUFDUCxPQUFPO0FBQUEsUUFDTCxLQUFLLGNBQWMsSUFBSSxJQUFJLFNBQVMsd0NBQWUsQ0FBQztBQUFBLE1BQ3REO0FBQUEsTUFDQSxZQUFZLENBQUMsUUFBUSxPQUFPLE9BQU8sUUFBUSxRQUFRLFNBQVMsTUFBTTtBQUFBLElBQ3BFO0FBQUEsSUFDQSxTQUFTO0FBQUEsTUFDUCxJQUFJO0FBQUEsTUFDSixRQUFRO0FBQUEsTUFDUixlQUFlO0FBQUEsTUFDZixXQUFXO0FBQUEsUUFDVCxXQUFXLENBQUMsb0JBQW9CLENBQUM7QUFBQSxNQUNuQyxDQUFDO0FBQUEsTUFDRCxXQUFXO0FBQUEsUUFDVCxXQUFXLENBQUMsb0JBQW9CLENBQUM7QUFBQSxNQUNuQyxDQUFDO0FBQUEsTUFDRCxXQUFXO0FBQUEsUUFDVCxjQUFjO0FBQUEsUUFDZCxTQUFTLENBQUMsaUJBQWlCO0FBQUEsUUFDM0IsU0FBUyxVQUFVO0FBQ2pCLGdCQUFNLFdBQVcsS0FBSyxTQUFTLFFBQVE7QUFDdkMsaUJBQU8sVUFBVSxTQUFTLFVBQVUsR0FBRyxTQUFTLFlBQVksR0FBRyxDQUFDO0FBQUEsUUFDbEU7QUFBQSxNQUNGLENBQUM7QUFBQTtBQUFBLE1BRUQsY0FBYztBQUFBLFFBQ1osUUFBUTtBQUFBLFFBQ1IsVUFBVTtBQUFBLFFBQ1YsV0FBVztBQUFBLFFBQ1gsWUFBWTtBQUFBLFFBQ1osY0FBYyxZQUFZO0FBQUEsUUFDMUIsYUFBYSxZQUFZLFdBQVc7QUFBQTtBQUFBLFFBRXBDLFFBQVE7QUFBQSxRQUNSLFlBQVk7QUFBQTtBQUFBLE1BRWQsQ0FBQztBQUFBLElBQ0g7QUFBQSxJQUNBLFFBQVE7QUFBQSxNQUNOLE1BQU07QUFBQSxNQUNOLE1BQU07QUFBQSxJQUNSO0FBQUEsRUFDRjtBQUNGLENBQUM7IiwKICAibmFtZXMiOiBbXQp9Cg==
vite.config.ts.timestamp-1736919044075-8e0cad74d89b3.mjs
New file
@@ -0,0 +1,78 @@
// vite.config.ts
import { fileURLToPath, URL } from "node:url";
import path from "node:path";
import { defineConfig, loadEnv } from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/vite@5.4.11_@types+node@20.17.10_sass@1.83.0/node_modules/vite/dist/node/index.js";
import vue from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/@vitejs+plugin-vue@5.2.1_vite@5.4.11_vue@3.5.13/node_modules/@vitejs/plugin-vue/dist/index.mjs";
import VueSetupExtend from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/vite-plugin-vue-setup-extend@0.4.0_vite@5.4.11/node_modules/vite-plugin-vue-setup-extend/dist/index.mjs";
import Inspect from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/vite-plugin-inspect@0.8.9_vite@5.4.11/node_modules/vite-plugin-inspect/dist/index.mjs";
import AutoImport from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/unplugin-auto-import@0.17.8/node_modules/unplugin-auto-import/dist/vite.js";
import Components from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/unplugin-vue-components@0.27.5_vue@3.5.13/node_modules/unplugin-vue-components/dist/vite.js";
import { ElementPlusResolver } from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/unplugin-vue-components@0.27.5_vue@3.5.13/node_modules/unplugin-vue-components/dist/resolvers.js";
import svgSprites from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/rollup-plugin-svg-sprites@1.2.5_@vue+compiler-sfc@3.5.13/node_modules/rollup-plugin-svg-sprites/lib/index.js";
import { viteMockServe } from "file:///D:/GitWorkSpace/hm-lock/node_modules/.pnpm/vite-plugin-mock@2.9.8_mockjs@1.1.0_vite@5.4.11/node_modules/vite-plugin-mock/dist/index.js";
var __vite_injected_original_import_meta_url = "file:///D:/GitWorkSpace/hm-lock/vite.config.ts";
var vite_config_default = defineConfig(({ command, mode }) => {
  console.log("vite.config defineConfig", command, mode);
  const env = loadEnv(mode, process.cwd(), "");
  console.log("vite.config env.NODE_ENV=", env.NODE_ENV);
  const prodMock = true;
  return {
    base: "./",
    // 注意,必须以"/"结尾,BASE_URL配置
    build: {
      outDir: "../hm-lock-sys/public"
    },
    define: {
      "process.env": env
    },
    resolve: {
      alias: {
        "@": fileURLToPath(new URL("./src", __vite_injected_original_import_meta_url))
      },
      extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".vue"]
    },
    plugins: [
      vue(),
      Inspect(),
      VueSetupExtend(),
      AutoImport({
        resolvers: [ElementPlusResolver()]
      }),
      Components({
        resolvers: [ElementPlusResolver()]
      }),
      svgSprites({
        vueComponent: true,
        exclude: ["node_modules/**"],
        symbolId(filePath) {
          const filename = path.basename(filePath);
          return "icon-" + filename.substring(0, filename.lastIndexOf("."));
        }
      }),
      // https://openbase.com/js/vite-plugin-mock
      viteMockServe({
        ignore: /^_/,
        mockPath: "./mock/",
        supportTs: true,
        watchFiles: true,
        localEnabled: command === "serve",
        prodEnabled: command !== "serve" && prodMock,
        // configPath: './mock/index.js',
        logger: false,
        injectCode: `import { setupProdMockServer } from '../mock/_createProductionServer.js';
      setupProdMockServer();`
      })
    ],
    server: {
      // https: true,
      // key: './key.pem',
      // cert: './cert.pem',
      host: "localhost",
      port: 8001
    }
  };
});
export {
  vite_config_default as default
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJEOlxcXFxHaXRXb3JrU3BhY2VcXFxcaG0tbG9ja1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiRDpcXFxcR2l0V29ya1NwYWNlXFxcXGhtLWxvY2tcXFxcdml0ZS5jb25maWcudHNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL0Q6L0dpdFdvcmtTcGFjZS9obS1sb2NrL3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZmlsZVVSTFRvUGF0aCwgVVJMIH0gZnJvbSAnbm9kZTp1cmwnO1xyXG5pbXBvcnQgcGF0aCBmcm9tICdub2RlOnBhdGgnO1xyXG5cclxuaW1wb3J0IHsgZGVmaW5lQ29uZmlnLCBsb2FkRW52IH0gZnJvbSAndml0ZSc7XHJcbmltcG9ydCB2dWUgZnJvbSAnQHZpdGVqcy9wbHVnaW4tdnVlJztcclxuaW1wb3J0IFZ1ZVNldHVwRXh0ZW5kIGZyb20gJ3ZpdGUtcGx1Z2luLXZ1ZS1zZXR1cC1leHRlbmQnO1xyXG5pbXBvcnQgSW5zcGVjdCBmcm9tICd2aXRlLXBsdWdpbi1pbnNwZWN0JztcclxuXHJcbi8vIGVsZW1lbnQgcGx1cyBcdTY4MzdcdTVGMEZcdTgxRUFcdTUyQThcdTYzMDlcdTk3MDBcdTVCRkNcdTUxNjVcclxuaW1wb3J0IEF1dG9JbXBvcnQgZnJvbSAndW5wbHVnaW4tYXV0by1pbXBvcnQvdml0ZSc7XHJcbmltcG9ydCBDb21wb25lbnRzIGZyb20gJ3VucGx1Z2luLXZ1ZS1jb21wb25lbnRzL3ZpdGUnO1xyXG5pbXBvcnQgeyBFbGVtZW50UGx1c1Jlc29sdmVyIH0gZnJvbSAndW5wbHVnaW4tdnVlLWNvbXBvbmVudHMvcmVzb2x2ZXJzJztcclxuXHJcbmltcG9ydCBzdmdTcHJpdGVzIGZyb20gJ3JvbGx1cC1wbHVnaW4tc3ZnLXNwcml0ZXMnO1xyXG5pbXBvcnQgeyB2aXRlTW9ja1NlcnZlIH0gZnJvbSAndml0ZS1wbHVnaW4tbW9jayc7XHJcblxyXG4vLyBodHRwczovL3ZpdGVqcy5kZXYvY29uZmlnL1xyXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVDb25maWcoKHsgY29tbWFuZCwgbW9kZSB9KSA9PiB7XHJcbiAgY29uc29sZS5sb2coJ3ZpdGUuY29uZmlnIGRlZmluZUNvbmZpZycsIGNvbW1hbmQsIG1vZGUpO1xyXG4gIGNvbnN0IGVudiA9IGxvYWRFbnYobW9kZSwgcHJvY2Vzcy5jd2QoKSwgJycpO1xyXG4gIGNvbnNvbGUubG9nKCd2aXRlLmNvbmZpZyBlbnYuTk9ERV9FTlY9JywgZW52Lk5PREVfRU5WKTtcclxuICAvLyBBY2NvcmRpbmcgdG8gdGhlIHByb2plY3QgY29uZmlndXJhdGlvbi4gQ2FuIGJlIGNvbmZpZ3VyZWQgaW4gdGhlIC5lbnYgZmlsZVxyXG4gIGNvbnN0IHByb2RNb2NrID0gdHJ1ZTtcclxuXHJcbiAgcmV0dXJuIHtcclxuICAgIGJhc2U6ICcuLycsIC8vIFx1NkNFOFx1NjEwRlx1RkYwQ1x1NUZDNVx1OTg3Qlx1NEVFNVwiL1wiXHU3RUQzXHU1QzNFXHVGRjBDQkFTRV9VUkxcdTkxNERcdTdGNkVcclxuICAgIGJ1aWxkOiB7XHJcbiAgICAgIG91dERpcjogJy4uL2htLWxvY2stc3lzL3B1YmxpYydcclxuICAgIH0sXHJcbiAgICBkZWZpbmU6IHtcclxuICAgICAgJ3Byb2Nlc3MuZW52JzogZW52XHJcbiAgICB9LFxyXG4gICAgcmVzb2x2ZToge1xyXG4gICAgICBhbGlhczoge1xyXG4gICAgICAgICdAJzogZmlsZVVSTFRvUGF0aChuZXcgVVJMKCcuL3NyYycsIGltcG9ydC5tZXRhLnVybCkpXHJcbiAgICAgIH0sXHJcbiAgICAgIGV4dGVuc2lvbnM6IFsnLm1qcycsICcuanMnLCAnLnRzJywgJy5qc3gnLCAnLnRzeCcsICcuanNvbicsICcudnVlJ11cclxuICAgIH0sXHJcbiAgICBwbHVnaW5zOiBbXHJcbiAgICAgIHZ1ZSgpLFxyXG4gICAgICBJbnNwZWN0KCksXHJcbiAgICAgIFZ1ZVNldHVwRXh0ZW5kKCksXHJcbiAgICAgIEF1dG9JbXBvcnQoe1xyXG4gICAgICAgIHJlc29sdmVyczogW0VsZW1lbnRQbHVzUmVzb2x2ZXIoKV1cclxuICAgICAgfSksXHJcbiAgICAgIENvbXBvbmVudHMoe1xyXG4gICAgICAgIHJlc29sdmVyczogW0VsZW1lbnRQbHVzUmVzb2x2ZXIoKV1cclxuICAgICAgfSksXHJcbiAgICAgIHN2Z1Nwcml0ZXMoe1xyXG4gICAgICAgIHZ1ZUNvbXBvbmVudDogdHJ1ZSxcclxuICAgICAgICBleGNsdWRlOiBbJ25vZGVfbW9kdWxlcy8qKiddLFxyXG4gICAgICAgIHN5bWJvbElkKGZpbGVQYXRoKSB7XHJcbiAgICAgICAgICBjb25zdCBmaWxlbmFtZSA9IHBhdGguYmFzZW5hbWUoZmlsZVBhdGgpO1xyXG4gICAgICAgICAgcmV0dXJuICdpY29uLScgKyBmaWxlbmFtZS5zdWJzdHJpbmcoMCwgZmlsZW5hbWUubGFzdEluZGV4T2YoJy4nKSk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9KSxcclxuICAgICAgLy8gaHR0cHM6Ly9vcGVuYmFzZS5jb20vanMvdml0ZS1wbHVnaW4tbW9ja1xyXG4gICAgICB2aXRlTW9ja1NlcnZlKHtcclxuICAgICAgICBpZ25vcmU6IC9eXy8sXHJcbiAgICAgICAgbW9ja1BhdGg6ICcuL21vY2svJyxcclxuICAgICAgICBzdXBwb3J0VHM6IHRydWUsXHJcbiAgICAgICAgd2F0Y2hGaWxlczogdHJ1ZSxcclxuICAgICAgICBsb2NhbEVuYWJsZWQ6IGNvbW1hbmQgPT09ICdzZXJ2ZScsXHJcbiAgICAgICAgcHJvZEVuYWJsZWQ6IGNvbW1hbmQgIT09ICdzZXJ2ZScgJiYgcHJvZE1vY2ssXHJcbiAgICAgICAgLy8gY29uZmlnUGF0aDogJy4vbW9jay9pbmRleC5qcycsXHJcbiAgICAgICAgbG9nZ2VyOiBmYWxzZSxcclxuICAgICAgICBpbmplY3RDb2RlOiBgaW1wb3J0IHsgc2V0dXBQcm9kTW9ja1NlcnZlciB9IGZyb20gJy4uL21vY2svX2NyZWF0ZVByb2R1Y3Rpb25TZXJ2ZXIuanMnO1xyXG4gICAgICBzZXR1cFByb2RNb2NrU2VydmVyKCk7YFxyXG4gICAgICB9KVxyXG4gICAgXSxcclxuICAgIHNlcnZlcjoge1xyXG4gICAgICAvLyBodHRwczogdHJ1ZSxcclxuICAgICAgLy8ga2V5OiAnLi9rZXkucGVtJyxcclxuICAgICAgLy8gY2VydDogJy4vY2VydC5wZW0nLFxyXG4gICAgICBob3N0OiAnbG9jYWxob3N0JyxcclxuICAgICAgcG9ydDogODAwMVxyXG4gICAgfVxyXG4gIH07XHJcbn0pO1xyXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQTZQLFNBQVMsZUFBZSxXQUFXO0FBQ2hTLE9BQU8sVUFBVTtBQUVqQixTQUFTLGNBQWMsZUFBZTtBQUN0QyxPQUFPLFNBQVM7QUFDaEIsT0FBTyxvQkFBb0I7QUFDM0IsT0FBTyxhQUFhO0FBR3BCLE9BQU8sZ0JBQWdCO0FBQ3ZCLE9BQU8sZ0JBQWdCO0FBQ3ZCLFNBQVMsMkJBQTJCO0FBRXBDLE9BQU8sZ0JBQWdCO0FBQ3ZCLFNBQVMscUJBQXFCO0FBZDZILElBQU0sMkNBQTJDO0FBaUI1TSxJQUFPLHNCQUFRLGFBQWEsQ0FBQyxFQUFFLFNBQVMsS0FBSyxNQUFNO0FBQ2pELFVBQVEsSUFBSSw0QkFBNEIsU0FBUyxJQUFJO0FBQ3JELFFBQU0sTUFBTSxRQUFRLE1BQU0sUUFBUSxJQUFJLEdBQUcsRUFBRTtBQUMzQyxVQUFRLElBQUksNkJBQTZCLElBQUksUUFBUTtBQUVyRCxRQUFNLFdBQVc7QUFFakIsU0FBTztBQUFBLElBQ0wsTUFBTTtBQUFBO0FBQUEsSUFDTixPQUFPO0FBQUEsTUFDTCxRQUFRO0FBQUEsSUFDVjtBQUFBLElBQ0EsUUFBUTtBQUFBLE1BQ04sZUFBZTtBQUFBLElBQ2pCO0FBQUEsSUFDQSxTQUFTO0FBQUEsTUFDUCxPQUFPO0FBQUEsUUFDTCxLQUFLLGNBQWMsSUFBSSxJQUFJLFNBQVMsd0NBQWUsQ0FBQztBQUFBLE1BQ3REO0FBQUEsTUFDQSxZQUFZLENBQUMsUUFBUSxPQUFPLE9BQU8sUUFBUSxRQUFRLFNBQVMsTUFBTTtBQUFBLElBQ3BFO0FBQUEsSUFDQSxTQUFTO0FBQUEsTUFDUCxJQUFJO0FBQUEsTUFDSixRQUFRO0FBQUEsTUFDUixlQUFlO0FBQUEsTUFDZixXQUFXO0FBQUEsUUFDVCxXQUFXLENBQUMsb0JBQW9CLENBQUM7QUFBQSxNQUNuQyxDQUFDO0FBQUEsTUFDRCxXQUFXO0FBQUEsUUFDVCxXQUFXLENBQUMsb0JBQW9CLENBQUM7QUFBQSxNQUNuQyxDQUFDO0FBQUEsTUFDRCxXQUFXO0FBQUEsUUFDVCxjQUFjO0FBQUEsUUFDZCxTQUFTLENBQUMsaUJBQWlCO0FBQUEsUUFDM0IsU0FBUyxVQUFVO0FBQ2pCLGdCQUFNLFdBQVcsS0FBSyxTQUFTLFFBQVE7QUFDdkMsaUJBQU8sVUFBVSxTQUFTLFVBQVUsR0FBRyxTQUFTLFlBQVksR0FBRyxDQUFDO0FBQUEsUUFDbEU7QUFBQSxNQUNGLENBQUM7QUFBQTtBQUFBLE1BRUQsY0FBYztBQUFBLFFBQ1osUUFBUTtBQUFBLFFBQ1IsVUFBVTtBQUFBLFFBQ1YsV0FBVztBQUFBLFFBQ1gsWUFBWTtBQUFBLFFBQ1osY0FBYyxZQUFZO0FBQUEsUUFDMUIsYUFBYSxZQUFZLFdBQVc7QUFBQTtBQUFBLFFBRXBDLFFBQVE7QUFBQSxRQUNSLFlBQVk7QUFBQTtBQUFBLE1BRWQsQ0FBQztBQUFBLElBQ0g7QUFBQSxJQUNBLFFBQVE7QUFBQTtBQUFBO0FBQUE7QUFBQSxNQUlOLE1BQU07QUFBQSxNQUNOLE1BQU07QUFBQSxJQUNSO0FBQUEsRUFDRjtBQUNGLENBQUM7IiwKICAibmFtZXMiOiBbXQp9Cg==