1.修复安全入仓联动控制按钮无权限问题
2.增加安全入仓联动控制配置项,默认不启用
3.修改气体浓度验证选项默认不启用
4.增加门禁web页面源码
Changes to be committed:
new file: "face_webserver/
modified: vf107/src/config.json
modified: vf107/src/service/grainService.js
modified: vf107/src/view/config/menu/doorControlView.js
modified: vf107/src/view/mainView.js
已添加59个文件
已修改7个文件
15955 ■■■■■ 文件已修改
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/.gitignore 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/README.md 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/babel.config.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/package.json 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/public/config.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/public/favicon.ico 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/public/index.css 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/public/index.html 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/App.vue 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/bg.png 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/base64ToImg.js 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/demo.css 539 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/demo_index.html 354 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/iconfont.css 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/iconfont.eot 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/iconfont.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/iconfont.json 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/iconfont.svg 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/iconfont.ttf 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/iconfont.woff 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/iconfont.woff2 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/right.png 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/styles/theme.css 307 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/components/table/pagination.vue 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/components/table/tableList.vue 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/directives/model-permission.js 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/excel/Blob.js 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/excel/Export2Excel.js 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/ar.js 432 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/de.js 433 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/en.js 450 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/es.js 452 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/fr.js 435 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/index.js 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/ko.js 431 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/pt.js 430 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/ru.js 430 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/zh.js 452 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/main.js 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/router.js 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/utils/bus.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/utils/export.js 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/utils/index.js 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/utils/request.js 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/utils/timezones.js 790 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/config/index.vue 2241 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/control/index.vue 982 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/home/index.vue 535 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/login/index.vue 348 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/monitor/index.vue 715 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/person/addOrEdit.vue 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/person/index.vue 254 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/person/info.vue 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/person/permission.vue 702 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/person/vourcher.vue 1026 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/record/index.vue 314 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/security/add.vue 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/security/index.vue 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-修复2.0.2版本和企微bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/vue.config.js 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf107/.temp/dxide_debug.log 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf107/.temp/md5s.json 53 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf107/.temp/md5snew.json 53 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf107/src/config.json 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf107/src/service/grainService.js 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf107/src/view/config/menu/doorControlView.js 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vf107/src/view/mainView.js 278 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/.gitignore
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
/package-lock.json
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/README.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
# å®‰è£…依赖
npm install
# å¯åŠ¨å¼€å‘æœåŠ¡å™¨
npm run serve
# æž„建生产版本
npm run build
# é¡¹ç›®ç»“æž„
main.js          # Vue åº”用的入口文件,创建Vue实例挂在到DOM上
vue.config.js    # Vue CLI é…ç½®æ–‡ä»¶ï¼ˆå¯é…ç½®ä»£ç†ï¼‰
public/
├── config.js       # å­˜æ”¾è¿è¡Œæ—¶é…ç½®
├── favicon.ico   # ç½‘站图标
├── index.html     # åº”用的主文件,Vue的入口
├── index.css        # å…¨å±€CSS样式文件
src/
├── assets/       # é™æ€èµ„源
├── components/   # å…¬å…±ç»„ä»¶
├── language/     # å›½é™…化文件
├── utils/        # å·¥å…·å‡½æ•°
├   â”œâ”€â”€ bus.js  # ç»„件间通信
├   â”œâ”€â”€ request.js  # http设置
├── views/        # é¡µé¢ç»„ä»¶
└── router.js     # è·¯ç”±é…ç½®
# UI组件使用参考
https://element.eleme.cn/#/zh-CN/component/installation
# vue2学习参考
https://v2.cn.vuejs.org/v2/guide/index.html
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/babel.config.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,5 @@
module.exports = {
  presets: [
    '@vue/cli-plugin-babel/preset'
  ]
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/package.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
{
  "name": "webserver",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "axios": "^0.21.1",
    "core-js": "^3.6.5",
    "echarts": "^5.2.2",
    "element-ui": "^2.15.1",
    "file-saver": "^2.0.5",
    "install": "^0.13.0",
    "jszip": "^3.7.1",
    "npm": "^7.21.0",
    "or": "^0.2.0",
    "vue": "^2.6.11",
    "vue-i18n": "8.23.0",
    "vue-router": "^3.5.1",
    "xlsx": "^0.17.0"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-vue": "^6.2.2",
    "less": "^3.13.1",
    "less-loader": "^5.0.0",
    "script-loader": "^0.7.2",
    "vue-template-compiler": "^2.6.11"
  },
  "eslintConfig": {
    "root": true,
    "env": {
      "node": true
    },
    "extends": [
      "plugin:vue/essential",
      "eslint:recommended"
    ],
    "parserOptions": {
      "parser": "babel-eslint"
    },
    "rules": {}
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
  ]
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/public/config.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
document.title = '人脸系统'
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/public/favicon.ico
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/public/index.css
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,136 @@
html,
body,
#app {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  position: absolute;
}
h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0;
}
/*
 *
 * æœ´ç´ æŒ‰é’®å­—体颜色
 * é»˜è®¤ä¸ºé»‘色,为与主题一致改成主题色
 * ä¸Žä¸»é¢˜è‰²ä¸€è‡´
 * æ›´æ”¹ä¸»é¢˜è‰²æ—¶ï¼Œéœ€è¦æ›´æ”¹è¯¥è‰²å€¼
 *
 */
/* .el-button.el-button--default {
  color: #00bcd4;
} */
.el-button.el-button--default.el-button--primary {
  color: #ffffff;
}
/*
 * æœç´¢æ¡†ç»Ÿä¸€æ ·å¼
 */
.el-InputForm {
  background-color: #fff;
  padding: 10px 20px;
  box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.16);
  border-radius: 15px;
  position: relative;
}
.el-InputForm .el-form-item {
  margin-top: 10px;
  margin-bottom: 10px;
  padding: 5px 0;
}
/*
 * ä¾§è¾¹æ é€‰é¡¹è¡Œé«˜
 */
.el-submenu__title,
.el-menu-item {
  height: 46px !important;
  line-height: 46px !important;
}
/*
 * é¢åŒ…屑最后一项颜色
 */
.el-breadcrumb__item:last-child span {
  color: #999 !important;
}
/* è¡¨æ ¼é¼ æ ‡æ‚¬åœæ¡†å®½åº¦é™åˆ¶ */
.el-tooltip__popper {
  max-width: 90%;
  line-height: 2 !important;
}
/* ç¦ç”¨çŠ¶æ€è¾“å…¥æ¡†æ–‡å­—é¢œè‰² */
.the-main .el-textarea.is-disabled .el-textarea__inner,
.the-main .el-input.is-disabled .el-input__inner {
  color: #000;
}
.el-button {
  min-width: 120px;
}
.el-button--text {
  min-width: auto;
}
/* ä¿®æ”¹å¼€å…³æŒ‰é’®æ ·å¼ */
.el-switch__core {
  width: 60px !important;
}
.el-switch__label {
  display: none;
  color: #ffffff00 !important;
  position: absolute;
  z-index: 9;
  margin-left: 0;
  margin-right: 0;
}
.el-switch__label--right {
  right: 22px;
}
.el-switch__label--left {
  left: 22px;
}
.el-switch .is-active {
  color: #fff !important;
}
.el-dialog__footer {
  text-align: center !important;
}
.el-dialog {
  border-radius: 10px !important;
}
.dialog-footer {
  text-align: center;
  margin-top: 15px;
}
.el-message {
  pointer-events: none;
  z-index: 999 !important;
}
.el-message__content,
.el-message__closeBtn {
  pointer-events: auto;
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/public/index.html
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title>Sistema facial</title>
    <link href="./index.css" rel="stylesheet">
    <script src="./config.js"></script>
  </head>
  <body>
    <div id="app">
    <div class="loading">
      <svg width="135" height="135" viewBox="0 0 135 135" xmlns="http://www.w3.org/2000/svg" fill="#7493FF">
        <path d="M67.447 58c5.523 0 10-4.477 10-10s-4.477-10-10-10-10 4.477-10 10 4.477 10 10 10zm9.448 9.447c0 5.523 4.477 10 10 10 5.522 0 10-4.477 10-10s-4.478-10-10-10c-5.523 0-10 4.477-10 10zm-9.448 9.448c-5.523 0-10 4.477-10 10 0 5.522 4.477 10 10 10s10-4.478 10-10c0-5.523-4.477-10-10-10zM58 67.447c0-5.523-4.477-10-10-10s-10 4.477-10 10 4.477 10 10 10 10-4.477 10-10z">
          <animateTransform
          attributeName="transform"
          type="rotate"
          from="0 67 67"
          to="-360 67 67"
          dur="2.5s"
          repeatCount="indefinite"/>
        </path>
        <path d="M28.19 40.31c6.627 0 12-5.374 12-12 0-6.628-5.373-12-12-12-6.628 0-12 5.372-12 12 0 6.626 5.372 12 12 12zm30.72-19.825c4.686 4.687 12.284 4.687 16.97 0 4.686-4.686 4.686-12.284 0-16.97-4.686-4.687-12.284-4.687-16.97 0-4.687 4.686-4.687 12.284 0 16.97zm35.74 7.705c0 6.627 5.37 12 12 12 6.626 0 12-5.373 12-12 0-6.628-5.374-12-12-12-6.63 0-12 5.372-12 12zm19.822 30.72c-4.686 4.686-4.686 12.284 0 16.97 4.687 4.686 12.285 4.686 16.97 0 4.687-4.686 4.687-12.284 0-16.97-4.685-4.687-12.283-4.687-16.97 0zm-7.704 35.74c-6.627 0-12 5.37-12 12 0 6.626 5.373 12 12 12s12-5.374 12-12c0-6.63-5.373-12-12-12zm-30.72 19.822c-4.686-4.686-12.284-4.686-16.97 0-4.686 4.687-4.686 12.285 0 16.97 4.686 4.687 12.284 4.687 16.97 0 4.687-4.685 4.687-12.283 0-16.97zm-35.74-7.704c0-6.627-5.372-12-12-12-6.626 0-12 5.373-12 12s5.374 12 12 12c6.628 0 12-5.373 12-12zm-19.823-30.72c4.687-4.686 4.687-12.284 0-16.97-4.686-4.686-12.284-4.686-16.97 0-4.687 4.686-4.687 12.284 0 16.97 4.686 4.687 12.284 4.687 16.97 0z">
          <animateTransform
          attributeName="transform"
          type="rotate"
          from="0 67 67"
          to="360 67 67"
          dur="8s"
          repeatCount="indefinite"/>
        </path>
      </svg>
    </div>
  </div>
    <!-- built files will be auto injected -->
  </body>
</html>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/App.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,172 @@
<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>
<script>
export default {
  name: "App",
   mounted(){
    function checkIE(){
      return '-ms-scroll-limit' in document.documentElement.style && '-ms-ime-align' in document.documentElement.style
    }
    if (checkIE()) {
      window.addEventListener('hashchange', () => {
        var currentPath = window.location.hash.slice(1);
        if (this.$route.path !== currentPath) {
        this.$router.push(currentPath)
      }
    }, false)
    }
  }
};
</script>
<style lang="less">
@import './assets/styles/theme.css';
html {
  height: 100%;
  font-size: 14px;
  body {
    padding: 0;
    margin: 0;
    height: 100%;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif;
    background-color: var(--bg-secondary);
    color: var(--text-primary);
    line-height: 1.5715;
    #app {
      height: 100%;
    }
  }
}
/* å…¨å±€Element UI样式优化 */
.el-button {
  border-radius: var(--radius-md);
  font-weight: 500;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  &:hover {
    transform: translateY(-1px);
    box-shadow: var(--shadow-md);
  }
}
.el-input__inner {
  border-radius: var(--radius-md);
  border: 1px solid var(--border-color);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  &:focus {
    border-color: var(--primary-color);
    box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
  }
}
.el-table {
  border-radius: var(--radius-lg);
  overflow: hidden;
  .el-table__header th {
    background: var(--bg-tertiary);
    color: var(--text-primary);
    font-weight: 600;
  }
  .el-table__row:hover {
    background: rgba(24, 144, 255, 0.04);
  }
}
.el-card {
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  border: 1px solid var(--border-light);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  &:hover {
    box-shadow: var(--shadow-lg);
    transform: translateY(-2px);
  }
}
.el-drawer {
  .el-drawer__header {
    margin-bottom: 0;
    padding: var(--spacing-lg);
    border-bottom: 1px solid var(--border-light);
    background: var(--bg-primary);
  }
  .el-drawer__body {
    padding: var(--spacing-lg);
    background: var(--bg-secondary);
  }
}
.el-dialog {
  border-radius: var(--radius-lg);
  overflow: hidden;
  .el-dialog__header {
    background: var(--bg-primary);
    border-bottom: 1px solid var(--border-light);
    padding: var(--spacing-lg);
  }
  .el-dialog__body {
    background: var(--bg-secondary);
    padding: var(--spacing-lg);
  }
  .el-dialog__footer {
    background: var(--bg-primary);
    border-top: 1px solid var(--border-light);
    padding: var(--spacing-md) var(--spacing-lg);
  }
}
/* æ»šåŠ¨æ¡ç¾ŽåŒ– */
::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}
::-webkit-scrollbar-track {
  background: var(--bg-tertiary);
  border-radius: var(--radius-sm);
}
::-webkit-scrollbar-thumb {
  background: var(--border-color);
  border-radius: var(--radius-sm);
  transition: background 0.3s ease;
}
::-webkit-scrollbar-thumb:hover {
  background: var(--text-disabled);
}
/* åŠ è½½åŠ¨ç”» */
.el-loading-mask {
  background-color: rgba(255, 255, 255, 0.9);
  backdrop-filter: blur(4px);
}
/* æ¶ˆæ¯æç¤ºä¼˜åŒ– */
.el-message {
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  border: none;
}
.el-notification {
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xl);
  border: 1px solid var(--border-light);
}
</style>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/bg.png
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/base64ToImg.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
/*
    *dataurl:base64
    *filename:设置文件名称
*/
export const dataURLtoFile=(dataurl,filename) =>{
    let arr = dataurl.split(',')
    let mime = arr[0].match(/:(.*?);/)[1]
    let suffix = mime.split('/')[1]
    let bstr = atob(arr[1])
    let n = bstr.length
    let u8arr = new Uint8Array(n)
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n)
    }
    return new File([u8arr], `${filename}.${suffix}`, {
      type: mime
    })
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/demo.css
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,539 @@
/* Logo å­—体 */
@font-face {
  font-family: "iconfont logo";
  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
}
.logo {
  font-family: "iconfont logo";
  font-size: 160px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
/* tabs */
.nav-tabs {
  position: relative;
}
.nav-tabs .nav-more {
  position: absolute;
  right: 0;
  bottom: 0;
  height: 42px;
  line-height: 42px;
  color: #666;
}
#tabs {
  border-bottom: 1px solid #eee;
}
#tabs li {
  cursor: pointer;
  width: 100px;
  height: 40px;
  line-height: 40px;
  text-align: center;
  font-size: 16px;
  border-bottom: 2px solid transparent;
  position: relative;
  z-index: 1;
  margin-bottom: -1px;
  color: #666;
}
#tabs .active {
  border-bottom-color: #f00;
  color: #222;
}
.tab-container .content {
  display: none;
}
/* é¡µé¢å¸ƒå±€ */
.main {
  padding: 30px 100px;
  width: 960px;
  margin: 0 auto;
}
.main .logo {
  color: #333;
  text-align: left;
  margin-bottom: 30px;
  line-height: 1;
  height: 110px;
  margin-top: -50px;
  overflow: hidden;
  *zoom: 1;
}
.main .logo a {
  font-size: 160px;
  color: #333;
}
.helps {
  margin-top: 40px;
}
.helps pre {
  padding: 20px;
  margin: 10px 0;
  border: solid 1px #e7e1cd;
  background-color: #fffdef;
  overflow: auto;
}
.icon_lists {
  width: 100% !important;
  overflow: hidden;
  *zoom: 1;
}
.icon_lists li {
  width: 100px;
  margin-bottom: 10px;
  margin-right: 20px;
  text-align: center;
  list-style: none !important;
  cursor: default;
}
.icon_lists li .code-name {
  line-height: 1.2;
}
.icon_lists .icon {
  display: block;
  height: 100px;
  line-height: 100px;
  font-size: 42px;
  margin: 10px auto;
  color: #333;
  -webkit-transition: font-size 0.25s linear, width 0.25s linear;
  -moz-transition: font-size 0.25s linear, width 0.25s linear;
  transition: font-size 0.25s linear, width 0.25s linear;
}
.icon_lists .icon:hover {
  font-size: 100px;
}
.icon_lists .svg-icon {
  /* é€šè¿‡è®¾ç½® font-size æ¥æ”¹å˜å›¾æ ‡å¤§å° */
  width: 1em;
  /* å›¾æ ‡å’Œæ–‡å­—相邻时,垂直对齐 */
  vertical-align: -0.15em;
  /* é€šè¿‡è®¾ç½® color æ¥æ”¹å˜ SVG çš„颜色/fill */
  fill: currentColor;
  /* path å’Œ stroke æº¢å‡º viewBox éƒ¨åˆ†åœ¨ IE ä¸‹ä¼šæ˜¾ç¤º
      normalize.css ä¸­ä¹ŸåŒ…含这行 */
  overflow: hidden;
}
.icon_lists li .name,
.icon_lists li .code-name {
  color: #666;
}
/* markdown æ ·å¼ */
.markdown {
  color: #666;
  font-size: 14px;
  line-height: 1.8;
}
.highlight {
  line-height: 1.5;
}
.markdown img {
  vertical-align: middle;
  max-width: 100%;
}
.markdown h1 {
  color: #404040;
  font-weight: 500;
  line-height: 40px;
  margin-bottom: 24px;
}
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
  color: #404040;
  margin: 1.6em 0 0.6em 0;
  font-weight: 500;
  clear: both;
}
.markdown h1 {
  font-size: 28px;
}
.markdown h2 {
  font-size: 22px;
}
.markdown h3 {
  font-size: 16px;
}
.markdown h4 {
  font-size: 14px;
}
.markdown h5 {
  font-size: 12px;
}
.markdown h6 {
  font-size: 12px;
}
.markdown hr {
  height: 1px;
  border: 0;
  background: #e9e9e9;
  margin: 16px 0;
  clear: both;
}
.markdown p {
  margin: 1em 0;
}
.markdown>p,
.markdown>blockquote,
.markdown>.highlight,
.markdown>ol,
.markdown>ul {
  width: 80%;
}
.markdown ul>li {
  list-style: circle;
}
.markdown>ul li,
.markdown blockquote ul>li {
  margin-left: 20px;
  padding-left: 4px;
}
.markdown>ul li p,
.markdown>ol li p {
  margin: 0.6em 0;
}
.markdown ol>li {
  list-style: decimal;
}
.markdown>ol li,
.markdown blockquote ol>li {
  margin-left: 20px;
  padding-left: 4px;
}
.markdown code {
  margin: 0 3px;
  padding: 0 5px;
  background: #eee;
  border-radius: 3px;
}
.markdown strong,
.markdown b {
  font-weight: 600;
}
.markdown>table {
  border-collapse: collapse;
  border-spacing: 0px;
  empty-cells: show;
  border: 1px solid #e9e9e9;
  width: 95%;
  margin-bottom: 24px;
}
.markdown>table th {
  white-space: nowrap;
  color: #333;
  font-weight: 600;
}
.markdown>table th,
.markdown>table td {
  border: 1px solid #e9e9e9;
  padding: 8px 16px;
  text-align: left;
}
.markdown>table th {
  background: #F7F7F7;
}
.markdown blockquote {
  font-size: 90%;
  color: #999;
  border-left: 4px solid #e9e9e9;
  padding-left: 0.8em;
  margin: 1em 0;
}
.markdown blockquote p {
  margin: 0;
}
.markdown .anchor {
  opacity: 0;
  transition: opacity 0.3s ease;
  margin-left: 8px;
}
.markdown .waiting {
  color: #ccc;
}
.markdown h1:hover .anchor,
.markdown h2:hover .anchor,
.markdown h3:hover .anchor,
.markdown h4:hover .anchor,
.markdown h5:hover .anchor,
.markdown h6:hover .anchor {
  opacity: 1;
  display: inline-block;
}
.markdown>br,
.markdown>p>br {
  clear: both;
}
.hljs {
  display: block;
  background: white;
  padding: 0.5em;
  color: #333333;
  overflow-x: auto;
}
.hljs-comment,
.hljs-meta {
  color: #969896;
}
.hljs-string,
.hljs-variable,
.hljs-template-variable,
.hljs-strong,
.hljs-emphasis,
.hljs-quote {
  color: #df5000;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-type {
  color: #a71d5d;
}
.hljs-literal,
.hljs-symbol,
.hljs-bullet,
.hljs-attribute {
  color: #0086b3;
}
.hljs-section,
.hljs-name {
  color: #63a35c;
}
.hljs-tag {
  color: #333333;
}
.hljs-title,
.hljs-attr,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
  color: #795da3;
}
.hljs-addition {
  color: #55a532;
  background-color: #eaffea;
}
.hljs-deletion {
  color: #bd2c00;
  background-color: #ffecec;
}
.hljs-link {
  text-decoration: underline;
}
/* ä»£ç é«˜äº® */
/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
/**
 * prism.js default theme for JavaScript, CSS and HTML
 * Based on dabblet (http://dabblet.com)
 * @author Lea Verou
 */
code[class*="language-"],
pre[class*="language-"] {
  color: black;
  background: none;
  text-shadow: 0 1px white;
  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
  text-align: left;
  white-space: pre;
  word-spacing: normal;
  word-break: normal;
  word-wrap: normal;
  line-height: 1.5;
  -moz-tab-size: 4;
  -o-tab-size: 4;
  tab-size: 4;
  -webkit-hyphens: none;
  -moz-hyphens: none;
  -ms-hyphens: none;
  hyphens: none;
}
pre[class*="language-"]::-moz-selection,
pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection,
code[class*="language-"] ::-moz-selection {
  text-shadow: none;
  background: #b3d4fc;
}
pre[class*="language-"]::selection,
pre[class*="language-"] ::selection,
code[class*="language-"]::selection,
code[class*="language-"] ::selection {
  text-shadow: none;
  background: #b3d4fc;
}
@media print {
  code[class*="language-"],
  pre[class*="language-"] {
    text-shadow: none;
  }
}
/* Code blocks */
pre[class*="language-"] {
  padding: 1em;
  margin: .5em 0;
  overflow: auto;
}
:not(pre)>code[class*="language-"],
pre[class*="language-"] {
  background: #f5f2f0;
}
/* Inline code */
:not(pre)>code[class*="language-"] {
  padding: .1em;
  border-radius: .3em;
  white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
  color: slategray;
}
.token.punctuation {
  color: #999;
}
.namespace {
  opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
  color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
  color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
  color: #9a6e3a;
  background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
  color: #07a;
}
.token.function,
.token.class-name {
  color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
  color: #e90;
}
.token.important,
.token.bold {
  font-weight: bold;
}
.token.italic {
  font-style: italic;
}
.token.entity {
  cursor: help;
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/demo_index.html
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,354 @@
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <title>IconFont Demo</title>
  <link rel="shortcut icon" href="https://img.alicdn.com/tps/i4/TB1_oz6GVXXXXaFXpXXJDFnIXXX-64-64.ico" type="image/x-icon"/>
  <link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
  <link rel="stylesheet" href="demo.css">
  <link rel="stylesheet" href="iconfont.css">
  <script src="iconfont.js"></script>
  <!-- jQuery -->
  <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
  <!-- ä»£ç é«˜äº® -->
  <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
</head>
<body>
  <div class="main">
    <h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont é¦–页" target="_blank">&#xe86b;</a></h1>
    <div class="nav-tabs">
      <ul id="tabs" class="dib-box">
        <li class="dib active"><span>Unicode</span></li>
        <li class="dib"><span>Font class</span></li>
        <li class="dib"><span>Symbol</span></li>
      </ul>
      <a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=2399544" target="_blank" class="nav-more">查看项目</a>
    </div>
    <div class="tab-container">
      <div class="content unicode" style="display: block;">
          <ul class="icon_lists dib-box">
            <li class="dib">
              <span class="icon iconfont">&#xe7ff;</span>
                <div class="name">用户</div>
                <div class="code-name">&amp;#xe7ff;</div>
              </li>
            <li class="dib">
              <span class="icon iconfont">&#xe616;</span>
                <div class="name">关闭</div>
                <div class="code-name">&amp;#xe616;</div>
              </li>
            <li class="dib">
              <span class="icon iconfont">&#xe78d;</span>
                <div class="name">人脸识别</div>
                <div class="code-name">&amp;#xe78d;</div>
              </li>
            <li class="dib">
              <span class="icon iconfont">&#xe60d;</span>
                <div class="name">退出</div>
                <div class="code-name">&amp;#xe60d;</div>
              </li>
            <li class="dib">
              <span class="icon iconfont">&#xeb15;</span>
                <div class="name">消息</div>
                <div class="code-name">&amp;#xeb15;</div>
              </li>
            <li class="dib">
              <span class="icon iconfont">&#xe612;</span>
                <div class="name">消息</div>
                <div class="code-name">&amp;#xe612;</div>
              </li>
            <li class="dib">
              <span class="icon iconfont">&#xe644;</span>
                <div class="name">密码</div>
                <div class="code-name">&amp;#xe644;</div>
              </li>
            <li class="dib">
              <span class="icon iconfont">&#x666;</span>
                <div class="name">账号</div>
                <div class="code-name">&amp;#x666;</div>
              </li>
          </ul>
          <div class="article markdown">
          <h2 id="unicode-">Unicode å¼•用</h2>
          <hr>
          <p>Unicode æ˜¯å­—体在网页端最原始的应用方式,特点是:</p>
          <ul>
            <li>兼容性最好,支持 IE6+,及所有现代浏览器。</li>
            <li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
            <li>但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。</li>
          </ul>
          <blockquote>
            <p>注意:新版 iconfont æ”¯æŒå¤šè‰²å›¾æ ‡ï¼Œè¿™äº›å¤šè‰²å›¾æ ‡åœ¨ Unicode æ¨¡å¼ä¸‹å°†ä¸èƒ½ä½¿ç”¨ï¼Œå¦‚果有需求建议使用symbol çš„引用方式</p>
          </blockquote>
          <p>Unicode ä½¿ç”¨æ­¥éª¤å¦‚下:</p>
          <h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
<pre><code class="language-css"
>@font-face {
  font-family: 'iconfont';
  src: url('iconfont.eot');
  src: url('iconfont.eot?#iefix') format('embedded-opentype'),
      url('iconfont.woff2') format('woff2'),
      url('iconfont.woff') format('woff'),
      url('iconfont.ttf') format('truetype'),
      url('iconfont.svg#iconfont') format('svg');
}
</code></pre>
          <h3 id="-iconfont-">第二步:定义使用 iconfont çš„æ ·å¼</h3>
<pre><code class="language-css"
>.iconfont {
  font-family: "iconfont" !important;
  font-size: 16px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
</code></pre>
          <h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
<pre>
<code class="language-html"
>&lt;span class="iconfont"&gt;&amp;#x33;&lt;/span&gt;
</code></pre>
          <blockquote>
            <p>"iconfont" æ˜¯ä½ é¡¹ç›®ä¸‹çš„ font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
          </blockquote>
          </div>
      </div>
      <div class="content font-class">
        <ul class="icon_lists dib-box">
          <li class="dib">
            <span class="icon iconfont icon-dashujukeshihuaico-"></span>
            <div class="name">
              ç”¨æˆ·
            </div>
            <div class="code-name">.icon-dashujukeshihuaico-
            </div>
          </li>
          <li class="dib">
            <span class="icon iconfont icon-guanxi"></span>
            <div class="name">
              å…³é—­
            </div>
            <div class="code-name">.icon-guanxi
            </div>
          </li>
          <li class="dib">
            <span class="icon iconfont icon-renlianshibie1"></span>
            <div class="name">
              äººè„¸è¯†åˆ«
            </div>
            <div class="code-name">.icon-renlianshibie1
            </div>
          </li>
          <li class="dib">
            <span class="icon iconfont icon-tuichu"></span>
            <div class="name">
              é€€å‡º
            </div>
            <div class="code-name">.icon-tuichu
            </div>
          </li>
          <li class="dib">
            <span class="icon iconfont icon-biaoqiankuozhan_xiaoxi-157"></span>
            <div class="name">
              æ¶ˆæ¯
            </div>
            <div class="code-name">.icon-biaoqiankuozhan_xiaoxi-157
            </div>
          </li>
          <li class="dib">
            <span class="icon iconfont icon-xiaoxi"></span>
            <div class="name">
              æ¶ˆæ¯
            </div>
            <div class="code-name">.icon-xiaoxi
            </div>
          </li>
          <li class="dib">
            <span class="icon iconfont icon-mima"></span>
            <div class="name">
              å¯†ç 
            </div>
            <div class="code-name">.icon-mima
            </div>
          </li>
          <li class="dib">
            <span class="icon iconfont icon-zhanghao"></span>
            <div class="name">
              è´¦å·
            </div>
            <div class="code-name">.icon-zhanghao
            </div>
          </li>
        </ul>
        <div class="article markdown">
        <h2 id="font-class-">font-class å¼•用</h2>
        <hr>
        <p>font-class æ˜¯ Unicode ä½¿ç”¨æ–¹å¼çš„一种变种,主要是解决 Unicode ä¹¦å†™ä¸ç›´è§‚,语意不明确的问题。</p>
        <p>与 Unicode ä½¿ç”¨æ–¹å¼ç›¸æ¯”,具有如下特点:</p>
        <ul>
          <li>兼容性良好,支持 IE8+,及所有现代浏览器。</li>
          <li>相比于 Unicode è¯­æ„æ˜Žç¡®ï¼Œä¹¦å†™æ›´ç›´è§‚。可以很容易分辨这个 icon æ˜¯ä»€ä¹ˆã€‚</li>
          <li>因为使用 class æ¥å®šä¹‰å›¾æ ‡ï¼Œæ‰€ä»¥å½“要替换图标时,只需要修改 class é‡Œé¢çš„ Unicode å¼•用。</li>
          <li>不过因为本质上还是使用的字体,所以多色图标还是不支持的。</li>
        </ul>
        <p>使用步骤如下:</p>
        <h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass ä»£ç ï¼š</h3>
<pre><code class="language-html">&lt;link rel="stylesheet" href="./iconfont.css"&gt;
</code></pre>
        <h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
<pre><code class="language-html">&lt;span class="iconfont icon-xxx"&gt;&lt;/span&gt;
</code></pre>
        <blockquote>
          <p>"
            iconfont" æ˜¯ä½ é¡¹ç›®ä¸‹çš„ font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
        </blockquote>
      </div>
      </div>
      <div class="content symbol">
          <ul class="icon_lists dib-box">
            <li class="dib">
                <svg class="icon svg-icon" aria-hidden="true">
                  <use xlink:href="#icon-dashujukeshihuaico-"></use>
                </svg>
                <div class="name">用户</div>
                <div class="code-name">#icon-dashujukeshihuaico-</div>
            </li>
            <li class="dib">
                <svg class="icon svg-icon" aria-hidden="true">
                  <use xlink:href="#icon-guanxi"></use>
                </svg>
                <div class="name">关闭</div>
                <div class="code-name">#icon-guanxi</div>
            </li>
            <li class="dib">
                <svg class="icon svg-icon" aria-hidden="true">
                  <use xlink:href="#icon-renlianshibie1"></use>
                </svg>
                <div class="name">人脸识别</div>
                <div class="code-name">#icon-renlianshibie1</div>
            </li>
            <li class="dib">
                <svg class="icon svg-icon" aria-hidden="true">
                  <use xlink:href="#icon-tuichu"></use>
                </svg>
                <div class="name">退出</div>
                <div class="code-name">#icon-tuichu</div>
            </li>
            <li class="dib">
                <svg class="icon svg-icon" aria-hidden="true">
                  <use xlink:href="#icon-biaoqiankuozhan_xiaoxi-157"></use>
                </svg>
                <div class="name">消息</div>
                <div class="code-name">#icon-biaoqiankuozhan_xiaoxi-157</div>
            </li>
            <li class="dib">
                <svg class="icon svg-icon" aria-hidden="true">
                  <use xlink:href="#icon-xiaoxi"></use>
                </svg>
                <div class="name">消息</div>
                <div class="code-name">#icon-xiaoxi</div>
            </li>
            <li class="dib">
                <svg class="icon svg-icon" aria-hidden="true">
                  <use xlink:href="#icon-mima"></use>
                </svg>
                <div class="name">密码</div>
                <div class="code-name">#icon-mima</div>
            </li>
            <li class="dib">
                <svg class="icon svg-icon" aria-hidden="true">
                  <use xlink:href="#icon-zhanghao"></use>
                </svg>
                <div class="name">账号</div>
                <div class="code-name">#icon-zhanghao</div>
            </li>
          </ul>
          <div class="article markdown">
          <h2 id="symbol-">Symbol å¼•用</h2>
          <hr>
          <p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
            è¿™ç§ç”¨æ³•其实是做了一个 SVG çš„集合,与另外两种相比具有如下特点:</p>
          <ul>
            <li>支持多色图标了,不再受单色限制。</li>
            <li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> æ¥è°ƒæ•´æ ·å¼ã€‚</li>
            <li>兼容性较差,支持 IE9+,及现代浏览器。</li>
            <li>浏览器渲染 SVG çš„æ€§èƒ½ä¸€èˆ¬ï¼Œè¿˜ä¸å¦‚ png。</li>
          </ul>
          <p>使用步骤如下:</p>
          <h3 id="-symbol-">第一步:引入项目下面生成的 symbol ä»£ç ï¼š</h3>
<pre><code class="language-html">&lt;script src="./iconfont.js"&gt;&lt;/script&gt;
</code></pre>
          <h3 id="-css-">第二步:加入通用 CSS ä»£ç ï¼ˆå¼•入一次就行):</h3>
<pre><code class="language-html">&lt;style&gt;
.icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
&lt;/style&gt;
</code></pre>
          <h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
<pre><code class="language-html">&lt;svg class="icon" aria-hidden="true"&gt;
  &lt;use xlink:href="#icon-xxx"&gt;&lt;/use&gt;
&lt;/svg&gt;
</code></pre>
          </div>
      </div>
    </div>
  </div>
  <script>
  $(document).ready(function () {
      $('.tab-container .content:first').show()
      $('#tabs li').click(function (e) {
        var tabContent = $('.tab-container .content')
        var index = $(this).index()
        if ($(this).hasClass('active')) {
          return
        } else {
          $('#tabs li').removeClass('active')
          $(this).addClass('active')
          tabContent.hide().eq(index).fadeIn()
        }
      })
    })
  </script>
</body>
</html>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/iconfont.css
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
@font-face {font-family: "iconfont";
  src: url('iconfont.eot?t=1614833736726'); /* IE9 */
  src: url('iconfont.eot?t=1614833736726#iefix') format('embedded-opentype'), /* IE6-IE8 */
  url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAewAAsAAAAADuQAAAdgAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCECgqPRI0IATYCJAMkCxQABCAFhG0HgRgbzAwRFaxTIPvqgDebctK2YsIVXhR2hagG9WP1vtcZPG76914gQNIUVpLKTAyZOFZzOhEJm8PmyGCm0JkIsi+uBAC4MZCJtxogwBL4pk2TlOD2ThqWEK6lHdeaZFt+0NcvlxIggOz/1lq9c+q9DQX7eNsQqWVmd0/mxHwQtUTGQyYkUW+UfITIo3ZTqc1LwWeI629cDQQAh7paI0bzcBvUUEDTE3SYOK6qFOqYA4oSFYHazOYcqQDZCCXUdA19AmCD+/nkG3RMDVAoGWjXGu00VaA/6/xGYuSkrALGBbk/HwBxPYAB0BqgEE2hXKsXMJA3w4DrrsBYAGl09DUp6/xc+Dzt8/TPjV+s+yL5jZRMpkfS5iJI0w3Yh1oWFKrRiPL3z2OhGVJIPrVODbBOkwudnwuwQQFjGmxgYUyHDRRGI6pABXyxzgYGxiSqgADfSL2gBABo7CPSAI2AbgSYfGgW4xwslBMxOj11FdMzRVEQREm3ZggoaifUy9Kci63fNEXfMGVGIuGIxx3RqC0as9ukRmT8SZIrd36cSMSH+tOzdBPMxsGxmJ3Pfix3rnMsNjl0ranflXHoeq2ARwpebeKTxf1XMr3ueSHieZ7HfHVzo1Pc6U5guVmlT/aWuct8VQFP/kUrvALn9hPiirgPAzteduE91a7IlEhU9nKc21wO8/z51/UKTUWxjT0+QA57qgnZ+WquE3AF+BaU4868qJ19vmmBnZfOZPpe2OKvMvznGnu3S76YfeHUU12LhMaozUQwnN3IEIEzXOYIyWip2xHX14snEtmBQweCwa3ipnghx7Q8T8JGXAXUH6o+JCVw5rmiXLE/QphAtScsUflwuTvdH+og5k/n3O/mrmpe6wmDunEpbtHmLXKHe/H8gTAg+3yejOxAM8H+Zhk/l3Pwmuu656p8xeH2yj5PwOW3pdafsv2F+1XdYtc19xXg1LrBF23sPRwMD9it2nUw/WA8gxygb0YKWQc+Ta4ByUis546+iALc/sOgW0mEFYsYmiOLKX4Px3uiZP/hTF9QQuWehAS2ZYQiC07dj+DlBLk8W8mxHl7xQbT9CfD93pF3OfclBt2NNdgdNd6Ll80f2tiwTjc6bdT8MgsssK36UbG1jeuGNA40ufQV2T6Dbnd+xNYmFaxXV3LkYrV9j5jGM0v7Td75849jML9UP1oUrT3XxoYX3ua8v5fBYJr0dFPD82/z3ry6eV8H7m0cxyzrN2nDz7oTuwy1Rk5v1G2rc41FsKypDDYRpLA9GJ4r6DgsCR0+fHIpVF9Vf9tFqqIXt/l5oUvBrvgvB1MTGok0YBtYe1AVIk9V9hHDTdqI1uS0sGsvlfCqwDvClgqR8XATWk1zWePXO5s59X4h8sL3TYyKsSF5qhcRwWVZ8hpWed7OA1rUa7K+34aNjMbwfmFi9LU/U+5SsX91U7LK8iYoGbLZz3bqnUyEcTZt/sUS0Sm2FFtymO6PvSmSS0qyG9sb9R8wZMigmzVeVp6y9VnRqdsJpWv58G6trW0cZaN7bZw2bVOvPRt6OU16bxxd1saRtdbwrheHZ92+b+9Teao6avJOQ4YOHHjEXMzhQ5v2fKqhXqfU81ob1uj5/bEvtb2UnfrvlZ5IZ01TyF1LZBouHffohyqN5qwR29v/iVRhrfrKDq5xb+tEtfosmq9tesLWw9bdzFmtPP596f3076Xvn+anMUYGrvORmLSSLbWUsrv5rA7dO2RxBil/DkZZWU3VnBc6tU5QCPVrtrvh5MayVozMmW3OwB8f7jAQc2Gh2dIRAeFe3BXxiobmzp6dS1sNr3el3rBWhvb9L0sO6Yr098z3cEiPtasU/f/qr3AolX8Zb6UY0D5IU874crnH+sdczoE7Ag2i049tLyp6+Puna9rbOxPLpMkW0hEBoWmXDvbgp7GH+V23Hze3pYsOHVoEHeUQurY1f3cw1Wb96CPTmap174clMgAknzJXmRIAoNfoSCAZcZxxsM1lf8S4AICZRw8yph19zrSif4Zy6JvHz1Y4/n+XN8Xa3r/t/0ta8a5zzWBKJOVEPwE1aBEhH/5hI9ScovhoNKdG8Rq7ybco0mAf1JQC4PRZwCMmkub9fk1SbeGZZpJCoFChDjBQo7FYIW8NSvDoDCzU6AccWhl0PA+96WBAFBoALa1gQKBzHChScR0Y6DxFQJC/B0qk+w5Y6GKAg4Poz8hD2jS6vM+oBR2YPyDLrTfccul1fMFQap2jGjf/A3NPaZhPZsmGZ2wx97FP/x0WIh585gae2PWwrhm6zAmtTKJId5hOfd6+E8vN4JIhoxa7vAPmj02WWz+3XKZy/4Kh1DqXjPhmf2DuafVgbmJWQ/xsaGuNOJfu/XdYEKIeeLE9N+CJfFjrtQy6/M0SWpmILWh3MKWZfF3DZHxTc60Nunyjane8ApMUzbAcT/p1H0sqFTo68og+kH3Hp6Ho9pduM7Y16TZzf0M4P5VCNpY7Q5q/xO5V4f+o28/fODLrxvP19jRocdxQo8/zdYH8PBgAAAAA') format('woff2'),
  url('iconfont.woff?t=1614833736726') format('woff'),
  url('iconfont.ttf?t=1614833736726') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
  url('iconfont.svg?t=1614833736726#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
  font-family: "iconfont" !important;
  font-size: 16px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
.icon-dashujukeshihuaico-:before {
  content: "\e7ff";
}
.icon-guanxi:before {
  content: "\e616";
}
.icon-renlianshibie1:before {
  content: "\e78d";
}
.icon-tuichu:before {
  content: "\e60d";
}
.icon-biaoqiankuozhan_xiaoxi-157:before {
  content: "\eb15";
}
.icon-xiaoxi:before {
  content: "\e612";
}
.icon-mima:before {
  content: "\e644";
}
.icon-zhanghao:before {
  content: "\666";
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/iconfont.eot
Binary files differ
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/iconfont.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
!function(c){var t,e,a,i,f,l,o='<svg><symbol id="icon-dashujukeshihuaico-" viewBox="0 0 1024 1024"><path d="M611.413333 516.693333a231.253333 231.253333 0 1 0-199.253333 0 441.173333 441.173333 0 0 0-341.333333 429.653334h882.773333a441.173333 441.173333 0 0 0-342.186667-429.653334z" fill="#ffffff" ></path><path d="M611.413333 516.693333a231.253333 231.253333 0 1 0-199.253333 0 441.173333 441.173333 0 0 0-341.333333 429.653334h882.773333a441.173333 441.173333 0 0 0-342.186667-429.653334z" fill="#ffffff" ></path><path d="M611.413333 516.693333a231.253333 231.253333 0 1 0-199.253333 0 441.173333 441.173333 0 0 0-341.333333 429.653334h882.773333a441.173333 441.173333 0 0 0-342.186667-429.653334z" fill="#ffffff" ></path><path d="M953.6 946.346667a441.173333 441.173333 0 0 0-341.333333-429.653334 229.546667 229.546667 0 0 0 11.093333-409.173333 228.693333 228.693333 0 0 0-32-3.413333 230.4 230.4 0 0 0-99.413333 438.613333 440.32 440.32 0 0 0-341.333334 403.2z" fill="#ffffff" ></path><path d="M653.653333 334.933333a230.4 230.4 0 0 1-131.413333 207.786667 441.173333 441.173333 0 0 1 341.333333 403.2h90.453334a441.173333 441.173333 0 0 0-341.333334-429.653333 229.546667 229.546667 0 0 0 11.093334-409.173334 228.266667 228.266667 0 0 0-32-3.413333 229.973333 229.973333 0 0 0-85.333334 15.786667 230.826667 230.826667 0 0 1 147.2 215.466666z" fill="#ffffff" ></path><path d="M104.96 206.08A135.68 135.68 0 0 0 31.146667 131.84a9.813333 9.813333 0 0 1 0-18.346667A135.68 135.68 0 0 0 104.96 39.253333a9.813333 9.813333 0 0 1 18.346667 0 135.68 135.68 0 0 0 74.24 74.24 9.813333 9.813333 0 0 1 0 18.346667 135.68 135.68 0 0 0-74.24 74.24 9.813333 9.813333 0 0 1-18.346667 0z" fill="#ffffff" ></path><path d="M905.813333 416a69.546667 69.546667 0 0 0-37.973333-37.973333 5.12 5.12 0 0 1 0-9.386667 69.546667 69.546667 0 0 0 37.973333-37.973333 5.12 5.12 0 0 1 9.386667 0 69.546667 69.546667 0 0 0 37.973333 37.973333 5.12 5.12 0 0 1 0 9.386667 69.546667 69.546667 0 0 0-37.973333 37.973333 5.12 5.12 0 0 1-9.386667 0z" fill="#ffffff" ></path><path d="M42.666667 672a69.546667 69.546667 0 0 0-37.973334-37.973333 5.12 5.12 0 0 1 0-9.386667 69.546667 69.546667 0 0 0 37.973334-38.4 5.12 5.12 0 0 1 9.386666 0 69.546667 69.546667 0 0 0 37.973334 37.973333 5.12 5.12 0 0 1 0 9.386667 69.546667 69.546667 0 0 0-37.973334 37.973333 5.12 5.12 0 0 1-9.386666 0.426667z" fill="#ffffff" ></path></symbol><symbol id="icon-guanxi" viewBox="0 0 1024 1024"><path d="M683.008 689.664c-4.608 0-9.216-2.048-13.312-5.632L336.384 350.72c-7.168-7.168-7.168-18.944 0-26.112 7.168-7.168 18.944-7.168 26.112 0l333.824 333.824c7.168 7.168 7.168 18.944 0 26.112-3.584 3.584-8.704 5.12-13.312 5.12z" fill="#686464" ></path><path d="M337.408 677.888c-4.608 0-9.216-2.048-13.312-5.632-7.168-7.168-7.168-18.944 0-26.112L657.92 312.32c7.168-7.168 18.944-7.168 26.112 0 7.168 7.168 7.168 18.944 0 26.112l-333.824 333.824c-3.584 4.096-8.192 5.632-12.8 5.632z" fill="#686464" ></path></symbol><symbol id="icon-renlianshibie1" viewBox="0 0 1024 1024"><path d="M298.666667 896H149.333333c-12.8 0-21.333333-8.533333-21.333333-21.333333V725.333333c0-25.6-17.066667-42.666667-42.666667-42.666666s-42.666667 17.066667-42.666666 42.666666v149.333334C42.666667 934.4 89.6 981.333333 149.333333 981.333333H298.666667c25.6 0 42.666667-17.066667 42.666666-42.666666s-17.066667-42.666667-42.666666-42.666667zM938.666667 682.666667c-25.6 0-42.666667 17.066667-42.666667 42.666666v149.333334c0 12.8-8.533333 21.333333-21.333333 21.333333H725.333333c-25.6 0-42.666667 17.066667-42.666666 42.666667s17.066667 42.666667 42.666666 42.666666h149.333334c59.733333 0 106.666667-46.933333 106.666666-106.666666V725.333333c0-25.6-17.066667-42.666667-42.666666-42.666666zM874.666667 42.666667H725.333333c-25.6 0-42.666667 17.066667-42.666666 42.666666s17.066667 42.666667 42.666666 42.666667h149.333334c12.8 0 21.333333 8.533333 21.333333 21.333333V298.666667c0 25.6 17.066667 42.666667 42.666667 42.666666s42.666667-17.066667 42.666666-42.666666V149.333333C981.333333 89.6 934.4 42.666667 874.666667 42.666667zM85.333333 341.333333c25.6 0 42.666667-17.066667 42.666667-42.666666V149.333333c0-12.8 8.533333-21.333333 21.333333-21.333333H298.666667c25.6 0 42.666667-17.066667 42.666666-42.666667s-17.066667-42.666667-42.666666-42.666666H149.333333C89.6 42.666667 42.666667 89.6 42.666667 149.333333V298.666667c0 25.6 17.066667 42.666667 42.666666 42.666666zM721.066667 661.333333c12.8-21.333333 4.266667-46.933333-17.066667-59.733333-21.333333-12.8-46.933333-4.266667-59.733333 17.066667 0 0-38.4 64-132.266667 64s-132.266667-59.733333-132.266667-64c-12.8-21.333333-38.4-25.6-59.733333-17.066667-21.333333 12.8-25.6 38.4-17.066667 59.733333 4.266667 4.266667 64 106.666667 209.066667 106.666667s204.8-102.4 209.066667-106.666667zM512 512c25.6 0 42.666667-17.066667 42.666667-42.666667V298.666667c0-25.6-17.066667-42.666667-42.666667-42.666667s-42.666667 17.066667-42.666667 42.666667v170.666666c0 25.6 17.066667 42.666667 42.666667 42.666667zM725.333333 384c25.6 0 42.666667-17.066667 42.666667-42.666667V298.666667c0-25.6-17.066667-42.666667-42.666667-42.666667s-42.666667 17.066667-42.666666 42.666667v42.666666c0 25.6 17.066667 42.666667 42.666666 42.666667zM298.666667 256c-25.6 0-42.666667 17.066667-42.666667 42.666667v42.666666c0 25.6 17.066667 42.666667 42.666667 42.666667s42.666667-17.066667 42.666666-42.666667V298.666667c0-25.6-17.066667-42.666667-42.666666-42.666667z" fill="#dfe5f4" ></path></symbol><symbol id="icon-tuichu" viewBox="0 0 1024 1024"><path d="M511.924148 1023.924148a470.660741 470.660741 0 0 1-184.395852-37.167407 471.115852 471.115852 0 0 1-150.679703-101.376A471.87437 471.87437 0 0 1 236.468148 166.381037a37.091556 37.091556 0 1 1 43.159704 59.998815 404.745481 404.745481 0 0 0-121.742222 139.339852 392.305778 392.305778 0 0 0-45.700741 185.078518c0 106.571852 41.604741 206.810074 117.077333 282.206815a397.349926 397.349926 0 0 0 282.661926 117.001482c106.68563 0 207.151407-41.528889 282.737778-117.001482a396.09837 396.09837 0 0 0 117.039407-282.244741c0.227556-64.474074-15.435852-128.037926-45.662814-185.040592a404.366222 404.366222 0 0 0-121.742223-139.188148 36.939852 36.939852 0 1 1 43.159704-60.074667 471.798519 471.798519 0 0 1 59.58163 719.075555A472.936296 472.936296 0 0 1 511.924148 1024v-0.075852z m11.150222-513.403259a37.015704 37.015704 0 0 1-37.091555-36.939852V35.384889a37.129481 37.129481 0 0 1 74.221037 0v438.196148a36.939852 36.939852 0 0 1-37.167408 36.939852z" fill="#ffffff" ></path></symbol><symbol id="icon-biaoqiankuozhan_xiaoxi-157" viewBox="0 0 1024 1024"><path d="M393.7792 876.1344c-7.3728 0-14.848-1.6384-21.8624-5.0176-17.7664-8.4992-28.8256-26.0096-28.7744-45.6704l0.1536-59.4944c0-3.4304-1.7408-5.7344-2.7648-6.7584-1.024-1.024-3.328-2.816-6.7584-2.816H172.1344c-27.8528 0-50.4832-22.6304-50.4832-50.4832v-436.736c0-27.8528 22.6304-50.4832 50.4832-50.4832h665.6c27.8528 0 50.4832 22.6304 50.4832 50.4832v436.736c0 27.8528-22.6304 50.4832-50.4832 50.4832h-275.712c-2.2016 0-4.3008 0.768-6.0416 2.1504L425.472 864.7168a49.9968 49.9968 0 0 1-31.6928 11.4176zM172.1344 259.584c-5.2736 0-9.5232 4.3008-9.5232 9.5232v436.736c0 5.2736 4.2496 9.5232 9.5232 9.5232h161.5872c13.5168 0 26.2144 5.2736 35.7376 14.848a50.176 50.176 0 0 1 14.7456 35.7888l-0.1536 59.4944c0 5.4272 3.7888 7.8336 5.4272 8.6528 1.6384 0.768 5.888 2.2528 10.1376-1.2288l130.5088-106.1888a50.43712 50.43712 0 0 1 31.8464-11.3152h275.712c5.2736 0 9.5232-4.2496 9.5232-9.5232v-436.736c0-5.2736-4.3008-9.5232-9.5232-9.5232H172.1344z m525.056 295.4752c-36.7104 0-66.56-29.8496-66.56-66.56s29.8496-66.56 66.56-66.56 66.56 29.8496 66.56 66.56-29.8496 66.56-66.56 66.56z m0-92.16c-14.1312 0-25.6 11.4688-25.6 25.6s11.4688 25.6 25.6 25.6 25.6-11.4688 25.6-25.6-11.52-25.6-25.6-25.6z m-195.0208 92.16c-36.7104 0-66.56-29.8496-66.56-66.56s29.8496-66.56 66.56-66.56 66.56 29.8496 66.56 66.56-29.8496 66.56-66.56 66.56z m0-92.16c-14.1312 0-25.6 11.4688-25.6 25.6s11.4688 25.6 25.6 25.6 25.6-11.4688 25.6-25.6-11.4688-25.6-25.6-25.6z m-194.9696 92.16c-36.7104 0-66.56-29.8496-66.56-66.56s29.8496-66.56 66.56-66.56 66.56 29.8496 66.56 66.56-29.9008 66.56-66.56 66.56z m0-92.16c-14.1312 0-25.6 11.4688-25.6 25.6s11.4688 25.6 25.6 25.6 25.6-11.4688 25.6-25.6-11.52-25.6-25.6-25.6z" fill="#ffffff" ></path></symbol><symbol id="icon-xiaoxi" viewBox="0 0 1024 1024"><path d="M621.6 761.4c0 50.5-43 89.8-96.6 89.8s-96.6-39.3-96.6-89.8h193.2m-32.2-523.9c0-33.7-27.9-59.9-64.4-59.9s-64.4 26.2-64.4 59.9v9.3c-88 28.1-161 117.8-161 222.7v82.3s0 117.8-32.2 119.7c-19.3 0-32.2 13.1-32.2 29.9 0 16.9 15 29.9 32.2 29.9h515.3c17.2 0 32.2-13.1 32.2-29.9 0-16.9-15-29.9-32.2-29.9-32.2 0-32.2-119.7-32.2-119.7v-82.3c0-104.7-68.7-192.7-161-222.7v-9.3" fill="#ffffff" ></path></symbol><symbol id="icon-mima" viewBox="0 0 1024 1024"><path d="M760.171097 415.494703 346.927843 415.494703l0-67.441135c0-91.90006 74.809514-166.709574 166.811913-166.709574 92.002399 0 166.811913 74.911853 166.811913 166.914251 0 12.485309 10.029182 22.514491 22.514491 22.514491s22.514491-10.029182 22.514491-22.514491c0-116.870678-95.072556-211.943234-211.840895-211.943234-56.593244 0-109.809314 22.002798-149.823706 62.01719-40.014391 40.014391-62.01719 93.128123-62.01719 149.721367L301.898861 415.494703l-47.485109 0c-27.631421 0-49.736558 22.105137-49.736558 49.634219l0 341.913252c0 27.631421 22.207476 50.452928 49.736558 50.452928l505.655007 0c27.631421 0 50.35059-22.821507 50.35059-50.452928L810.419348 465.128923C810.521687 437.59984 787.802518 415.494703 760.171097 415.494703zM765.492704 807.553868c0 2.763142-2.251449 5.014591-5.014591 5.014591L254.720768 812.568459c-2.763142 0-5.014591-2.251449-5.014591-5.014591L249.706176 465.538277c0-2.763142 2.251449-5.014591 5.014591-5.014591l505.655007 0c2.763142 0 5.014591 2.251449 5.014591 5.014591L765.390366 807.553868zM503.505896 532.160704c-25.482311 0-46.052369 20.570058-46.052369 46.052369 0 16.78353 9.005797 31.520288 22.514491 39.605037L479.968019 707.159704c0 12.38297 10.131521 22.514491 22.514491 22.514491s22.514491-10.131521 22.514491-22.514491l0-88.21587c14.634419-7.675395 24.561263-23.026184 24.561263-40.730762C549.558265 552.730762 528.988207 532.160704 503.505896 532.160704z" fill="#666666" ></path></symbol><symbol id="icon-zhanghao" viewBox="0 0 1024 1024"><path d="M828.1 722.4c-17.3-40.9-42-77.5-73.5-109s-68.2-56.2-109.1-73.5c-10.2-4.3-20.6-8.1-31-11.4 54.5-34.7 90.8-95.7 90.8-164.9 0-107.7-87.6-195.4-195.4-195.4-107.7 0-195.4 87.6-195.4 195.4 0 69.9 36.9 131.3 92.2 165.8-9.6 3.1-19 6.6-28.3 10.5-40.9 17.3-77.5 42-109 73.5s-56.2 68.2-73.5 109c-17.9 42.3-27 87.2-27 133.6h46c0-163.8 133.3-297.1 297.1-297.1S809.1 692.2 809.1 856h46c0-46.4-9.1-91.3-27-133.6zM360.6 363.5c0-82.4 67-149.4 149.4-149.4s149.4 67 149.4 149.4-67 149.4-149.4 149.4c-82.4-0.1-149.4-67.1-149.4-149.4z" fill="#666666" ></path></symbol></svg>',n=(n=document.getElementsByTagName("script"))[n.length-1].getAttribute("data-injectcss");if(n&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(c){console&&console.log(c)}}function s(){f||(f=!0,a())}t=function(){var c,t,e,a;(a=document.createElement("div")).innerHTML=o,o=null,(e=a.getElementsByTagName("svg")[0])&&(e.setAttribute("aria-hidden","true"),e.style.position="absolute",e.style.width=0,e.style.height=0,e.style.overflow="hidden",c=e,(t=document.body).firstChild?(a=c,(e=t.firstChild).parentNode.insertBefore(a,e)):t.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(t,0):(e=function(){document.removeEventListener("DOMContentLoaded",e,!1),t()},document.addEventListener("DOMContentLoaded",e,!1)):document.attachEvent&&(a=t,i=c.document,f=!1,(l=function(){try{i.documentElement.doScroll("left")}catch(c){return void setTimeout(l,50)}s()})(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,s())})}(window);
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/iconfont.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
{
  "id": "2399544",
  "name": "vp",
  "font_family": "iconfont",
  "css_prefix_text": "icon-",
  "description": "",
  "glyphs": [
    {
      "icon_id": "5920503",
      "name": "用户",
      "font_class": "dashujukeshihuaico-",
      "unicode": "e7ff",
      "unicode_decimal": 59391
    },
    {
      "icon_id": "14720218",
      "name": "关闭",
      "font_class": "guanxi",
      "unicode": "e616",
      "unicode_decimal": 58902
    },
    {
      "icon_id": "19984626",
      "name": "人脸识别",
      "font_class": "renlianshibie1",
      "unicode": "e78d",
      "unicode_decimal": 59277
    },
    {
      "icon_id": "14843450",
      "name": "退出",
      "font_class": "tuichu",
      "unicode": "e60d",
      "unicode_decimal": 58893
    },
    {
      "icon_id": "16386636",
      "name": "消息",
      "font_class": "biaoqiankuozhan_xiaoxi-157",
      "unicode": "eb15",
      "unicode_decimal": 60181
    },
    {
      "icon_id": "15954693",
      "name": "消息",
      "font_class": "xiaoxi",
      "unicode": "e612",
      "unicode_decimal": 58898
    },
    {
      "icon_id": "1111451",
      "name": "密码",
      "font_class": "mima",
      "unicode": "e644",
      "unicode_decimal": 58948
    },
    {
      "icon_id": "3851393",
      "name": "账号",
      "font_class": "zhanghao",
      "unicode": "666",
      "unicode_decimal": 1638
    }
  ]
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/iconfont.svg
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,50 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<!--
2013-9-30: Created.
-->
<svg>
<metadata>
Created by iconfont
</metadata>
<defs>
<font id="iconfont" horiz-adv-x="1024" >
  <font-face
    font-family="iconfont"
    font-weight="500"
    font-stretch="normal"
    units-per-em="1024"
    ascent="896"
    descent="-128"
  />
    <missing-glyph />
    <glyph glyph-name="dashujukeshihuaico-" unicode="&#59391;" d="M611.413333 379.30666699999995a231.253333 231.253333 0 1 1-199.253333 0 441.173333 441.173333 0 0 1-341.333333-429.653334h882.773333a441.173333 441.173333 0 0 1-342.186667 429.653334zM611.413333 379.30666699999995a231.253333 231.253333 0 1 1-199.253333 0 441.173333 441.173333 0 0 1-341.333333-429.653334h882.773333a441.173333 441.173333 0 0 1-342.186667 429.653334zM611.413333 379.30666699999995a231.253333 231.253333 0 1 1-199.253333 0 441.173333 441.173333 0 0 1-341.333333-429.653334h882.773333a441.173333 441.173333 0 0 1-342.186667 429.653334zM953.6-50.346667000000025a441.173333 441.173333 0 0 1-341.333333 429.653334 229.546667 229.546667 0 0 1 11.093333 409.173333 228.693333 228.693333 0 0 1-32 3.413333 230.4 230.4 0 0 1-99.413333-438.613333 440.32 440.32 0 0 1-341.333334-403.2zM653.653333 561.066667a230.4 230.4 0 0 0-131.413333-207.786667 441.173333 441.173333 0 0 0 341.333333-403.2h90.453334a441.173333 441.173333 0 0 1-341.333334 429.653333 229.546667 229.546667 0 0 1 11.093334 409.173334 228.266667 228.266667 0 0 1-32 3.413333 229.973333 229.973333 0 0 1-85.333334-15.786667 230.826667 230.826667 0 0 0 147.2-215.466666zM104.96 689.92A135.68 135.68 0 0 1 31.146667 764.16a9.813333 9.813333 0 0 0 0 18.346667A135.68 135.68 0 0 1 104.96 856.746667a9.813333 9.813333 0 0 0 18.346667 0 135.68 135.68 0 0 1 74.24-74.24 9.813333 9.813333 0 0 0 0-18.346667 135.68 135.68 0 0 1-74.24-74.24 9.813333 9.813333 0 0 0-18.346667 0zM905.813333 480a69.546667 69.546667 0 0 1-37.973333 37.973333 5.12 5.12 0 0 0 0 9.386667 69.546667 69.546667 0 0 1 37.973333 37.973333 5.12 5.12 0 0 0 9.386667 0 69.546667 69.546667 0 0 1 37.973333-37.973333 5.12 5.12 0 0 0 0-9.386667 69.546667 69.546667 0 0 1-37.973333-37.973333 5.12 5.12 0 0 0-9.386667 0zM42.666667 224a69.546667 69.546667 0 0 1-37.973334 37.973333 5.12 5.12 0 0 0 0 9.386667 69.546667 69.546667 0 0 1 37.973334 38.4 5.12 5.12 0 0 0 9.386666 0 69.546667 69.546667 0 0 1 37.973334-37.973333 5.12 5.12 0 0 0 0-9.386667 69.546667 69.546667 0 0 1-37.973334-37.973333 5.12 5.12 0 0 0-9.386666-0.426667z"  horiz-adv-x="1024" />
    <glyph glyph-name="guanxi" unicode="&#58902;" d="M683.008 206.336c-4.608 0-9.216 2.048-13.312 5.632L336.384 545.28c-7.168 7.168-7.168 18.944 0 26.112 7.168 7.168 18.944 7.168 26.112 0l333.824-333.824c7.168-7.168 7.168-18.944 0-26.112-3.584-3.584-8.704-5.12-13.312-5.12zM337.408 218.11199999999997c-4.608 0-9.216 2.048-13.312 5.632-7.168 7.168-7.168 18.944 0 26.112L657.92 583.6800000000001c7.168 7.168 18.944 7.168 26.112 0 7.168-7.168 7.168-18.944 0-26.112l-333.824-333.824c-3.584-4.096-8.192-5.632-12.8-5.632z"  horiz-adv-x="1024" />
    <glyph glyph-name="renlianshibie1" unicode="&#59277;" d="M298.666667 0H149.333333c-12.8 0-21.333333 8.533333-21.333333 21.333333V170.66666699999996c0 25.6-17.066667 42.666667-42.666667 42.666666s-42.666667-17.066667-42.666666-42.666666v-149.333334C42.666667-38.39999999999998 89.6-85.33333300000004 149.333333-85.33333300000004H298.666667c25.6 0 42.666667 17.066667 42.666666 42.666666s-17.066667 42.666667-42.666666 42.666667zM938.666667 213.33333300000004c-25.6 0-42.666667-17.066667-42.666667-42.666666v-149.333334c0-12.8-8.533333-21.333333-21.333333-21.333333H725.333333c-25.6 0-42.666667-17.066667-42.666666-42.666667s17.066667-42.666667 42.666666-42.666666h149.333334c59.733333 0 106.666667 46.933333 106.666666 106.666666V170.66666699999996c0 25.6-17.066667 42.666667-42.666666 42.666666zM874.666667 853.333333H725.333333c-25.6 0-42.666667-17.066667-42.666666-42.666666s17.066667-42.666667 42.666666-42.666667h149.333334c12.8 0 21.333333-8.533333 21.333333-21.333333V597.333333c0-25.6 17.066667-42.666667 42.666667-42.666666s42.666667 17.066667 42.666666 42.666666V746.666667C981.333333 806.4 934.4 853.333333 874.666667 853.333333zM85.333333 554.666667c25.6 0 42.666667 17.066667 42.666667 42.666666V746.666667c0 12.8 8.533333 21.333333 21.333333 21.333333H298.666667c25.6 0 42.666667 17.066667 42.666666 42.666667s-17.066667 42.666667-42.666666 42.666666H149.333333C89.6 853.333333 42.666667 806.4 42.666667 746.666667V597.333333c0-25.6 17.066667-42.666667 42.666666-42.666666zM721.066667 234.66666699999996c12.8 21.333333 4.266667 46.933333-17.066667 59.733333-21.333333 12.8-46.933333 4.266667-59.733333-17.066667 0 0-38.4-64-132.266667-64s-132.266667 59.733333-132.266667 64c-12.8 21.333333-38.4 25.6-59.733333 17.066667-21.333333-12.8-25.6-38.4-17.066667-59.733333 4.266667-4.266667 64-106.666667 209.066667-106.666667s204.8 102.4 209.066667 106.666667zM512 384c25.6 0 42.666667 17.066667 42.666667 42.666667V597.333333c0 25.6-17.066667 42.666667-42.666667 42.666667s-42.666667-17.066667-42.666667-42.666667v-170.666666c0-25.6 17.066667-42.666667 42.666667-42.666667zM725.333333 512c25.6 0 42.666667 17.066667 42.666667 42.666667V597.333333c0 25.6-17.066667 42.666667-42.666667 42.666667s-42.666667-17.066667-42.666666-42.666667v-42.666666c0-25.6 17.066667-42.666667 42.666666-42.666667zM298.666667 640c-25.6 0-42.666667-17.066667-42.666667-42.666667v-42.666666c0-25.6 17.066667-42.666667 42.666667-42.666667s42.666667 17.066667 42.666666 42.666667V597.333333c0 25.6-17.066667 42.666667-42.666666 42.666667z"  horiz-adv-x="1024" />
    <glyph glyph-name="tuichu" unicode="&#58893;" d="M511.924148-127.92414799999995a470.660741 470.660741 0 0 0-184.395852 37.167407 471.115852 471.115852 0 0 0-150.679703 101.376A471.87437 471.87437 0 0 0 236.468148 729.618963a37.091556 37.091556 0 1 0 43.159704-59.998815 404.745481 404.745481 0 0 1-121.742222-139.339852 392.305778 392.305778 0 0 1-45.700741-185.078518c0-106.571852 41.604741-206.810074 117.077333-282.206815a397.349926 397.349926 0 0 1 282.661926-117.001482c106.68563 0 207.151407 41.528889 282.737778 117.001482a396.09837 396.09837 0 0 1 117.039407 282.244741c0.227556 64.474074-15.435852 128.037926-45.662814 185.040592a404.366222 404.366222 0 0 1-121.742223 139.188148 36.939852 36.939852 0 1 0 43.159704 60.074667 471.798519 471.798519 0 0 0 59.58163-719.075555A472.936296 472.936296 0 0 0 511.924148-128v0.075852z m11.150222 513.403259a37.015704 37.015704 0 0 0-37.091555 36.939852V860.615111a37.129481 37.129481 0 0 0 74.221037 0v-438.196148a36.939852 36.939852 0 0 0-37.167408-36.939852z"  horiz-adv-x="1024" />
    <glyph glyph-name="biaoqiankuozhan_xiaoxi-157" unicode="&#60181;" d="M393.7792 19.865599999999972c-7.3728 0-14.848 1.6384-21.8624 5.0176-17.7664 8.4992-28.8256 26.0096-28.7744 45.6704l0.1536 59.4944c0 3.4304-1.7408 5.7344-2.7648 6.7584-1.024 1.024-3.328 2.816-6.7584 2.816H172.1344c-27.8528 0-50.4832 22.6304-50.4832 50.4832v436.736c0 27.8528 22.6304 50.4832 50.4832 50.4832h665.6c27.8528 0 50.4832-22.6304 50.4832-50.4832v-436.736c0-27.8528-22.6304-50.4832-50.4832-50.4832h-275.712c-2.2016 0-4.3008-0.768-6.0416-2.1504L425.472 31.283199999999965a49.9968 49.9968 0 0 0-31.6928-11.4176zM172.1344 636.4159999999999c-5.2736 0-9.5232-4.3008-9.5232-9.5232v-436.736c0-5.2736 4.2496-9.5232 9.5232-9.5232h161.5872c13.5168 0 26.2144-5.2736 35.7376-14.848a50.176 50.176 0 0 0 14.7456-35.7888l-0.1536-59.4944c0-5.4272 3.7888-7.8336 5.4272-8.6528 1.6384-0.768 5.888-2.2528 10.1376 1.2288l130.5088 106.1888a50.43712 50.43712 0 0 0 31.8464 11.3152h275.712c5.2736 0 9.5232 4.2496 9.5232 9.5232v436.736c0 5.2736-4.3008 9.5232-9.5232 9.5232H172.1344z m525.056-295.4752c-36.7104 0-66.56 29.8496-66.56 66.56s29.8496 66.56 66.56 66.56 66.56-29.8496 66.56-66.56-29.8496-66.56-66.56-66.56z m0 92.16c-14.1312 0-25.6-11.4688-25.6-25.6s11.4688-25.6 25.6-25.6 25.6 11.4688 25.6 25.6-11.52 25.6-25.6 25.6z m-195.0208-92.16c-36.7104 0-66.56 29.8496-66.56 66.56s29.8496 66.56 66.56 66.56 66.56-29.8496 66.56-66.56-29.8496-66.56-66.56-66.56z m0 92.16c-14.1312 0-25.6-11.4688-25.6-25.6s11.4688-25.6 25.6-25.6 25.6 11.4688 25.6 25.6-11.4688 25.6-25.6 25.6z m-194.9696-92.16c-36.7104 0-66.56 29.8496-66.56 66.56s29.8496 66.56 66.56 66.56 66.56-29.8496 66.56-66.56-29.9008-66.56-66.56-66.56z m0 92.16c-14.1312 0-25.6-11.4688-25.6-25.6s11.4688-25.6 25.6-25.6 25.6 11.4688 25.6 25.6-11.52 25.6-25.6 25.6z"  horiz-adv-x="1024" />
    <glyph glyph-name="xiaoxi" unicode="&#58898;" d="M621.6 134.60000000000002c0-50.5-43-89.8-96.6-89.8s-96.6 39.3-96.6 89.8h193.2m-32.2 523.9c0 33.7-27.9 59.9-64.4 59.9s-64.4-26.2-64.4-59.9v-9.3c-88-28.1-161-117.8-161-222.7v-82.3s0-117.8-32.2-119.7c-19.3 0-32.2-13.1-32.2-29.9 0-16.9 15-29.9 32.2-29.9h515.3c17.2 0 32.2 13.1 32.2 29.9 0 16.9-15 29.9-32.2 29.9-32.2 0-32.2 119.7-32.2 119.7v82.3c0 104.7-68.7 192.7-161 222.7v9.3"  horiz-adv-x="1024" />
    <glyph glyph-name="mima" unicode="&#58948;" d="M760.171097 480.505297L346.927843 480.505297l0 67.441135c0 91.90006 74.809514 166.709574 166.811913 166.709574 92.002399 0 166.811913-74.911853 166.811913-166.914251 0-12.485309 10.029182-22.514491 22.514491-22.514491s22.514491 10.029182 22.514491 22.514491c0 116.870678-95.072556 211.943234-211.840895 211.943234-56.593244 0-109.809314-22.002798-149.823706-62.01719-40.014391-40.014391-62.01719-93.128123-62.01719-149.721367L301.898861 480.505297l-47.485109 0c-27.631421 0-49.736558-22.105137-49.736558-49.634219l0-341.913252c0-27.631421 22.207476-50.452928 49.736558-50.452928l505.655007 0c27.631421 0 50.35059 22.821507 50.35059 50.452928L810.419348 430.871077C810.521687 458.40016 787.802518 480.505297 760.171097 480.505297zM765.492704 88.44613200000003c0-2.763142-2.251449-5.014591-5.014591-5.014591L254.720768 83.43154100000004c-2.763142 0-5.014591 2.251449-5.014591 5.014591L249.706176 430.461723c0 2.763142 2.251449 5.014591 5.014591 5.014591l505.655007 0c2.763142 0 5.014591-2.251449 5.014591-5.014591L765.390366 88.44613200000003zM503.505896 363.839296c-25.482311 0-46.052369-20.570058-46.052369-46.052369 0-16.78353 9.005797-31.520288 22.514491-39.605037L479.968019 188.84029599999997c0-12.38297 10.131521-22.514491 22.514491-22.514491s22.514491 10.131521 22.514491 22.514491l0 88.21587c14.634419 7.675395 24.561263 23.026184 24.561263 40.730762C549.558265 343.269238 528.988207 363.839296 503.505896 363.839296z"  horiz-adv-x="1024" />
    <glyph glyph-name="zhanghao" unicode="&#1638;" d="M828.1 173.60000000000002c-17.3 40.9-42 77.5-73.5 109s-68.2 56.2-109.1 73.5c-10.2 4.3-20.6 8.1-31 11.4 54.5 34.7 90.8 95.7 90.8 164.9 0 107.7-87.6 195.4-195.4 195.4-107.7 0-195.4-87.6-195.4-195.4 0-69.9 36.9-131.3 92.2-165.8-9.6-3.1-19-6.6-28.3-10.5-40.9-17.3-77.5-42-109-73.5s-56.2-68.2-73.5-109c-17.9-42.3-27-87.2-27-133.6h46c0 163.8 133.3 297.1 297.1 297.1S809.1 203.79999999999995 809.1 40h46c0 46.4-9.1 91.3-27 133.6zM360.6 532.5c0 82.4 67 149.4 149.4 149.4s149.4-67 149.4-149.4-67-149.4-149.4-149.4c-82.4 0.1-149.4 67.1-149.4 149.4z"  horiz-adv-x="1024" />
  </font>
</defs></svg>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/iconfont.ttf
Binary files differ
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/iconfont.woff
Binary files differ
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/font/iconfont.woff2
Binary files differ
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/right.png
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/assets/styles/theme.css
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,307 @@
/* çŽ°ä»£åŒ–åŽå°ç®¡ç†ç³»ç»Ÿä¸»é¢˜æ ·å¼ */
/* ========== è‰²å½©è§„范 ========== */
:root {
  /* ä¸»è‰²è°ƒ */
  --primary-color: #1890ff;
  --primary-light: #40a9ff;
  --primary-dark: #096dd9;
  /* è¾…助色 */
  --success-color: #52c41a;
  --warning-color: #faad14;
  --error-color: #ff4d4f;
  --info-color: #1890ff;
  /* ä¸­æ€§è‰² */
  --text-primary: #262626;
  --text-secondary: #595959;
  --text-disabled: #bfbfbf;
  --text-white: #ffffff;
  /* èƒŒæ™¯è‰² */
  --bg-primary: #ffffff;
  --bg-secondary: #fafafa;
  --bg-tertiary: #f5f5f5;
  --bg-dark: #001529;
  --bg-sidebar: #001529;
  --bg-header: #ffffff;
  /* è¾¹æ¡†è‰² */
  --border-color: #d9d9d9;
  --border-light: #f0f0f0;
  --border-dark: #434343;
  /* é˜´å½± */
  --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
  --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07), 0 1px 3px rgba(0, 0, 0, 0.06);
  --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.05);
  --shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.1), 0 10px 10px rgba(0, 0, 0, 0.04);
  /* åœ†è§’ */
  --radius-sm: 4px;
  --radius-md: 6px;
  --radius-lg: 8px;
  --radius-xl: 12px;
  /* é—´è· */
  --spacing-xs: 4px;
  --spacing-sm: 8px;
  --spacing-md: 16px;
  --spacing-lg: 24px;
  --spacing-xl: 32px;
  --spacing-xxl: 48px;
}
/* ========== å…¨å±€æ ·å¼é‡ç½® ========== */
* {
  box-sizing: border-box;
}
body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  line-height: 1.5715;
  color: var(--text-primary);
  background-color: var(--bg-secondary);
  margin: 0;
  padding: 0;
}
/* ========== å¡ç‰‡æ ·å¼ ========== */
.modern-card {
  background: var(--bg-primary);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  border: 1px solid var(--border-light);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.modern-card:hover {
  box-shadow: var(--shadow-lg);
  transform: translateY(-2px);
}
/* ========== æŒ‰é’®æ ·å¼å¢žå¼º ========== */
.modern-btn {
  border-radius: var(--radius-md);
  font-weight: 500;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  border: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-sm);
}
.modern-btn:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
}
.modern-btn-primary {
  background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-light) 100%);
  color: var(--text-white);
}
.modern-btn-primary:hover {
  background: linear-gradient(135deg, var(--primary-dark) 0%, var(--primary-color) 100%);
}
.modern-btn-success {
  background: linear-gradient(135deg, var(--success-color) 0%, #73d13d 100%);
  color: var(--text-white);
}
.modern-btn-warning {
  background: linear-gradient(135deg, var(--warning-color) 0%, #ffc53d 100%);
  color: var(--text-white);
}
.modern-btn-danger {
  background: linear-gradient(135deg, var(--error-color) 0%, #ff7875 100%);
  color: var(--text-white);
}
/* ========== è¡¨å•样式增强 ========== */
.modern-form-item {
  margin-bottom: var(--spacing-lg);
}
.modern-input {
  border-radius: var(--radius-md);
  border: 1px solid var(--border-color);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  padding: var(--spacing-sm) var(--spacing-md);
}
.modern-input:focus {
  border-color: var(--primary-color);
  box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
  outline: none;
}
/* ========== è¡¨æ ¼æ ·å¼å¢žå¼º ========== */
.modern-table {
  background: var(--bg-primary);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}
.modern-table .el-table__header {
  background: var(--bg-tertiary);
}
.modern-table .el-table__header th {
  background: var(--bg-tertiary);
  color: var(--text-primary);
  font-weight: 600;
  border-bottom: 2px solid var(--border-light);
}
.modern-table .el-table__row:hover {
  background: rgba(24, 144, 255, 0.04);
}
/* ========== å¯¼èˆªæ ·å¼å¢žå¼º ========== */
.modern-sidebar {
  background: var(--bg-sidebar);
  box-shadow: var(--shadow-lg);
}
.modern-sidebar .el-menu {
  background: transparent;
  border: none;
}
.modern-sidebar .el-menu-item {
  color: rgba(255, 255, 255, 0.65);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  border-radius: var(--radius-md);
  margin: var(--spacing-xs) var(--spacing-md);
  width: calc(100% - 32px);
}
.modern-sidebar .el-menu-item:hover {
  color: var(--text-white);
  background: rgba(255, 255, 255, 0.1);
  transform: translateX(4px);
}
.modern-sidebar .el-menu-item.is-active {
  color: var(--text-white);
  background: var(--primary-color);
  box-shadow: var(--shadow-md);
}
.modern-header {
  background: var(--bg-header);
  box-shadow: var(--shadow-sm);
  border-bottom: 1px solid var(--border-light);
}
/* ========== åŠ¨ç”»æ•ˆæžœ ========== */
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(30px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
@keyframes fadeInLeft {
  from {
    opacity: 0;
    transform: translateX(-30px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}
@keyframes fadeInRight {
  from {
    opacity: 0;
    transform: translateX(30px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}
.fade-in-up {
  animation: fadeInUp 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.fade-in-left {
  animation: fadeInLeft 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.fade-in-right {
  animation: fadeInRight 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
/* ========== å·¥å…·ç±» ========== */
.text-center { text-align: center; }
.text-left { text-align: left; }
.text-right { text-align: right; }
.flex { display: flex; }
.flex-center { display: flex; align-items: center; justify-content: center; }
.flex-between { display: flex; align-items: center; justify-content: space-between; }
.flex-column { display: flex; flex-direction: column; }
.mt-sm { margin-top: var(--spacing-sm); }
.mt-md { margin-top: var(--spacing-md); }
.mt-lg { margin-top: var(--spacing-lg); }
.mt-xl { margin-top: var(--spacing-xl); }
.mb-sm { margin-bottom: var(--spacing-sm); }
.mb-md { margin-bottom: var(--spacing-md); }
.mb-lg { margin-bottom: var(--spacing-lg); }
.mb-xl { margin-bottom: var(--spacing-xl); }
.p-sm { padding: var(--spacing-sm); }
.p-md { padding: var(--spacing-md); }
.p-lg { padding: var(--spacing-lg); }
.p-xl { padding: var(--spacing-xl); }
/* ========== å“åº”式设计 ========== */
@media (max-width: 768px) {
  :root {
    --spacing-md: 12px;
    --spacing-lg: 16px;
    --spacing-xl: 24px;
  }
  .modern-card {
    margin: var(--spacing-md);
    border-radius: var(--radius-md);
  }
}
/* ========== æ»šåŠ¨æ¡æ ·å¼ ========== */
::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}
::-webkit-scrollbar-track {
  background: var(--bg-tertiary);
  border-radius: var(--radius-sm);
}
::-webkit-scrollbar-thumb {
  background: var(--border-color);
  border-radius: var(--radius-sm);
}
::-webkit-scrollbar-thumb:hover {
  background: var(--text-disabled);
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/components/table/pagination.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
<template>
  <div class="pagination">
    <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="page.page" :page-sizes="pageSizes" :page-size="page.limit" layout="total, sizes, prev, pager, next, jumper" :total="total">
    </el-pagination>
  </div>
</template>
<script>
export default {
  props: {
    total: {
      type: Number,
      default: () => { }
    },
    pageSizes: {
      type: Array,
      default: () => {
        return [5, 10, 20, 100]
      }
    }
  },
  data () {
    return {
      page: {
        page: 1,
        limit: 20
      }
    }
  },
  methods: {
    Page (val) {
      this.page.page = val
    },
    handleSizeChange (val) {
      this.page.limit = val
      this.$emit('pageChange', this.page)
    },
    handleCurrentChange (val) {
      this.page.page = val
      this.$emit('pageChange', this.page)
    }
  }
}
</script>
<style scoped lang="less">
.pagination {
  float: right;
  padding: 1% 4% 2% 0px;
}
::v-deep .el-pagination .el-select .el-input {
  width: 110px;
}
</style>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/components/table/tableList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,222 @@
<template>
  <!-- ä¸»åˆ—表 -->
  <el-table :data='tableData' :height='tableHeight' :show-header='showHeader' stripe @expand-change="expandSelect"
    :expand-row-keys="expands" @cell-click="handleCellClick" :empty-text="$t('common.noData')" class='el_tab_alage'
    :header-cell-style="cellHeaderStyle" :row-key='getRowKeys' @selection-change="handleSelectionChange">
    <!-- å•选框 -->
    <el-table-column align="center" width="50" label=""
      v-if="tableSelection.key === true && tableSelection.type === 'radio'">
      <template slot-scope="scope">
        <el-radio :label="scope.$index" v-model="radio"
          @change="handleTemplateRow(scope.$index, scope.row)">&nbsp;</el-radio>
      </template>
    </el-table-column>
    <!-- index索引 -->
    <el-table-column label="序号" type="index" width="50" align="center"
      v-if="tableSelection.key === true && tableSelection.type === 'index'"></el-table-column>
    <!-- å¤šé€‰æ¡† -->
    <el-table-column type="selection" width="50" align="center"
      v-if="tableSelection.key === true && tableSelection.type === 'selection'"></el-table-column>
    <!-- åˆ—表表头-->
    <el-table-column type="expand" v-if="tableSelection.key === true && tableSelection.type === 'expand'">
      <template slot-scope="scope">
        <el-form label-position="left" inline class="demo-table-expand">
          <el-form-item :label="index.label" v-for="(index, item) in tableLabel" :key='item'
            v-show="index.type === 'expand'">
            <span>{{ scope.row[index.list] }}</span>
          </el-form-item>
        </el-form>
      </template>
    </el-table-column>
    <template v-for="(index, item) in tableLabel">
      <el-table-column fit :align='index.tableAlign ? index.tableAlign : "center"' :key='item' :sortable='index.sort'
        v-if="index.type !== 'expand'" :label="index.label" :width="index.width"
        :show-overflow-tooltip="index.overflowShow === 'hidden' ? true : false" :prop="index.list">
        <template slot-scope="scope">
          <!-- å›¾ç‰‡ -->
          <template v-if="index.type === 'image'">
            <el-image v-if="scope.row[index.list] !== ''" style="width: 100px; height: 50px;"
              :src="scope.row[index.list]">
            </el-image>
            <div v-else></div>
          </template>
          <!-- äº§å“å›¾ç‰‡ -->
          <template v-if="index.type === 'productImage'">
            <el-image v-if="scope.row[index.list] !== ''" style="width: 100px; height: 50px;"
              :src="getProductImageUrl(scope.row[index.list])">
            </el-image>
            <div v-else></div>
          </template>
          <!-- å¤´åƒ -->
          <template v-else-if="index.type === 'head'">
            <el-image v-if="!(scope.row[index.list] === '' || scope.row[index.list] === null)"
              style="width: 50px; height: 50px;" :src="scope.row[index.list]">
            </el-image>
            <div v-else></div>
          </template>
          <!-- æŒ‰é’® -->
          <template v-else-if="index.type === 'btn'">
            <el-button type="text" @click.native.prevent="index.method(scope.row, scope)">
              <u>{{ scope.row[index.list] }}</u>
            </el-button>
          </template>
          <!-- ä¸‹æ‹‰ -->
          <template v-else-if="index.type === 'select'">
            <el-select v-model="scope.row[index.list]" @change="changeType($event, scope.row, item)" size="medium">
              <el-option v-for="item in index.options" :key="item.value" :label="item.label" :value="item.value">
              </el-option>
            </el-select>
          </template>
          <!-- å¼€å…³æŒ‰é’® -->
          <template v-else-if="index.type === 'switch'">
            <div v-if="index.noSwitch(scope.row)">
              <el-switch @change="index.method(scope.row, scope)" v-model="scope.row[index.list]"
                :inactive-value="index.offValue !== null ? index.offValue : 'off'"
                :active-value="index.onValue ? index.onValue : 'on'" :inactive-text="index.offText ? index.offText : ''"
                :active-text="index.onText ? index.onText : ''"
                :inactive-color="index.offColor ? index.offColor : '#ff4949'"
                :active-color="index.onColor ? index.onColor : '#13ce66'"></el-switch>
            </div>
          </template>
          <!-- å†…容自定义 -->
          <template v-else-if="index.type === 'html'">
            <div v-html="index.code(scope.row)" class="theHtml"></div>
          </template>
          <!-- æ­£å¸¸æ˜¾ç¤º -->
          <template v-else>
            <!-- {{scope.row[index.list]}} -->
            {{ scope.row[index.list] ? scope.row[index.list] : '-' }}
          </template>
        </template>
      </el-table-column>
    </template>
    <!-- æ­£å¸¸æŒ‰é’®æ“ä½œ -->
    <el-table-column v-if="tableOption.value !== undefined" fit
      :align='tableOption.align ? tableOption.align : "center"' :label="tableOption.label"
      :fixed="tableOption.fixed ? tableOption.fixed : false" :width="tableOption.width">
      <template style="margin-left: 30px;" slot-scope="scope">
        <el-button v-for="(value, item) in filteredOptions(scope.row)" :key='item'
          :disabled="value.disabled ? value.disabled(scope.row) : false" :type="value.type ? value.type : 'text'"
          :style="value.style ? value.style : {}" :plain='value.plain ? value.plain : false'
          :round='value.round ? value.round : false' :size='value.size ? value.size : "medium"' :icon="value.icon"
          @click.native.prevent="value.method(scope.row, scope)">{{ value.label }}
        </el-button>
        <el-popover placement="top" trigger="hover"
          v-if="tableOption.isShowMore ? tableOption.isShowMore(scope.row) : false">
          <div class="popover-content">
            <el-button class="popover-button" v-for="(value, item) in filteredPopoverOptions(scope.row)" :key='item'
              :disabled="value.disabled ? value.disabled(scope.row) : false" :type="value.type ? value.type : 'info'"
              :style="value.style ? value.style : {}" :size='value.size ? value.size : "mini"'
              @click.native.prevent="value.method(scope.row, scope)">{{ value.label }}
            </el-button>
          </div>
          <el-button slot="reference" type="text"
            :style="tableOption.buttonStyle ? tableOption.buttonStyle : {}">{{ tableOption.buttonText }}</el-button>
        </el-popover>
      </template>
    </el-table-column>
  </el-table>
</template>
<script>
export default {
  data () {
    return {
      radio: '',
      cellHeaderStyle: {
        fontSize: "16px",
        color: "#606266"
      },
      expands: [],
      getRowKeys (row) {
        return row.id
      }
    }
  },
  props: {
    tableData: {
      type: Array,
      default: () => { }
    },
    tableHeight: {
      type: Number,
      default: () => {
        return null
      }
    },
    showHeader: {
      type: Boolean,
      default: () => {
        return true
      }
    },
    tableSelection: {
      type: Object,
      default: () => {
        return {
          key: false,
          type: '',
          detaile: false
        }
      }
    },
    tableLabel: {
      type: Array,
      default: () => { }
    },
    tableOption: {
      type: Object,
      default: () => {
        return {
          value: undefined
        }
      }
    }
  },
  methods: {
    handleSelectionChange (val) {
      this.$emit('onHandleSelectionChange', val)
    },
    handleTemplateRow (index, row) {
      this.$emit('onHandleTemplateRow', row)
    },
    changeType (event, row) {
      this.$emit('onChangeType', event, row)
    },
    expandSelect (row, expandedRows) {
      const that = this
      if (expandedRows.length) {
        that.expands = []
        if (row) {
          that.expands.push(row.id)
        }
      } else {
        that.expands = []
      }
    },
    handleCellClick (row, column) {
      this.$emit('onHandleCellClick', row, column)
    },
    getProductImageUrl (id) {
      return process.env.VUE_APP_BASE_API + 'saas/sysadmin/product/download/id?id=' + id
    },
    // âœ… æ–°å¢žï¼šè¿‡æ»¤é€‰é¡¹çš„æ–¹æ³•
    filteredOptions(row) {
      if (!this.tableOption.options) return [];
      return this.tableOption.options.filter(value => {
        return value.show ? value.show(row) : true;
      });
    },
    // âœ… æ–°å¢žï¼šè¿‡æ»¤å¼¹å‡ºæ¡†é€‰é¡¹çš„æ–¹æ³•
    filteredPopoverOptions(row) {
      if (!this.tableOption.popoverOptions) return [];
      return this.tableOption.popoverOptions.filter(value => {
        return value.show ? value.show(row) : true;
      });
    }
  }
}
</script>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/directives/model-permission.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,44 @@
// src/directives/model-permission.js
export default {
  inserted(el, binding) {
    // ä»ŽsessionStorage获取设备型号
    let publicConfig = sessionStorage.getItem('publicConfig')
    let { model } = publicConfig ? JSON.parse(publicConfig) : {}
    const { value } = binding
    if (!value) return // æ²¡æœ‰é…ç½®æƒé™ï¼Œé»˜è®¤æ˜¾ç¤º
    if (Array.isArray(value)) {
      // å…è®¸çš„型号列表
      if (!value.includes(model)) {
        el.parentNode?.removeChild(el)
      }
    } else if (typeof value === 'string') {
      // å•个允许的型号
      if (model !== value) {
        el.parentNode?.removeChild(el)
      }
    } else if (typeof value === 'object') {
      // å¤æ‚配置:{ allow: [], deny: [] }
      const { allow, deny } = value
      if (allow && !allow.includes(model)) {
        el.parentNode?.removeChild(el)
      }
      if (deny && deny.includes(model)) {
        el.parentNode?.removeChild(el)
      }
    }
  },
  // update(el, binding) {
  //   const { value, oldValue } = binding
  //   if (value !== oldValue) {
  //     const elClone = el.cloneNode(true)
  //     el.parentNode?.replaceChild(elClone, el)
  //     this.inserted(elClone, binding)
  //   }
  // }
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/excel/Blob.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,179 @@
/* eslint-disable */
/* Blob.js
 * A Blob implementation.
 * 2014-05-27
 *
 * By Eli Grey, http://eligrey.com
 * By Devin Samarin, https://github.com/eboyjr
 * License: X11/MIT
 *   See LICENSE.md
 */
/*global self, unescape */
/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
 plusplus: true */
/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
(function (view) {
    "use strict";
    view.URL = view.URL || view.webkitURL;
    if (view.Blob && view.URL) {
        try {
            new Blob;
            return;
        } catch (e) {}
    }
    // Internally we use a BlobBuilder implementation to base Blob off of
    // in order to support older browsers that only have BlobBuilder
    var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) {
            var
                get_class = function(object) {
                    return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
                }
                , FakeBlobBuilder = function BlobBuilder() {
                    this.data = [];
                }
                , FakeBlob = function Blob(data, type, encoding) {
                    this.data = data;
                    this.size = data.length;
                    this.type = type;
                    this.encoding = encoding;
                }
                , FBB_proto = FakeBlobBuilder.prototype
                , FB_proto = FakeBlob.prototype
                , FileReaderSync = view.FileReaderSync
                , FileException = function(type) {
                    this.code = this[this.name = type];
                }
                , file_ex_codes = (
                    "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR "
                    + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
                ).split(" ")
                , file_ex_code = file_ex_codes.length
                , real_URL = view.URL || view.webkitURL || view
                , real_create_object_URL = real_URL.createObjectURL
                , real_revoke_object_URL = real_URL.revokeObjectURL
                , URL = real_URL
                , btoa = view.btoa
                , atob = view.atob
                , ArrayBuffer = view.ArrayBuffer
                , Uint8Array = view.Uint8Array
                ;
            FakeBlob.fake = FB_proto.fake = true;
            while (file_ex_code--) {
                FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
            }
            if (!real_URL.createObjectURL) {
                URL = view.URL = {};
            }
            URL.createObjectURL = function(blob) {
                var
                    type = blob.type
                    , data_URI_header
                    ;
                if (type === null) {
                    type = "application/octet-stream";
                }
                if (blob instanceof FakeBlob) {
                    data_URI_header = "data:" + type;
                    if (blob.encoding === "base64") {
                        return data_URI_header + ";base64," + blob.data;
                    } else if (blob.encoding === "URI") {
                        return data_URI_header + "," + decodeURIComponent(blob.data);
                    } if (btoa) {
                        return data_URI_header + ";base64," + btoa(blob.data);
                    } else {
                        return data_URI_header + "," + encodeURIComponent(blob.data);
                    }
                } else if (real_create_object_URL) {
                    return real_create_object_URL.call(real_URL, blob);
                }
            };
            URL.revokeObjectURL = function(object_URL) {
                if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) {
                    real_revoke_object_URL.call(real_URL, object_URL);
                }
            };
            FBB_proto.append = function(data/*, endings*/) {
                var bb = this.data;
                // decode data to a binary string
                if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) {
                    var
                        str = ""
                        , buf = new Uint8Array(data)
                        , i = 0
                        , buf_len = buf.length
                        ;
                    for (; i < buf_len; i++) {
                        str += String.fromCharCode(buf[i]);
                    }
                    bb.push(str);
                } else if (get_class(data) === "Blob" || get_class(data) === "File") {
                    if (FileReaderSync) {
                        var fr = new FileReaderSync;
                        bb.push(fr.readAsBinaryString(data));
                    } else {
                        // async FileReader won't work as BlobBuilder is sync
                        throw new FileException("NOT_READABLE_ERR");
                    }
                } else if (data instanceof FakeBlob) {
                    if (data.encoding === "base64" && atob) {
                        bb.push(atob(data.data));
                    } else if (data.encoding === "URI") {
                        bb.push(decodeURIComponent(data.data));
                    } else if (data.encoding === "raw") {
                        bb.push(data.data);
                    }
                } else {
                    if (typeof data !== "string") {
                        data += ""; // convert unsupported types to strings
                    }
                    // decode UTF-16 to binary string
                    bb.push(unescape(encodeURIComponent(data)));
                }
            };
            FBB_proto.getBlob = function(type) {
                if (!arguments.length) {
                    type = null;
                }
                return new FakeBlob(this.data.join(""), type, "raw");
            };
            FBB_proto.toString = function() {
                return "[object BlobBuilder]";
            };
            FB_proto.slice = function(start, end, type) {
                var args = arguments.length;
                if (args < 3) {
                    type = null;
                }
                return new FakeBlob(
                    this.data.slice(start, args > 1 ? end : this.data.length)
                    , type
                    , this.encoding
                );
            };
            FB_proto.toString = function() {
                return "[object Blob]";
            };
            FB_proto.close = function() {
                this.size = this.data.length = 0;
            };
            return FakeBlobBuilder;
        }(view));
    view.Blob = function Blob(blobParts, options) {
        var type = options ? (options.type || "") : "";
        var builder = new BlobBuilder();
        if (blobParts) {
            for (var i = 0, len = blobParts.length; i < len; i++) {
                builder.append(blobParts[i]);
            }
        }
        return builder.getBlob(type);
    };
}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/excel/Export2Excel.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,142 @@
/* eslint-disable */
require('script-loader!file-saver');
require('./Blob');
require('script-loader!xlsx/dist/xlsx.core.min');
function generateArray(table) {
    var out = [];
    var rows = table.querySelectorAll('tr');
    var ranges = [];
    for (var R = 0; R < rows.length; ++R) {
        var outRow = [];
        var row = rows[R];
        var columns = row.querySelectorAll('td');
        for (var C = 0; C < columns.length; ++C) {
            var cell = columns[C];
            var colspan = cell.getAttribute('colspan');
            var rowspan = cell.getAttribute('rowspan');
            var cellValue = cell.innerText;
            if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
            //Skip ranges
            ranges.forEach(function (range) {
                if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
                    for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
                }
            });
            //Handle Row Span
            if (rowspan || colspan) {
                rowspan = rowspan || 1;
                colspan = colspan || 1;
                ranges.push({s: {r: R, c: outRow.length}, e: {r: R + rowspan - 1, c: outRow.length + colspan - 1}});
            }
            ;
            //Handle Value
            outRow.push(cellValue !== "" ? cellValue : null);
            //Handle Colspan
            if (colspan) for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
        }
        out.push(outRow);
    }
    return [out, ranges];
};
function datenum(v, date1904) {
    if (date1904) v += 1462;
    var epoch = Date.parse(v);
    return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}
function sheet_from_array_of_arrays(data, opts) {
    var ws = {};
    var range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}};
    for (var R = 0; R != data.length; ++R) {
        for (var C = 0; C != data[R].length; ++C) {
            if (range.s.r > R) range.s.r = R;
            if (range.s.c > C) range.s.c = C;
            if (range.e.r < R) range.e.r = R;
            if (range.e.c < C) range.e.c = C;
            var cell = {v: data[R][C]};
            if (cell.v == null) continue;
            var cell_ref = XLSX.utils.encode_cell({c: C, r: R});
            if (typeof cell.v === 'number') cell.t = 'n';
            else if (typeof cell.v === 'boolean') cell.t = 'b';
            else if (cell.v instanceof Date) {
                cell.t = 'n';
                cell.z = XLSX.SSF._table[14];
                cell.v = datenum(cell.v);
            }
            else cell.t = 's';
            ws[cell_ref] = cell;
        }
    }
    if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
    return ws;
}
function Workbook() {
    if (!(this instanceof Workbook)) return new Workbook();
    this.SheetNames = [];
    this.Sheets = {};
}
function s2ab(s) {
    var buf = new ArrayBuffer(s.length);
    var view = new Uint8Array(buf);
    for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
    return buf;
}
export function export_table_to_excel(id) {
    var theTable = document.getElementById(id);
    console.log('a')
    var oo = generateArray(theTable);
    var ranges = oo[1];
    /* original data */
    var data = oo[0];
    var ws_name = "SheetJS";
    console.log(data);
    var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
    /* add ranges to worksheet */
    // ws['!cols'] = ['apple', 'banan'];
    ws['!merges'] = ranges;
    /* add worksheet to workbook */
    wb.SheetNames.push(ws_name);
    wb.Sheets[ws_name] = ws;
    var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
    saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), "test.xlsx")
}
function formatJson(jsonData) {
    console.log(jsonData)
}
export function export_json_to_excel(th, jsonData, defaultTitle) {
    /* original data */
    var data = jsonData;
    data.unshift(th);
    var ws_name = "SheetJS";
    var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
    /* add worksheet to workbook */
    wb.SheetNames.push(ws_name);
    wb.Sheets[ws_name] = ws;
    var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
    var title = defaultTitle || '列表'
    saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), title + ".xlsx")
}
export default class Export2Excel {}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/ar.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,432 @@
export default {
  // ØªØ³Ø¬ÙŠÙ„ Ø§Ù„دخول
  login: {
    lang: 'اللغة',
    systemname: 'دخول',
    username: 'المستخدم',
    username_label: 'يرجى Ø¥Ø¯Ø®Ø§Ù„ Ø§Ø³Ù… Ø§Ù„مستخدم',
    pwd: 'كلمة Ø§Ù„مرور',
    pwd_label: 'يرجى Ø¥Ø¯Ø®Ø§Ù„ ÙƒÙ„مة Ø§Ù„مرور',
    pwd_info: 'أدخل ØµÙŠØºØ© ÙƒÙ„مة Ù…رور ØµØ­ÙŠØ­Ø©',
    success_msg: 'تم ØªØ³Ø¬ÙŠÙ„ Ø§Ù„دخول Ø¨Ù†Ø¬Ø§Ø­',
    error_name: 'كلمة Ø§Ù„مرور ØºÙŠØ± ØµØ­ÙŠØ­Ø©',
    error_res: 'لا ÙŠÙ„بي Ù…تطلبات Ø§Ù„دخول',
    login: 'تسجيل Ø§Ù„دخول',
  },
  // Ø§Ù„شريط Ø§Ù„جانبي
  aside: {
    systemname: 'لوحة',
    quit: 'تسجيل Ø§Ù„خروج',
    deviceControl: 'التحكم',
    basicSetting: 'الإعدادات',
    workerSetting: 'الأفراد',
    deviceMonitoring: 'المراقبة',
    recordManagement: 'السجلات',
    securityManagement: "إدارة Ø§Ù„مفاتيح",
    tips: 'تنبيه',
    tips_msg: 'هل ØªØ±ÙŠØ¯ ØªØ³Ø¬ÙŠÙ„ Ø§Ù„خروج؟',
  },
  control: {
    remoteControl: 'عن Ø¨Ø¹Ø¯',
    restart: 'إعادة ØªØ´ØºÙŠÙ„',
    clickToRestart: 'اضغط Ù„إعادة Ø§Ù„تشغيل',
    restartConfirm: 'تأكيد Ø¥Ø¹Ø§Ø¯Ø© Ø§Ù„تشغيل؟',
    restartSuccess: 'تمت Ø¥Ø¹Ø§Ø¯Ø© Ø§Ù„تشغيل',
    restartFailed: 'فشلت Ø¥Ø¹Ø§Ø¯Ø© Ø§Ù„تشغيل',
    remoteOpen: 'فتح Ø§Ù„باب Ø¹Ù† Ø¨Ø¹Ø¯',
    clickToOpen: 'اضغط Ù„لفتح',
    openConfirm: 'تأكيد Ø§Ù„فتح Ø¹Ù† Ø¨Ø¹Ø¯ØŸ',
    remoteOpenSuccess: 'تم Ø§Ù„فتح Ø¨Ù†Ø¬Ø§Ø­',
    remoteOpenFailed: 'فشل Ø§Ù„فتح',
    reset: 'إعادة Ø¶Ø¨Ø· Ø§Ù„جهاز',
    clickToReset: 'اضغط Ù„إعادة Ø§Ù„ضبط',
    resetConfirm: 'تأكيد Ø¥Ø¹Ø§Ø¯Ø© Ø§Ù„ضبط؟',
    resetWillOut: "بعد Ø§Ù„ريست Ø®Ø±ÙˆØ¬",
    resetSuccess: 'تمت Ø¥Ø¹Ø§Ø¯Ø© Ø§Ù„ضبط',
    resetFailed: 'فشلت Ø¥Ø¹Ø§Ø¯Ø© Ø§Ù„ضبط',
    firmwareUpgrade: 'تحديث Ø§Ù„برنامج Ø§Ù„ثابت',
    upgradeConfig: 'إعدادات Ø§Ù„تحديث',
    firmwareUrl: 'رابط Ø§Ù„برنامج',
    md5Checksum: 'قيمة md5',
    startUpgrade: 'بدء Ø§Ù„تحديث',
    urlRequired: 'الرابط Ù…طلوب',
    md5Required: 'قيمة md5 Ù…طلوبة',
    urlInvalid: 'يرجى Ø¥Ø¯Ø®Ø§Ù„ Ø±Ø§Ø¨Ø· ØµØ­ÙŠØ­',
    md5Invalid: 'يرجى Ø¥Ø¯Ø®Ø§Ù„ md5 ØµØ­ÙŠØ­',
    upgradeConfirm: 'تأكيد Ø§Ù„تحديث؟',
    upgradeSuccess: 'تم Ø§Ù„تحديث Ø¨Ù†Ø¬Ø§Ø­',
    clearFile: "مسح Ø§Ù„ملف",
    uploading: 'جاري Ø§Ù„رفع...',
    uploadAndUpgrade: 'رفع ÙˆØªØ­Ø¯ÙŠØ«',
    restartTips: 'إعادة ØªØ´ØºÙŠÙ„ Ø¢Ù…نة Ø¯ÙˆÙ† ÙÙ‚دان Ø§Ù„بيانات',
    restarting: 'جاري Ø¥Ø¹Ø§Ø¯Ø© Ø§Ù„تشغيل...',
    remoteTips: 'تحكم Ø¹Ù† Ø¨Ø¹Ø¯ Ø¨Ø§Ù„وصول',
    opening: 'جاري Ø§Ù„فتح...',
    resetTips: 'إرجاع Ø¥Ø¹Ø¯Ø§Ø¯Ø§Øª Ø§Ù„مصنع، Ø³ÙŠØ­Ø°Ù Ø§Ù„بيانات',
    reseting: 'جاري Ø§Ù„إعادة...',
    urlUpgrade: 'تحديث Ø¹Ø¨Ø± Ø§Ù„رابط',
    fileUpgrade: 'تحديث Ø¹Ø¨Ø± Ù…لف',
    uploadFile: 'اضغط Ù„رفع Ù…لف Ø§Ù„برنامج',
    formatFile: 'يدعم .zip Ø£Ùˆ .dpk، Ø§Ù„حد 20MB',
    fileName: 'اسم Ø§Ù„ملف',
    size: 'الحجم',
  },
  config: {
    second: 'Ø«',
    millisecond: 'ملث',
    min: 'دقائق',
    notsave: 'لا ØªØ­ÙØ¸',
    save: 'حفظ',
    noVoice: 'بدون ØµÙˆØª',
    no: 'لا',
    yes: 'نعم',
    basicConfiguration: 'الإعدادات Ø§Ù„أساسية',
    displaySettings: 'إعدادات Ø§Ù„عرض',
    informationDisplay: 'عرض Ø§Ù„معلومات',
    audioSettings: 'إعدادات Ø§Ù„صوت',
    languageAndThemes: 'اللغة ÙˆØ§Ù„سمات',
    autoAdjustScreenBrightness: 'سطوع ØªÙ„قائي',
    screenBrightness: 'سطوع Ø§Ù„شاشة',
    autoTurnOffScreen: 'إيقاف Ø§Ù„شاشة ØªÙ„قائياً',
    autoTurnOffScreenTime: 'زمن Ø§Ù„إيقاف',
    autoScreenSaver: 'حافظة Ø´Ø§Ø´Ø© ØªÙ„قائية',
    autoScreenSaverTime: 'زمن Ø§Ù„حافظة',
    displayDeviceSn: 'عرض SN',
    displayIp: 'عرض IP',
    displayIdentityCard: 'عرض Ø´Ù‡Ø§Ø¯Ø© Ø§Ù„سحابة',
    volume: 'مستوى Ø§Ù„صوت',
    language: 'اللغة',
    displayCode: "عرض Ø±Ù…ز Ø§Ù„تطبيق Ø§Ù„مصغر",
    themeMode: "نمط Ø§Ù„عمل",
    cn: 'الصينية',
    en: 'الإنجليزية',
    es: 'الإسبانية',
    fr: 'الفرنسية',
    de: 'الألمانية',
    ru: 'الروسية',
    ar: 'العربية',
    pt: 'البرتغال',
    ko: 'الكورية',
    standardMode: 'الوضع Ø§Ù„قياسي',
    simpleMode: 'الوضع Ø§Ù„مبسط',
    firstLogin: 'أول ØªØ³Ø¬ÙŠÙ„ Ø¯Ø®ÙˆÙ„',
    backlight: 'إضاءة Ø®Ù„فية Ø§Ù„شاشة',
    brightness: 'الإضاءة Ø§Ù„بيضاء',
    nirBrightness: 'إضاءة Ø§Ù„أشعة ØªØ­Øª Ø§Ù„حمراء',
    never: 'أبداً',
    min1: 'دقيقة',
    min2: 'دقيقتان',
    min3: '3 Ø¯Ù‚ائق',
    min4: '4 Ø¯Ù‚ائق',
    min5: '5 Ø¯Ù‚ائق',
    networkConfiguration: 'إعدادات Ø§Ù„شبكة',
    otherConfiguration: 'إعدادات Ø£Ø®Ø±Ù‰',
    ipConfiguration: 'تكوين IP',
    devicePassword: 'كلمة Ù…رور Ø§Ù„جهاز',
    protocolPassword: 'كلمة Ù…رور Ø§Ù„بروتوكول',
    networkType: 'نوع Ø§Ù„شبكة',
    ethernet: 'إيثرنت',
    wifiName: 'اسم Wi‑Fi',
    wifiPassword: 'كلمة Ù…رور Wi‑Fi',
    dhcpModeSelection: 'وضع DHCP',
    dhcpMode: 'تلقائي',
    customNetworkConfiguration: 'يدوي',
    ipAddress: 'عنوان IP',
    gateway: 'البوابة',
    subnetMask: 'قناع Ø§Ù„شبكة',
    dnsServer: 'خادم DNS',
    mac: 'عنوان MAC',
    mqttRelatedConfiguration: 'إعدادات MQTT',
    mqttConnectionInformation: 'معلومات Ø§ØªØµØ§Ù„ MQTT',
    sessionConfiguration: 'إعدادات Ø§Ù„جلسة',
    serverAddress: 'عنوان Ø§Ù„خادم',
    clientID: 'معرّف Ø§Ù„عميل',
    userName: 'اسم Ø§Ù„مستخدم',
    userPassword: 'كلمة Ø§Ù„مرور',
    topicPrefix: 'بادئة Ø§Ù„موضوع',
    onlineChecking: 'تحقق Ø¹Ø¨Ø± Ø§Ù„إنترنت',
    onlineCheckingTimeout: 'مهلة Ø§Ù„تحقق',
    cleanSession: 'تنظيف Ø§Ù„جلسة',
    clientIdSuffix: 'لاحقة Ø§Ù„معرّف',
    willTopic: 'موضوع Will',
    enterpriseWechat:'وضع Enterprise WeChat ØºÙŠØ± Ù…فعل',
    faceRelatedConfiguration: 'إعدادات Ø§Ù„وجه',
    functionalInformation: 'معلومات Ø§Ù„وظيفة',
    prompt: 'تنبيه',
    faceSimilarityThreshold: 'عتبة Ø§Ù„تشابه',
    livenessDetectionFunction: 'كشف Ø§Ù„حيوية',
    livenessDetectionThreshold: 'عتبة Ø§Ù„حيوية',
    infraredImageDisplay: "عرض Ø§Ù„أشعة ØªØ­Øª Ø§Ù„حمراء",
    maskRecognition: "التعرف Ø¹Ù„Ù‰ Ø§Ù„قناع",
    strangerVoice: "صوت Ø§Ù„غريب",
    voiceMode: "وضع Ø§Ù„صوت",
    voiceModeDate: 'تحية Ù…خصصة',
    imageSaveType: "نوع Ø§Ù„حفظ",
    saveStrangerImage: "حفظ ØµÙˆØ±Ø© Ø§Ù„غريب",
    fullView: "بانوراما",
    face: "الوجه",
    broadcastPleaseRegisterFirst: 'تشغيل "سجل Ø£ÙˆÙ„اً"',
    broadcastHelloStranger: 'تشغيل "غريب"',
    broadcastName: 'تشغيل Ø§Ù„اسم',
    broadcastGreeting: 'تشغيل ØªØ­ÙŠØ© Ù…خصصة',
    greeting: 'تحية',
    broadcastWelcome: 'تشغيل "مرحباً"',
    recognitionSwitch: 'مفتاح Ø¥Ø¹Ø§Ø¯Ø© Ø§Ù„كشف',
    systemRelatedConfiguration: 'إعدادات Ø§Ù„نظام',
    functionSwitch: 'مفاتيح Ø§Ù„وظائف',
    cardSwipingSwitch: 'البطاقة',
    passwordSwitch: 'كلمة Ø§Ù„مرور',
    strangerImage: 'صورة Ø§Ù„غريب',
    cloudCertificateSwitch: 'شهادة Ø§Ù„سحابة',
    physicalCardNumber: 'رقم Ø§Ù„بطاقة Ø§Ù„فيزيائية',
    cloudCertificateAcquisition: 'الحصول Ø¹Ù„Ù‰ Ø´Ù‡Ø§Ø¯Ø© Ø§Ù„سحابة',
    heartbeatConfig: 'نبضات Ø§Ù„قلب',
    heartbeatSwitch: 'تفعيل Ø§Ù„نبضات',
    heartRateInterval: 'الفاصل Ø§Ù„زمني',
    heartbeatTopic: 'موضوع Ø§Ù„نبض',
    heartbeatContent: 'محتوى Ø§Ù„نبض',
    basicInformation: 'معلومات Ø£Ø³Ø§Ø³ÙŠØ©',
    deviceMac: 'عنوان MAC',
    uuid: 'UUID',
    sn: 'SN',
    model: 'الطراز',
    version: "الإصدار",
    appVersion: "إصدار Ø§Ù„برنامج",
    releaseTime: "وقت Ø§Ù„تحديث",
    totaldisk: 'المساحة Ø§Ù„إجمالية',
    freedisk: 'المساحة Ø§Ù„متبقية',
    passageConfiguration: 'إعدادات Ø§Ù„مرور',
    functionConfiguration: 'إعدادات Ø§Ù„وظائف',
    numberOfPassageRecords: 'الحد Ø§Ù„أقصى Ù„لسجلات',
    durationOfRelayOpening: 'مدة ÙØªØ­ Ø§Ù„مرحل',
    alarmSwitch: 'إنذار',
    fireAlarmSwitch: 'إنذار Ø­Ø±ÙŠÙ‚',
    fireAlarmStatus: 'حالة Ø§Ù„حريق',
    normal: 'طبيعي',
    warning: 'تحذير',
    tamperSwitch: 'إنذار Ø§Ù„عبث',
    uploadToCloudSwitch: 'مفتاح ØªØ­Ù…يل Ø§Ù„وجه',
    clockConfiguration: 'إعدادات Ø§Ù„وقت',
    timeSynchronizationSwitch: 'مزامنة Ø§Ù„وقت',
    timeSynchronizationServerIP: 'خادم Ø§Ù„وقت',
    timedSynchronizationTime: 'وقت Ø§Ù„مزامنة',
    timeZone: 'المنطقة Ø§Ù„زمنية',
    setDeviceTime: 'ضبط ÙˆÙ‚ت Ø§Ù„جهاز',
    restartAfterSetting: 'سيُعاد ØªØ´ØºÙŠÙ„ Ø§Ù„جهاز Ø¨Ø¹Ø¯ Ø§Ù„ضبط',
    cloudCertificateActivation: 'تفعيل Ø´Ù‡Ø§Ø¯Ø© Ø§Ù„سحابة',
    activationKey: 'مفتاح Ø§Ù„تفعيل',
    cloudTips1: 'أدخل Ø§Ù„مفتاح Ø¨Ø¯ÙˆÙ† Ù…سافات',
    cloudTips2: 'بعد Ø§Ù„تفعيل Ø³ÙŠØªØµÙ„ Ø¨Ø®Ø¯Ù…Ø© Ø§Ù„سحابة',
    confirmActivation: 'تأكيد Ø§Ù„تفعيل',
    activationInProgress: 'جاري Ø§Ù„تفعيل...',
    activationFailed: 'فشل Ø§Ù„تفعيل',
    activationSuccessful: 'تم Ø§Ù„تفعيل Ø¨Ù†Ø¬Ø§Ø­',
    passwordModification: 'تغيير ÙƒÙ„مة Ø§Ù„مرور',
    password: 'كلمة Ø§Ù„مرور',
    oldPassword: 'كلمة Ø§Ù„مرور Ø§Ù„قديمة',
    newPassword: 'كلمة Ø§Ù„مرور Ø§Ù„جديدة',
    confirmPassword: 'تأكيد ÙƒÙ„مة Ø§Ù„مرور',
    passwordRule: 'توصيات ÙƒÙ„مة Ø§Ù„مرور',
    passwordLength: 'الطول â‰¥6',
    cannotBeTheSame: 'لا ÙŠÙ…كن Ø£Ù† ØªÙƒÙˆÙ† ÙƒÙ„ Ø§Ù„أحرف Ù…تطابقة',
    cannotOrder: 'لا ÙŠØ´Ù…Ù„ 3+ Ø£Ø±Ù‚ام/حروف Ù…تتالية',
    cannotWeakPassword: 'لا ØªØ³ØªØ®Ø¯Ù… ÙƒÙ„مات Ù…رور Ø¶Ø¹ÙŠÙØ© Ø´Ø§Ø¦Ø¹Ø©',
    submit: 'إرسال',
    saveConfig: 'حفظ Ø§Ù„إعدادات',
    msg_please_enter: 'يرجى Ø§Ù„إدخال',
    msg_inputPassword: 'يرجى Ø¥Ø¯Ø®Ø§Ù„ ÙƒÙ„مة Ø§Ù„مرور',
    msg_oldPasswordError: 'كلمة Ø§Ù„مرور Ø§Ù„قديمة ØºÙŠØ± ØµØ­ÙŠØ­Ø©',
    msg_password_mismatch: 'كلمتا Ø§Ù„مرور ØºÙŠØ± Ù…تطابقتين',
    msg_password_min_length: 'الحد Ø§Ù„أدنى 6 Ø£Ø­Ø±Ù',
    msg_is_weak_password: 'كلمة Ù…رور Ø¶Ø¹ÙŠÙØ©ØŒ ÙŠØ±Ø¬Ù‰ Ø§Ù„تغيير',
    msg_pswChangeSuccessAndLogin: 'تم ØªØºÙŠÙŠØ± ÙƒÙ„مة Ø§Ù„مرور، ÙŠØ±Ø¬Ù‰ ØªØ³Ø¬ÙŠÙ„ Ø§Ù„دخول',
    msg_pswChangeSuccess: 'تم ØªØºÙŠÙŠØ± ÙƒÙ„مة Ø§Ù„مرور',
    msg_pswChangeFail: 'فشل ØªØºÙŠÙŠØ± ÙƒÙ„مة Ø§Ù„مرور',
    msg_saveSuccess: 'تم Ø§Ù„حفظ Ø¨Ù†Ø¬Ø§Ø­',
    msg_saveFail: 'فشل Ø§Ù„حفظ',
    msg_formFilled: 'تحقق Ù…Ù† ØªØ¹Ø¨Ø¦Ø© Ø§Ù„نموذج',
    msg_number_0_23: 'يدعم ÙÙ‚Ø· 0 Ø¥Ù„Ù‰ 23',
    msg_number_0_24: 'يدعم ÙÙ‚Ø· 0 Ø¥Ù„Ù‰ 24',
    msg_noChange: 'لا ØªÙˆØ¬Ø¯ ØªØºÙŠÙŠØ±Ø§Øª ÙÙŠ Ø§Ù„Ø¥Ø¹Ø¯Ø§Ø¯Ø§Øª Ù„لحفظ',
    resourceConfiguration: 'إعدادات Ø§Ù„موارد',
    backgroundImage: 'صورة Ø§Ù„خلفية',
    selectImage: 'اختر ØµÙˆØ±Ø©',
    uploadBackground: 'رفع Ø§Ù„خلفية',
    uploading: 'جارٍ Ø§Ù„رفع...',
    backgroundUploadTip: 'يرجى ØªØ­Ù…يل ØµÙˆØ±Ø© Ø¨ØµÙŠØºØ© PNG Ø¨Ø­Ø¬Ù… Ø§Ù„بكسلات {n}، ÙˆØ³ÙŠØªÙ… ØªØ­ÙˆÙŠÙ„ Ø§Ù„صورة Ø¥Ù„Ù‰ ØµÙŠØºØ© Base64 Ø«Ù… Ù†Ù‚لها Ø¥Ù„Ù‰ Ø§Ù„جهاز',
    backgroundResolutionMismatch: 'يجب Ø£Ù† ØªÙƒÙˆÙ† Ø¯Ù‚Ø© Ø§Ù„صورة {n}',
    backgroundRequired: 'يرجى Ø§Ø®ØªÙŠØ§Ø± ØµÙˆØ±Ø© Ø®Ù„فية',
    backgroundImageOnlyPNG: 'PNG ÙÙ‚Ø·',
    backgroundSizeLimit: 'الحجم Ù„ا ÙŠØªØ¬Ø§ÙˆØ² 5MB',
    backgroundParseFailed: 'فشل Ù‚راءة Ø§Ù„صورة',
    backgroundImageSelected: 'تم Ø§Ø®ØªÙŠØ§Ø± Ø§Ù„صورة',
    backgroundSuccess: 'تم Ø±ÙØ¹ Ø§Ù„خلفية',
    backgroundFailed: 'فشل Ø±ÙØ¹ Ø§Ù„خلفية',
    scanSettings: 'إعدادات Ø§Ù„مسح',
    scanSwitch: 'مفتاح Ø§Ù„مسح',
    scanInterval: 'فاصل Ø§Ù„مسح'
  },
  person: {
    idCard: 'رقم Ø§Ù„هوية',
    userType: 'نوع Ø§Ù„مستخدم',
    administrator: 'مسؤول',
    userId: 'ID',
    user: 'مستخدم',
    voucher: 'بيان Ø§Ø¹ØªÙ…اد',
    permission: 'الصلاحية',
    addUser: 'إضافة Ù…ستخدم',
    name: 'الاسم',
    editUser: 'تعديل Ø§Ù„مستخدم',
    placeholderUserId: 'أدخل Ù…عرف Ø§Ù„مستخدم',
    placeholderName: 'أدخل Ø§Ù„اسم',
    userNotExist: 'المستخدم ØºÙŠØ± Ù…وجود',
    oneClickClear: 'مسح Ø§Ù„كل',
    clearTips: 'سيتم Ø­Ø°Ù Ø¬Ù…يع Ø§Ù„بيانات، Ø§Ù„متابعة؟',
    clearSuccess: 'تم Ø§Ù„مسح Ø¨Ù†Ø¬Ø§Ø­',
    clearFailed: 'فشل Ø§Ù„مسح',
  },
  voucher: {
    password: 'كلمة Ø§Ù„مرور',
    card: 'بطاقة',
    face: 'وجه',
    finger: 'بصمة',
    code: "الرمز",
    codeType: "نوع Ø§Ù„رمز",
    passthroughCode: "رمز Ø§Ù„تمرير",
    staticCode: "رمز Ø«Ø§Ø¨Øª",
    dynamicCode: "رمز Ø¯ÙŠÙ†Ø§Ù…يكي",
    credentialId: 'معرّف Ø¨ÙŠØ§Ù†Ø§Øª Ø§Ù„اعتماد',
    credentialValue: 'قيمة Ø¨ÙŠØ§Ù†Ø§Øª Ø§Ù„اعتماد',
    placeholderCode: "الرجاء Ø¥Ø¯Ø®Ø§Ù„ Ø±Ù…ز Ø§Ù„شهادة",
    placeholderPwd: 'أدخل ÙƒÙ„مة Ø§Ù„مرور',
    placeholderCard: 'أدخل Ø§Ù„بطاقة',
    validPassword: 'أدخل 6 Ø£Ø±Ù‚ام',
    validCard: 'أدخل 8 Ø£Ø±Ù‚ام Ø£Ùˆ Ø£Ø­Ø±Ù',
    photoRegistration: 'تسجيل ØµÙˆØ±Ø©',
    featureValueRegistration: 'تسجيل Ø§Ù„سمات',
    fingerRegistration: 'تسجيل Ø§Ù„بصمة',
    fingerFeatureRegistration: 'تسجيل Ù‚يمة Ø§Ù„سمة',
    fingerInput: 'يرجى ÙˆØ¶Ø¹ Ø¥ØµØ¨Ø¹Ùƒ Ø¹Ù„Ù‰ Ù…اسح Ø§Ù„بصمة',
    fingerRemainingTime: 'الوقت Ø§Ù„متبقي',
    fingerInputting: 'جارٍ Ø§Ù„تسجيل...',
    startFingerInput: 'بدء ØªØ³Ø¬ÙŠÙ„ Ø§Ù„بصمة',
    fingerInputTips: 'يرجى Ø¥Ø¯Ø®Ø§Ù„ Ù‚يمة Ø³Ù…Ø© Ø§Ù„بصمة',
    fingerWaitInput: 'بانتظار Ø§Ù„تسجيل',
    fingerInputNow: 'جارٍ ØªØ³Ø¬ÙŠÙ„ Ø§Ù„بصمة...',
    fingerInputSuccess: 'تم Ø§Ù„تسجيل Ø¨Ù†Ø¬Ø§Ø­',
    fingerInputFailed: 'فشل Ø¨Ø¯Ø¡ ØªØ³Ø¬ÙŠÙ„ Ø§Ù„بصمة',
    fingerReTry: 'فشل ØªØ³Ø¬ÙŠÙ„ Ø§Ù„بصمة، ÙŠØ±Ø¬Ù‰ Ø§Ù„محاولة Ù…رة Ø£Ø®Ø±Ù‰',
    fingerFilled: 'تم ØªØ³Ø¬ÙŠÙ„ Ø§Ù„بصمة Ø¨Ù†Ø¬Ø§Ø­ ÙˆØªÙ… ØªØ¹Ø¨Ø¦Ø© Ù‚يمة Ø§Ù„سمة ØªÙ„قائيًا',
    fingerFailed: 'فشل ØªØ³Ø¬ÙŠÙ„ Ø§Ù„بصمة',
    fingerTimeout: 'انتهت Ø§Ù„مهلة',
    fingerInputTimeout: 'انتهت Ù…هلة ØªØ³Ø¬ÙŠÙ„ Ø§Ù„بصمة، ÙŠØ±Ø¬Ù‰ Ø§Ù„محاولة Ù…رة Ø£Ø®Ø±Ù‰',
    fingerError: 'فشل Ø§Ù„تسجيل',
    fingerInputError: 'فشل ØªØ³Ø¬ÙŠÙ„ Ø§Ù„بصمة، ÙŠØ±Ø¬Ù‰ Ø§Ù„محاولة Ù…رة Ø£Ø®Ø±Ù‰',
    fingerInputed: 'تم ØªØ³Ø¬ÙŠÙ„ Ø§Ù„بصمة',
    fingerReInput: 'إعادة ØªØ³Ø¬ÙŠÙ„ Ø§Ù„بصمة',
  },
  permission: {
    deletePermission: 'حذف Ø§Ù„صلاحية',
    addPermission: 'إضافة ØµÙ„احية',
    permissionId: 'معرّف Ø§Ù„صلاحية',
    userId: 'معرّف Ø§Ù„مستخدم',
    timeRange: 'النطاق Ø§Ù„زمني',
    extra: 'إضافي',
    effectiveType: 'نوع Ø§Ù„فعالية',
    effectiveTime: 'وقت Ø§Ù„فعالية',
    effectiveWeek: 'الأسبوع Ø§Ù„فعال',
    timePeriod: 'الفترة Ø§Ù„زمنية',
    addTimePeriod: 'إضافة ÙØªØ±Ø©',
    modify_previous_time: 'عدّل Ø§Ù„فترة Ø§Ù„سابقة Ø£ÙˆÙ„اً',
    cannot_be_earlier: 'لا ÙŠÙ…كن Ø£Ù† ØªÙƒÙˆÙ† Ø§Ù„نهاية Ù‚بل Ø§Ù„بداية',
    times_cannot_overlap: 'لا ÙŠÙ…كن ØªØ¯Ø§Ø®Ù„ Ø§Ù„أوقات',
    choose_time_range: 'اختر Ù†Ø·Ø§Ù‚ Ø§Ù„زمن',
    unlimitedMode: 'غير Ù…حدود',
    usualMode: 'الوضع Ø§Ù„معتاد',
    dailyMode: 'وضع ÙŠÙˆÙ…ÙŠ',
    weeklyRepetitionMode: 'تكرار Ø£Ø³Ø¨ÙˆØ¹ÙŠ',
    time_range: 'النطاق Ø§Ù„زمني',
  },
  common: {
    startDate: 'تاريخ Ø§Ù„بدء',
    endDate: 'تاريخ Ø§Ù„انتهاء',
    to: 'إلى',
    cancel: 'إلغاء',
    confirm: 'تأكيد',
    close: 'إغلاق',
    delete: 'حذف',
    edit: 'تعديل',
    batchDelete: 'حذف Ø¯ÙØ¹Ø©',
    startTime: 'وقت Ø§Ù„بدء',
    endTime: 'وقت Ø§Ù„انتهاء',
    monday: 'الاثنين',
    tuseday: 'الثلاثاء',
    wednesday: 'الأربعاء',
    thursday: 'الخميس',
    friday: 'الجمعة',
    saterday: 'السبت',
    sunday: 'الأحد',
    placeholder: 'يرجى Ø§Ù„إدخال',
    placeholderSelect: 'يرجى Ø§Ù„اختيار',
    closeTips: 'تأكيد Ø§Ù„إغلاق؟',
    deleteTips: 'تأكيد Ø§Ù„حذف؟',
    deleteSuccess: 'تم Ø§Ù„حذف Ø¨Ù†Ø¬Ø§Ø­',
    addSuccess: 'تمت Ø§Ù„إضافة Ø¨Ù†Ø¬Ø§Ø­',
    editSuccess: 'تم Ø§Ù„تعديل Ø¨Ù†Ø¬Ø§Ø­',
    saveSuccess: 'تم Ø§Ù„حفظ',
    tips: 'تنبيه',
    operation: 'عملية',
    query: 'استعلام',
    reset: 'إعادة',
    noData: 'لا ØªÙˆØ¬Ø¯ Ø¨ÙŠØ§Ù†Ø§Øª',
    export: 'تصدير',
    success: 'نجاح',
    failure: 'فشل',
    incorrectFormat: 'صيغة ØºÙŠØ± ØµØ­ÙŠØ­Ø©',
    integerFormat: 'يجب Ø£Ù† ÙŠÙƒÙˆÙ† Ø¹Ø¯Ø¯Ø§Ù‹ ØµØ­ÙŠØ­Ø§Ù‹ â‰¥0',
    positiveIntegerFormat: 'يجب Ø£Ù† ÙŠÙƒÙˆÙ† Ø¹Ø¯Ø¯Ø§Ù‹ ØµØ­ÙŠØ­Ø§Ù‹ >0',
    noDataSaved: 'لا ØªÙˆØ¬Ø¯ Ø¨ÙŠØ§Ù†Ø§Øª Ù„لحفظ',
    chinese: 'الصينية',
    english: 'الإنجليزية',
    spanish: 'الإسبانية',
    french: 'الفرنسية',
    german: 'الألمانية',
    russian: 'الروسية',
    arabic: 'العربية',
    portuguese: 'البرتغالية',
    korean: 'الكورية',
    detail: "التفاصيل",
    clearTips: "هل Ø£Ù†Øª Ù…تأكد Ù…Ù† Ø§Ù„مسح؟",
    clearSuccess: "تم Ø§Ù„مسح Ø¨Ù†Ø¬Ø§Ø­",
  },
  log: {
    accessMethod: 'طريقة Ø§Ù„دخول',
    passingTime: 'وقت Ø§Ù„مرور',
    accessPass: 'اعتماد Ø§Ù„دخول',
    accessResult: 'النتيجة',
    accessPhoto: 'صورة',
    viewPhotos: 'عرض Ø§Ù„صور'
  },
  error: {
    networkError: 'فشل Ø§Ù„شبكة، ØªØ­Ù‚Ù‚ Ù…Ù† Ø§Ù„اتصال',
    timeout: 'انتهى Ø§Ù„وقت، Ø­Ø§ÙˆÙ„ Ù„احقاً',
    serverError: 'خطأ Ø¯Ø§Ø®Ù„ÙŠ ÙÙŠ Ø§Ù„Ø®Ø§Ø¯Ù…',
    notFound: 'المورد ØºÙŠØ± Ù…وجود',
    unauthorized: 'غير Ù…صرح، ÙŠØ±Ø¬Ù‰ ØªØ³Ø¬ÙŠÙ„ Ø§Ù„دخول',
    noResponse: 'لا ÙŠÙˆØ¬Ø¯ Ø§Ø³ØªØ¬Ø§Ø¨Ø© Ù…Ù† Ø§Ù„خادم',
    unknownError: 'فشل Ø§Ù„طلب، Ø§Ù„رمز:',
    requestFailed: 'فشل Ø§Ù„طلب'
  },
  security: {
    keyId: "معرف Ø§Ù„مفتاح",
    keyType: "نوع Ø§Ù„مفتاح",
    keyEncoding: "ترميز Ø§Ù„مفتاح",
    keyValue: "قيمة Ø§Ù„مفتاح",
    startTime: "وقت Ø§Ù„بدء",
    expirationTime: "وقت Ø§Ù„انتهاء",
    newKey: "إضافة Ù…فتاح Ø¬Ø¯ÙŠØ¯",
    clearKey: "مسح Ø§Ù„مفتاح",
    validTime: "الوقت Ø§Ù„صالح",
  }
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/de.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,433 @@
export default {
  // Login
  login: {
    lang: 'Sprache',
    systemname: 'Login',
    username: 'Benutzername',
    username_label: 'Bitte Benutzername eingeben',
    pwd: 'Passwort',
    pwd_label: 'Bitte Passwort eingeben',
    pwd_info: 'Bitte korrektes Passwortformat eingeben',
    success_msg: 'Erfolgreich angemeldet',
    error_name: 'Falsches Passwort',
    error_res: 'Anmeldeanforderungen nicht erfüllt',
    login: 'Anmelden',
  },
  // Sidebar
  aside: {
    systemname: 'Konsole',
    quit: 'Abmelden',
    deviceControl: 'Steuerung',
    basicSetting: 'Einstellungen',
    workerSetting: 'Personen',
    deviceMonitoring: 'Überwachung',
    recordManagement: 'Aufzeichnungen',
    securityManagement: 'Schlüsselverwaltung',
    tips: 'Hinweis',
    tips_msg: 'Möchten Sie sich abmelden?',
  },
  control: {
    remoteControl: 'Fern',
    restart: 'Neustart',
    clickToRestart: 'Zum Neustart klicken',
    restartConfirm: 'Neustart bestätigen?',
    restartSuccess: 'Neustart erfolgreich',
    restartFailed: 'Neustart fehlgeschlagen',
    remoteOpen: 'Tür fernöffnen',
    clickToOpen: 'Zum Ã–ffnen klicken',
    openConfirm: 'Fernöffnung bestätigen?',
    remoteOpenSuccess: 'Öffnen erfolgreich',
    remoteOpenFailed: 'Öffnen fehlgeschlagen',
    reset: 'Gerät zurücksetzen',
    clickToReset: 'Zum Zurücksetzen klicken',
    resetConfirm: 'Zurücksetzen bestätigen?',
    resetWillOut: "Nach Reset abmelden",
    resetSuccess: 'Zurücksetzen erfolgreich',
    resetFailed: 'Zurücksetzen fehlgeschlagen',
    firmwareUpgrade: 'Firmware-Update',
    upgradeConfig: 'Update-Konfiguration',
    firmwareUrl: 'Firmware-URL',
    md5Checksum: 'md5-Wert',
    startUpgrade: 'Update starten',
    urlRequired: 'URL ist erforderlich',
    md5Required: 'md5 ist erforderlich',
    urlInvalid: 'Bitte gültige URL eingeben',
    md5Invalid: 'Bitte gültiges md5 eingeben',
    upgradeConfirm: 'Update bestätigen?',
    upgradeSuccess: 'Update erfolgreich',
    clearFile: "Datei löschen",
    uploading: 'Hochladen...',
    uploadAndUpgrade: 'Hochladen & Aktualisieren',
    restartTips: 'Sicherer Neustart, keine Daten gehen verloren',
    restarting: 'Neustart...',
    remoteTips: 'Fernsteuerung der Zugangstür',
    opening: 'Öffnet...',
    resetTips: 'Werksreset, löscht alle Daten',
    reseting: 'Setzt zurück...',
    urlUpgrade: 'Update per URL',
    fileUpgrade: 'Update per Datei',
    uploadFile: 'Firmware-Datei hochladen',
    formatFile: 'Unterstützt .zip oder .dpk, max 20MB',
    fileName: 'Dateiname',
    size: 'Größe',
  },
  config: {
    second: 's',
    millisecond: 'ms',
    min: 'Minuten',
    notsave: 'Nicht speichern',
    save: 'Speichern',
    noVoice: 'Keine Stimme',
    no: 'Nein',
    yes: 'Ja',
    basicConfiguration: 'Grundkonfiguration',
    displaySettings: 'Anzeige',
    informationDisplay: 'Infoanzeige',
    audioSettings: 'Audio',
    languageAndThemes: 'Sprache & Thema',
    autoAdjustScreenBrightness: 'Auto-Helligkeit',
    screenBrightness: 'Bildschirmhelligkeit',
    autoTurnOffScreen: 'Auto-Bildschirm aus',
    autoTurnOffScreenTime: 'Zeit bis Ausschalten',
    autoScreenSaver: 'Auto-Bildschirmschoner',
    autoScreenSaverTime: 'Zeit Bildschirmschoner',
    displayDeviceSn: 'SN anzeigen',
    displayIp: 'IP anzeigen',
    displayIdentityCard: 'Cloud-Zertifikat anzeigen',
    volume: 'Lautstärke',
    language: 'Sprache',
    displayCode: "Mini-App-Code anzeigen",
    themeMode: "Thema",
    cn: 'Chinesisch',
    en: 'Englisch',
    es: 'Spanisch',
    fr: 'Französisch',
    de: 'Deutsch',
    ru: 'Russisch',
    ar: 'Arabisch',
    pt: 'Port.',
    ko: 'Koreanisch',
    standardMode: 'Standardmodus',
    simpleMode: 'Einfacher Modus',
    firstLogin: 'Erste Anmeldung',
    backlight: 'Hintergrundlicht',
    brightness: 'Weißlicht',
    nirBrightness: 'IR-Licht',
    never: 'Nie',
    min1: '1 Minute',
    min2: '2 Minuten',
    min3: '3 Minuten',
    min4: '4 Minuten',
    min5: '5 Minuten',
    networkConfiguration: 'Netzwerk',
    otherConfiguration: 'Andere',
    ipConfiguration: 'IP-Konfiguration',
    devicePassword: 'Gerätepasswort',
    protocolPassword: 'Protokollpasswort',
    networkType: 'Netzwerktyp',
    ethernet: 'Ethernet',
    wifiName: 'Wi‑Fi',
    wifiPassword: 'Wi‑Fi-Passwort',
    dhcpModeSelection: 'DHCP-Modus',
    dhcpMode: 'Automatisch',
    customNetworkConfiguration: 'Manuell',
    ipAddress: 'IP-Adresse',
    gateway: 'Gateway',
    subnetMask: 'Subnetzmaske',
    dnsServer: 'DNS-Server',
    mac: 'Netzwerk-MAC',
    mqttRelatedConfiguration: 'MQTT',
    mqttConnectionInformation: 'MQTT-Verbindung',
    sessionConfiguration: 'Sitzung',
    serverAddress: 'Serveradresse',
    clientID: 'Client-ID',
    userName: 'Benutzername',
    userPassword: 'Passwort',
    topicPrefix: 'Topic-Präfix',
    onlineChecking: 'Online-Prüfung',
    onlineCheckingTimeout: 'Zeitüberschreitung',
    cleanSession: 'Saubere Sitzung',
    clientIdSuffix: 'Client-ID-Suffix',
    willTopic: 'Will-Topic',
    enterpriseWechat:'Enterprise WeChat Modus ohne Wirkung',
    faceRelatedConfiguration: 'Gesichtskonfiguration',
    functionalInformation: 'Funktion',
    prompt: 'Hinweis',
    faceSimilarityThreshold: 'Ähnlichkeitsschwelle',
    livenessDetectionFunction: 'Lebenderkennung',
    livenessDetectionThreshold: 'Schwelle Lebendigkeit',
    infraredImageDisplay: "Infrarotbild",
    maskRecognition: "Maskenerkennung",
    strangerVoice: "Stimme Unbekannt",
    voiceMode: "Sprachmodus",
    voiceModeDate: 'Benutzerdefinierter Gruß',
    imageSaveType: "Speichertyp",
    saveStrangerImage: "Bild von Unbekannt speichern",
    fullView: "Panorama",
    face: "Gesicht",
    broadcastPleaseRegisterFirst: 'Spiele "Bitte zuerst registrieren"',
    broadcastHelloStranger: 'Spiele "Unbekannt"',
    broadcastName: 'Namen abspielen',
    broadcastGreeting: 'Benutzerdefinierten Gruß abspielen',
    greeting: 'Gruß',
    broadcastWelcome: '"Willkommen" abspielen',
    recognitionSwitch: 'Erneute Erkennung Schalter',
    systemRelatedConfiguration: 'System',
    functionSwitch: 'Funktionsschalter',
    cardSwipingSwitch: 'Karte',
    passwordSwitch: 'Passwort',
    strangerImage: 'Bild Unbekannter',
    cloudCertificateSwitch: 'Cloud-Zertifikat',
    physicalCardNumber: 'Physische Kartennr.',
    cloudCertificateAcquisition: 'Cloud-Zertifikat Bezug',
    heartbeatConfig: 'Heartbeat',
    heartbeatSwitch: 'Heartbeat Schalter',
    heartRateInterval: 'Intervall',
    heartbeatTopic: 'Heartbeat-Topic',
    heartbeatContent: 'Heartbeat-Inhalt',
    basicInformation: 'Basisinfo',
    deviceMac: 'MAC-Adresse',
    uuid: 'UUID',
    sn: 'SN',
    model: 'Modell',
    version: "Version",
    appVersion: "Firmware-Version",
    releaseTime: "Aktualisierungszeit",
    totaldisk: 'Gesamtbereich',
    freedisk: 'Verbleibender Platz',
    passageConfiguration: 'Passage',
    functionConfiguration: 'Funktionen',
    numberOfPassageRecords: 'Max. Durchgangsaufzeichnungen',
    durationOfRelayOpening: 'Relais-Öffnungsdauer',
    alarmSwitch: 'Alarm',
    fireAlarmSwitch: 'Brandalarm',
    fireAlarmStatus: 'Brandstatus',
    normal: 'Normal',
    warning: 'Warnung',
    tamperSwitch: 'Sabotageschutz',
    uploadToCloudSwitch: 'Gesicht-Upload-Schalter',
    clockConfiguration: 'Uhr',
    timeSynchronizationSwitch: 'Zeitsynchronisation',
    timeSynchronizationServerIP: 'Zeitserver-IP',
    timedSynchronizationTime: 'Synchronisationszeit',
    timeZone: 'Zeitzone',
    setDeviceTime: 'Gerätezeit setzen',
    restartAfterSetting: 'Gerät startet neu',
    cloudCertificateActivation: 'Cloud-Zertifikat Aktivierung',
    activationKey: 'Aktivierungsschlüssel',
    cloudTips1: 'Schlüssel ohne Leerzeichen eingeben',
    cloudTips2: 'Nach Aktivierung verbindet sich das Gerät mit dem Cloud-Service',
    confirmActivation: 'Aktivierung bestätigen',
    activationInProgress: 'Aktivierung läuft...',
    activationFailed: 'Aktivierung fehlgeschlagen',
    activationSuccessful: 'Aktivierung erfolgreich',
    passwordModification: 'Passwort Ã¤ndern',
    password: 'Passwort',
    oldPassword: 'Altes Passwort',
    newPassword: 'Neues Passwort',
    confirmPassword: 'Passwort bestätigen',
    passwordRule: 'Passwortempfehlung',
    passwordLength: 'Länge â‰¥6',
    cannotBeTheSame: 'Alle Zeichen dürfen nicht gleich sein',
    cannotOrder: 'Keine 3+ aufeinanderfolgende Zahlen/Buchstaben',
    cannotWeakPassword: 'Kein schwaches Passwort',
    submit: 'Senden',
    saveConfig: 'Einstellungen speichern',
    msg_please_enter: 'Bitte eingeben',
    msg_inputPassword: 'Bitte Passwort eingeben',
    msg_oldPasswordError: 'Altes Passwort falsch',
    msg_password_mismatch: 'Passwörter stimmen nicht Ã¼berein',
    msg_password_min_length: 'Mindestens 6 Zeichen',
    msg_is_weak_password: 'Schwaches Passwort, bitte Ã¤ndern',
    msg_pswChangeSuccessAndLogin: 'Passwort geändert, bitte neu anmelden',
    msg_pswChangeSuccess: 'Passwort geändert',
    msg_pswChangeFail: 'Passwortänderung fehlgeschlagen',
    msg_saveSuccess: 'Erfolgreich gespeichert',
    msg_saveFail: 'Speichern fehlgeschlagen',
    msg_formFilled: 'Bitte Formular prüfen',
    msg_number_0_23: 'Nur 0 bis 23',
    msg_number_0_24: 'Nur 0 bis 24',
    msg_noChange: 'Keine Konfigurationsänderungen zu speichern',
    resourceConfiguration: 'Ressourcenkonfiguration',
    backgroundImage: 'Hintergrundbild',
    selectImage: 'Bild auswählen',
    uploadBackground: 'Hintergrund hochladen',
    uploading: 'Lädt hoch...',
    backgroundUploadTip: 'Bitte laden Sie ein PNG-Bild mit einer Auflösung von {n} Pixeln hoch. Das Bild wird in Base64-Format konvertiert und auf das Gerät hochgeladen',
    backgroundResolutionMismatch: 'Die Bildauflösung muss {n} sein',
    backgroundRequired: 'Bitte ein Hintergrundbild wählen',
    backgroundImageOnlyPNG: 'Nur PNG-Bilder',
    backgroundSizeLimit: 'Bild darf 5MB nicht Ã¼berschreiten',
    backgroundParseFailed: 'Bild konnte nicht gelesen werden',
    backgroundImageSelected: 'Bild ausgewählt',
    backgroundSuccess: 'Hintergrund hochgeladen',
    backgroundFailed: 'Upload fehlgeschlagen',
    scanSettings: 'Scan-Einstellungen',
    scanSwitch: 'Scan-Schalter',
    scanInterval: 'Scan-Intervall',
  },
  person: {
    idCard: 'Ausweisnummer',
    userType: 'Personentyp',
    administrator: 'Administrator',
    userId: 'ID',
    user: 'Benutzer',
    voucher: 'Nachweis',
    permission: 'Berechtigung',
    addUser: 'Person hinzufügen',
    name: 'Name',
    editUser: 'Person bearbeiten',
    placeholderUserId: 'Benutzer-ID eingeben',
    placeholderName: 'Name eingeben',
    userNotExist: 'Person existiert nicht',
    oneClickClear: 'Alles löschen',
    clearTips: 'Dies löscht alle Daten, fortfahren?',
    clearSuccess: 'Löschen erfolgreich',
    clearFailed: 'Löschen fehlgeschlagen',
  },
  voucher: {
    password: 'Passwort',
    card: 'Karte',
    face: 'Gesicht',
    finger: 'Fingerabdruck',
    code: 'Code',
    codeType: 'Codetyp',
    passthroughCode: 'Durchgangscode',
    staticCode: 'Statischer Code',
    dynamicCode: 'Dynamischer Code',
    credentialId: 'Anmelde-ID',
    credentialValue: 'Anmeldewert',
    placeholderCode: 'Bitte Codezertifikat eingeben',
    placeholderPwd: 'Passwortnachweis eingeben',
    placeholderCard: 'Kartennachweis eingeben',
    validPassword: 'Bitte 6 Ziffern eingeben',
    validCard: 'Bitte 8 Ziffern oder Buchstaben eingeben',
    photoRegistration: 'Foto-Registrierung',
    featureValueRegistration: 'Merkmals-Registrierung',
    fingerRegistration: 'Fingerabdruck-Registrierung',
    fingerFeatureRegistration: 'Registrierung Ã¼ber Merkmalswert',
    fingerInput: 'Bitte legen Sie den Finger auf den Fingerabdruckscanner',
    fingerRemainingTime: 'Verbleibende Zeit',
    fingerInputting: 'Wird erfasst...',
    startFingerInput: 'Fingerabdruck erfassen',
    fingerInputTips: 'Bitte Fingerabdruck-Merkmalswert eingeben',
    fingerWaitInput: 'Warten auf Erfassung',
    fingerInputNow: 'Fingerabdruck wird erfasst...',
    fingerInputSuccess: 'Erfassung erfolgreich',
    fingerInputFailed: 'Start der Fingerabdruckerfassung fehlgeschlagen',
    fingerReTry: 'Fingerabdruckerfassung fehlgeschlagen, bitte erneut versuchen',
    fingerFilled: 'Erfassung erfolgreich, Merkmalswert wurde automatisch eingetragen',
    fingerFailed: 'Fingerabdruckerfassung fehlgeschlagen',
    fingerTimeout: 'Zeitüberschreitung',
    fingerInputTimeout: 'Zeitüberschreitung bei der Fingerabdruckerfassung, bitte erneut versuchen',
    fingerError: 'Erfassung fehlgeschlagen',
    fingerInputError: 'Fingerabdruckerfassung fehlgeschlagen, bitte erneut versuchen',
    fingerInputed: 'Fingerabdruck bereits erfasst',
    fingerReInput: 'Fingerabdruck erneut erfassen',
  },
  permission: {
    deletePermission: 'Berechtigung löschen',
    addPermission: 'Berechtigung hinzufügen',
    permissionId: 'Berechtigungs-ID',
    userId: 'Benutzer-ID',
    timeRange: 'Zeitraum',
    extra: 'Extra',
    effectiveType: 'Gültigkeitstyp',
    effectiveTime: 'Gültige Zeit',
    effectiveWeek: 'Gültige Woche',
    timePeriod: 'Zeitabschnitt',
    addTimePeriod: 'Zeitabschnitt hinzufügen',
    modify_previous_time: 'Vorherigen Abschnitt zuerst Ã¤ndern',
    cannot_be_earlier: 'Endzeit darf nicht vor Startzeit liegen',
    times_cannot_overlap: 'Zeiten dürfen sich nicht Ã¼berschneiden',
    choose_time_range: 'Zeitraum wählen',
    unlimitedMode: 'Unbegrenzt',
    usualMode: 'Normalmodus',
    dailyMode: 'Täglich',
    weeklyRepetitionMode: 'Wöchentliche Wiederholung',
    time_range: 'Zeitbereich',
  },
  common: {
    startDate: 'Startdatum',
    endDate: 'Enddatum',
    to: 'bis',
    cancel: 'Abbrechen',
    confirm: 'Bestätigen',
    close: 'Schließen',
    delete: 'Löschen',
    edit: 'Bearbeiten',
    batchDelete: 'Stapel löschen',
    startTime: 'Startzeit',
    endTime: 'Endzeit',
    monday: 'Montag',
    tuseday: 'Dienstag',
    wednesday: 'Mittwoch',
    thursday: 'Donnerstag',
    friday: 'Freitag',
    saterday: 'Samstag',
    sunday: 'Sonntag',
    placeholder: 'Bitte eingeben',
    placeholderSelect: 'Bitte wählen',
    closeTips: 'Schließen bestätigen?',
    deleteTips: 'Löschen bestätigen?',
    deleteSuccess: 'Erfolgreich gelöscht',
    addSuccess: 'Erfolgreich hinzugefügt',
    editSuccess: 'Erfolgreich bearbeitet',
    saveSuccess: 'Erfolgreich gespeichert',
    tips: 'Hinweis',
    operation: 'Operation',
    query: 'Abfrage',
    reset: 'Reset',
    noData: 'Keine Daten',
    export: 'Exportieren',
    success: 'Erfolg',
    failure: 'Fehler',
    incorrectFormat: 'Falsches Format',
    integerFormat: 'Ganzzahl â‰¥0 erforderlich',
    positiveIntegerFormat: 'Ganzzahl >0 erforderlich',
    noDataSaved: 'Keine Daten zu speichern',
    chinese: 'Chinesisch',
    english: 'Englisch',
    spanish: 'Spanisch',
    french: 'Französisch',
    german: 'Deutsch',
    russian: 'Russisch',
    arabic: 'Arabisch',
    portuguese: 'Portugiesisch',
    korean: 'Koreanisch',
    detail: 'Details',
    clearTips: 'Wirklich leeren?​',
    clearSuccess: 'Erfolgreich gelöscht',
  },
  log: {
    accessMethod: 'Zugriffsmethode',
    passingTime: 'Durchgangszeit',
    accessPass: 'Zugangsnachweis',
    accessResult: 'Ergebnis',
    accessPhoto: 'Foto',
    viewPhotos: 'Fotos ansehen'
  },
  error: {
    networkError: 'Netzwerkfehler, Verbindung prüfen',
    timeout: 'Zeitüberschreitung, bitte erneut versuchen',
    serverError: 'Serverfehler, später erneut versuchen',
    notFound: 'Ressource nicht gefunden',
    unauthorized: 'Nicht autorisiert, bitte neu anmelden',
    noResponse: 'Keine Serverantwort, Netzwerk prüfen',
    unknownError: 'Anfrage fehlgeschlagen, Code:',
    requestFailed: 'Anfrage fehlgeschlagen'
  },
  security: {
    keyId: 'Schlüssel-ID',
    keyType: 'Schlüsseltyp',
    keyEncoding: 'Schlüsselkodierung',
    keyValue: 'Schlüsselwert',
    startTime: 'Startzeit',
    expirationTime: 'Ablaufzeit',
    newKey: 'Neuer Schlüssel',
    clearKey: 'Schlüssel löschen',
    validTime: 'Gültigkeitsdauer',
  }
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/en.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,450 @@
export default {
  // ç™»å½•页
  login: {
    lang: 'language',
    systemname: 'Face Login',
    username: 'username',
    username_label: 'Please input one user name',
    pwd: 'password',
    pwd_label: 'Please input a password',
    pwd_info: 'Please enter the correct password format',
    success_msg: 'Login successfully',
    error_name: 'Wrong password',
    error_res: 'Does not meet the login requirements',
    login: 'Login',
  },
  // ä¾§è¾¹æ éƒ¨åˆ†
  aside: {
    systemname: 'Face System',
    help: 'Help',
    administrator: 'Administrator',
    quit: 'Quit',
    deviceControl: 'Control',
    basicSetting: 'Settings',
    workerSetting: 'Personnel',
    deviceMonitoring: 'Monitoring',
    recordManagement: 'Records',
    securityManagement: 'Key',
    tips: 'Tips',
    tips_msg: 'Do you want to log out',
  },
  // è®¾å¤‡æŽ§åˆ¶é¡µé¢
  control: {
    remoteControl: 'Remote',
    restart: 'Restart',
    clickToRestart: 'Click to restart the device',
    restartConfirm: 'Are you sure you want to restart the device?',
    restartSuccess: 'Successful restart',
    restartFailed: 'Failed to restart',
    remoteOpen: 'Remote door opening',
    clickToOpen: 'Click remote door opening',
    openConfirm: 'Are you sure you want to open the door remotely?',
    remoteOpenSuccess: 'Remote door opening successful',
    remoteOpenFailed: 'Remote door opening failed',
    reset: 'Device reset',
    clickToReset: 'Click reset device',
    resetConfirm: 'Are you sure you want to reset the device?',
    resetWillOut: "Log out after reset.",
    resetSuccess: 'The device reset was successful',
    resetFailed: 'Device reset failed',
    firmwareUpgrade: 'Equipment upgrade',
    upgradeConfig: 'Upgrade configuration',
    firmwareUrl: 'Firmware address',
    md5Checksum: 'md5 value',
    startUpgrade: 'Start upgrading',
    urlRequired: 'The file address is mandatory',
    md5Required: 'The md5 value is required',
    urlInvalid: 'Please enter the correct address',
    md5Invalid: 'Please enter the correct md5',
    upgradeConfirm: 'Are you sure you want to upgrade the equipment?',
    upgradeSuccess: 'The equipment upgrade was successful.',
    clearFile: "Clear the file",
    uploading: 'Uploading',
    uploadAndUpgrade: 'Upload and upgrade',
    restartTips: 'Safely restart the device system without losing data',
    restarting: 'Restarting...',
    remoteTips: 'Remotely control the access control device to open',
    opening: 'Opening...',
    resetTips: 'Restore factory Settings and clear all data',
    reseting: 'Reseting...',
    urlUpgrade: 'URL upgrade method',
    fileUpgrade: 'File upload upgrade',
    uploadFile: 'Click to upload the firmware file',
    formatFile: 'Supports.zip or.dpk format files, with a maximum size of 20MB',
    fileName: 'File name',
    size: 'Size',
  },
  config: {
    second: 's',
    millisecond: 'Milliseconds',
    min: 'Minutes',
    notsave: 'Not saved',
    save: 'Save',
    noVoice: 'No voice',
    no: 'NO',
    yes: 'YES',
    // åŸºç¡€é…ç½®
    basicConfiguration: 'Basic configuration',
    informationDisplay: 'Information display',
    audioSettings: 'Audio Settings',
    displaySettings: 'Display Settings',
    languageAndThemes: 'Language and Themes',
    autoAdjustScreenBrightness: 'Automatically adjust the screen brightness',
    screenBrightness: 'Screen brightness',
    autoTurnOffScreen: 'Automatic screen-off',
    autoTurnOffScreenTime: 'Automatic screen-off time',
    autoScreenSaver: 'Automatic screen saver',
    autoScreenSaverTime: 'Automatic screen saver time',
    displayDeviceSn: 'Display SN',
    displayIp: 'Display IP',
    displayIdentityCard: 'Display Cloud Certificate',
    volume: 'Volume',
    language: 'Language',
    displayCode: "Display the mini-program code",
    themeMode: "Work theme",
    cn: 'Chinese',
    en: 'English',
    es: 'Spanish',
    fr: 'French',
    de: 'German',
    ru: 'Russian',
    ar: 'Arabic',
    pt: 'Port.',
    ko: 'Korean',
    standardMode: 'Standard Mode',
    simpleMode: 'Simple Mode',
    firstLogin: 'This is my first time logging into the backend',
    backlight: 'Screen backlight',
    brightness: 'White fill light',
    nirBrightness: 'Infrared supplementary light',
    never: 'Never',
    min1: '1 Min',
    min2: '2 Min',
    min3: '3 Min',
    min4: '4 Min',
    min5: '5 Min',
    // ç½‘络配置
    networkConfiguration: 'Network configuration',
    otherConfiguration: 'Other configurations',
    ipConfiguration: 'IP configuration',
    devicePassword: 'Device password ',
    protocolPassword: 'Communication protocol password',
    networkType: 'Network type ',
    ethernet: 'Ethernet',
    wifiName: 'Wi-Fi name',
    wifiPassword: 'Wi-Fi password',
    dhcpModeSelection: 'DHCP mode',
    dhcpMode: 'Automatic acquisition',
    customNetworkConfiguration: 'Manual configuration',
    ipAddress: 'IP address',
    gateway: 'Gateway',
    subnetMask: 'Subnet mask',
    dnsServer: 'DNS server',
    mac: 'Mac address',
    // MQTT配置
    mqttRelatedConfiguration: 'MQTT configuration',
    mqttConnectionInformation: 'MQTT connection information',
    sessionConfiguration: 'Session configuration',
    serverAddress: 'Server address',
    clientID: 'Client ID',
    userName: 'User name',
    userPassword: 'User password',
    topicPrefix: 'Theme prefix',
    onlineChecking: 'Online verification',
    onlineCheckingTimeout: "Online verification timeout",
    cleanSession: 'Clear the session',
    clientIdSuffix: 'ClientId suffix',
    willTopic: 'WillTopic',
    enterpriseWechat:'The Enterprise wechat mode is ineffective',
    // äººè„¸é…ç½®
    faceRelatedConfiguration: 'Face configuration',
    functionalInformation: 'Functional information',
    prompt: 'Prompt',
    faceSimilarityThreshold: 'Face similarity threshold',
    livenessDetectionFunction: 'Live detection function',
    livenessDetectionThreshold: 'Live detection threshold',
    infraredImageDisplay: "Infrared image display",
    maskRecognition: "Mask recognition",
    strangerVoice: "Stranger's voice",
    voiceMode: "Voice mode",
    voiceModeDate: 'Customized greeting',
    imageSaveType: "Image saving type",
    saveStrangerImage: "Save images of strangers",
    fullView: "Panoramic view",
    face: "Face",
    broadcastPleaseRegisterFirst: 'Play please register for a facial recognition voucher',
    broadcastHelloStranger: 'Play unregistered personnel',
    broadcastName: 'Play the name',
    broadcastGreeting: 'Play a custom greeting',
    greeting: 'Greeting',
    broadcastWelcome: 'Play welcome',
    recognitionSwitch: 'Re-detection switch',
    // ç³»ç»Ÿé…ç½®
    systemRelatedConfiguration: 'System configuration',
    functionSwitch: 'Function switch',
    cardSwipingSwitch: 'Card swiping switch',
    passwordSwitch: 'Password switch',
    cloudCertificateSwitch: 'Cloud Certificate switch',
    strangerImage: 'Stranger save picture switch',
    physicalCardNumber: 'Physical card number',
    cloudCertificateAcquisition: 'Cloud Certificate Acquisition',
    heartbeatConfig: 'Heartbeat config',
    heartbeatSwitch: 'Heartbeat switch',
    heartRateInterval: 'Heart rate interval',
    heartbeatTopic: 'Heartbeat Topic',
    heartbeatContent: 'Heartbeat content',
    basicInformation: 'Basic information',
    deviceMac: 'Device Mac',
    uuid: 'UUID',
    sn: 'SN',
    model: 'Model',
    version: "Version",
    appVersion: "Firmware version",
    releaseTime: "Update time",
    totaldisk: 'Total storage space',
    freedisk: 'Remaining space',
    // é€šè¡Œé…ç½®
    passageConfiguration: 'Passage configuration',
    functionConfiguration: 'Function Configuration',
    numberOfPassageRecords: 'The maximum number of passage records',
    durationOfRelayOpening: 'Duration of relay opening',
    alarmSwitch: 'Alarm switch',
    fireAlarmSwitch: 'Fire alarm switch',
    fireAlarmStatus: 'Fire alarm status',
    normal: 'Normal',
    warning: 'Warning',
    tamperSwitch: 'Tamper switch',
    uploadToCloudSwitch: 'Face upload switch',
    // æ—¶é’Ÿé…ç½®
    clockConfiguration: 'Clock configuration',
    timeSynchronizationSwitch: 'Time synchronization switch',
    timeSynchronizationServerIP: 'Time synchronization server IP',
    timedSynchronizationTime: 'Timed synchronization time',
    timeZone: 'Time zone',
    setDeviceTime: 'Set device time',
    restartAfterSetting: 'After setting, the device will automatically restart',
    // äº‘证激活
    cloudCertificateActivation: 'Cloud Certificate Activation',
    activationKey: 'Activation key',
    cloudTips1: 'Please enter the activation key and make sure there are no Spaces',
    cloudTips2: 'After successful activation, the device will connect to the cloud authentication service',
    confirmActivation: 'Confirm activation',
    activationInProgress: 'Activation in progress...',
    activationFailed: 'Activation failed',
    activationSuccessful: 'Activation successful',
    // å¯†ç ä¿®æ”¹
    passwordModification: 'Password modification',
    password: 'Password',
    oldPassword: 'Old password',
    newPassword: 'New password',
    confirmPassword: 'Confirm password',
    passwordRule: 'Password rule recommendation',
    passwordLength: 'Length â‰¥6',
    cannotBeTheSame: 'All characters cannot be the same',
    cannotOrder: 'It cannot contain at least three consecutive numbers or sequences of lowercase letters (in ascending or descending order).',
    cannotWeakPassword: 'It cannot be common weak passwords, including',
    submit: 'Submit',
    saveConfig: 'Save config',
    msg_please_enter: 'Please enter the content',
    msg_inputPassword: 'Please enter the password.',
    msg_oldPasswordError: 'The old password is incorrect',
    msg_password_mismatch: 'The passwords entered twice are inconsistent',
    msg_password_min_length: 'The password length should be at least 6 characters',
    msg_is_weak_password: 'This is a weak password. Please reset it',
    msg_pswChangeSuccessAndLogin: 'Your password has been modified successfully. Please log in again',
    msg_pswChangeSuccess: 'The password has been modified successfully.',
    msg_pswChangeFail: 'Password modification failed.',
    msg_saveSuccess: 'Saved successfully',
    msg_saveFail: 'Save failed',
    msg_formFilled: 'Please check whether the form is filled out correctly',
    msg_number_0_23: 'Only supports 0 to 23',
    msg_number_0_24: 'Only supports 0 to 24',
    msg_noChange: 'No configuration changes need to be saved',
    // Resource configuration
    resourceConfiguration: 'Resource configuration',
    backgroundImage: 'Background image',
    selectImage: 'Select image',
    uploadBackground: 'Upload background',
    uploading: 'Uploading...',
    backgroundUploadTip: 'Please upload a PNG image with pixel size of {n}. The image will be converted to Base64 format and then uploaded to the device',
    backgroundResolutionMismatch: 'Image resolution must be {n}',
    backgroundRequired: 'Please select a background image first',
    backgroundImageOnlyPNG: 'Please upload a PNG image',
    backgroundSizeLimit: 'Image size cannot exceed 5MB',
    backgroundParseFailed: 'Failed to read image, please retry',
    backgroundImageSelected: 'Image selected successfully',
    backgroundSuccess: 'Background uploaded successfully',
    backgroundFailed: 'Background upload failed',
    scanSettings: 'Scan Settings',
    scanSwitch: 'Scan Switch',
    scanInterval: 'Scan Interval',
  },
  // äººå‘˜è®¾ç½®
  person: {
    idCard: 'Id number',
    userType: 'Type',
    userId: 'ID',
    name: 'name',
    user: 'User',
    administrator: 'Administrator',
    voucher: 'Voucher',
    permission: 'Permission',
    placeholderUserId: 'Please enter the userId',
    placeholderName: 'Please enter the name',
    addUser: 'Add user',
    editUser: 'Edit user',
    userNotExist: 'The personnel do not exist',
    oneClickClear: 'One-click clear',
    clearTips: 'This operation will permanently delete all personnel, credentials and permission data. Do you want to continue?',
    clearSuccess: 'Cleared successfully',
    clearFailed: 'Failed to clear',
  },
  voucher: {
    password: 'Password',
    card: 'Card',
    face: 'Face',
    finger: 'Finger',
    code: 'Code',
    codeType: 'Code Type',
    passthroughCode: 'Passthrough Code',
    staticCode: 'Static Code',
    dynamicCode: 'Dynamic Code',
    credentialId: 'Credential ID',
    credentialValue: 'Credential Value',
    placeholderCode: 'Please enter code certificate',
    placeholderPwd: 'Please enter the password credential',
    placeholderCard: 'Please enter the card voucher',
    validPassword: 'Please enter six digits',
    validCard: 'Please enter 8 digits or letters',
    photoRegistration: 'Photo registration',
    featureValueRegistration: 'Feature value registration',
    fingerRegistration: 'Finger registration',
    fingerFeatureRegistration: 'Feature value registration',
    fingerInput: 'Please put your finger on the fingerprint scanner',
    fingerRemainingTime: 'Remaining time',
    fingerInputting: 'Inputting...',
    startFingerInput: 'Start inputting fingerprint',
    fingerInputTips: 'Please enter the fingerprint feature value',
    fingerWaitInput: 'Waiting for input',
    fingerInputNow: 'Inputting fingerprint...',
    fingerInputSuccess: 'Input success',
    fingerInputFailed: 'Finger input failed',
    fingerReTry: 'Finger input failed, please try again',
    fingerFilled: 'Finger input success, feature value has been automatically filled',
    fingerFailed: 'Finger input failed',
    fingerTimeout: 'Input timeout',
    fingerInputTimeout: 'Finger input timeout, please try again',
    fingerError: 'Input failed',
    fingerInputError: 'Finger input failed, please try again',
    fingerInputed: 'Fingerprint has been entered',
    fingerReInput: 'Re enter fingerprint',
  },
  permission: {
    deletePermission: 'Delete permission',
    addPermission: 'Add permissions',
    permissionId: 'Permission ID',
    userId: 'User ID',
    timeRange: 'Time interval',
    extra: 'Extra',
    effectiveType: 'Effective Type',
    effectiveTime: 'Effective Time',
    effectiveWeek: 'Effective Week',
    timePeriod: 'Time period',
    addTimePeriod: 'Add a time period',
    modify_previous_time: 'Please modify the previously added time period first',
    cannot_be_earlier: 'The end time must not be less than the start time',
    times_cannot_overlap: 'The selected times cannot overlap',
    choose_time_range: 'Please select the effective time range',
    unlimitedMode: 'Unlimited mode',
    usualMode: 'Usual mode',
    dailyMode: 'Daily mode',
    weeklyRepetitionMode: 'Weekly repetition mode',
    time_range: 'Time range',
  },
  common: {
    startDate: 'Start date',
    endDate: 'End date',
    to: 'to',
    cancel: 'Cancel',
    confirm: 'Confirm',
    close: 'Close',
    delete: 'Delete',
    edit: 'Edit',
    batchDelete: 'Batch Delete',
    startTime: 'Start time',
    endTime: 'End time',
    monday: 'Monday',
    tuseday: 'Tuseday',
    wednesday: 'Wednesday',
    thursday: 'Thursday',
    friday: 'Friday',
    saterday: 'Saterday',
    sunday: 'Sunday',
    placeholder: 'Please enter',
    placeholderSelect: 'Please select',
    closeTips: 'Is it confirmed to be closed?',
    deleteTips: 'Are you sure to delete it?',
    deleteSuccess: 'Deleted successfully',
    addSuccess: 'Added successfully',
    editSuccess: 'Edited successfully',
    saveSuccess: 'Saved successfully',
    tips: 'Tips',
    operation: 'Operation',
    query: 'Query',
    reset: 'Reset',
    noData: 'No data available for the time being.',
    export: 'Export',
    success: 'success',
    failure: 'failure',
    incorrectFormat: 'Incorrect format',
    integerFormat: 'Should be an integer greater than or equal to 0',
    positiveIntegerFormat: 'Should be an integer greater than 0',
    noDataSaved: 'No data needs to be saved',
    chinese: 'Chinese',
    english: 'English',
    spanish: 'Spanish',
    french: 'French',
    german: 'German',
    russian: 'Russian',
    arabic: 'Arabic',
    portuguese: 'Portuguese',
    korean: 'Korean',
    detail: 'Detail',
    clearTips: 'Confirm clear?',
    clearSuccess: 'Cleared successfully',
  },
  log: {
    accessMethod: 'Access method',
    passingTime: 'Passing time',
    accessPass: 'Access pass',
    accessResult: 'Access result',
    accessPhoto: 'Access photo',
    viewPhotos: 'View photos'
  },
  error: {
    networkError: 'Network request failed, please check your connection',
    timeout: 'Request timeout, please check your network or try again later',
    serverError: 'Server internal error, please try again later',
    notFound: 'Requested resource does not exist',
    unauthorized: 'Unauthorized, please login again',
    noResponse: 'Unable to connect to server, please check network or server status',
    unknownError: 'Request failed, error code:',
    requestFailed: 'Request failed'
  },
  security: {
    keyId: 'Key ID',
    keyType: 'Key Type',
    keyEncoding: 'Key Encoding',
    keyValue: 'Key Value',
    startTime: 'Start Time',
    expirationTime: 'Expiration Time',
    newKey: 'Add Key',
    clearKey: 'Clear Key',
    validTime: 'Valid Time',
  }
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/es.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,452 @@
export default {
  // Inicio de sesión
  login: {
    lang: 'Idioma',
    systemname: 'Login Facial',
    username: 'Usuario',
    username_label: 'Por favor ingrese el usuario',
    pwd: 'Contraseña',
    pwd_label: 'Por favor ingrese la contraseña',
    pwd_info: 'Ingrese el formato correcto de contraseña',
    success_msg: 'Inicio de sesión exitoso',
    error_name: 'Contraseña incorrecta',
    error_res: 'No cumple con los requisitos de inicio de sesión',
    login: 'Iniciar sesión',
  },
  // Barra lateral
  aside: {
    systemname: 'Panel Facial',
    quit: 'Salir',
    deviceControl: 'Control',
    basicSetting: 'Config.',
    workerSetting: 'Personal',
    deviceMonitoring: 'Monitor',
    recordManagement: 'Registros',
    securityManagement: 'La clave',
    tips: 'Aviso',
    tips_msg: '¿Desea cerrar sesión?',
  },
  // Control de dispositivo
  control: {
    remoteControl: 'Remoto',
    restart: 'Reiniciar',
    clickToRestart: 'Clic para reiniciar',
    restartConfirm: '¿Seguro que desea reiniciar?',
    restartSuccess: 'Reinicio exitoso',
    restartFailed: 'Reinicio fallido',
    remoteOpen: 'Abrir puerta de forma remota',
    clickToOpen: 'Clic para abrir',
    openConfirm: '¿Seguro que desea abrir remotamente?',
    remoteOpenSuccess: 'Apertura exitosa',
    remoteOpenFailed: 'Apertura fallida',
    reset: 'Restablecer dispositivo',
    clickToReset: 'Clic para restablecer',
    resetConfirm: '¿Seguro que desea restablecer?',
    resetWillOut: "Reinicio = cierre sesión",
    resetSuccess: 'Restablecimiento exitoso',
    resetFailed: 'Restablecimiento fallido',
    firmwareUpgrade: 'Actualización de firmware',
    upgradeConfig: 'Config. de actualización',
    firmwareUrl: 'URL del firmware',
    md5Checksum: 'Valor md5',
    startUpgrade: 'Iniciar actualización',
    urlRequired: 'La URL es obligatoria',
    md5Required: 'El md5 es obligatorio',
    urlInvalid: 'Ingrese una URL válida',
    md5Invalid: 'Ingrese un md5 válido',
    upgradeConfirm: '¿Confirmar actualización?',
    upgradeSuccess: 'Actualización exitosa',
    clearFile: "Borrar archivo",
    uploading: 'Subiendo...',
    uploadAndUpgrade: 'Subir y actualizar',
    restartTips: 'Reinicio seguro, no se perderán datos',
    restarting: 'Reiniciando...',
    remoteTips: 'Control remoto de acceso',
    opening: 'Abriendo...',
    resetTips: 'Restaurar de fábrica, borrará datos',
    reseting: 'Restableciendo...',
    urlUpgrade: 'Actualización por URL',
    fileUpgrade: 'Subida de archivo',
    uploadFile: 'Clic para subir firmware',
    formatFile: 'Soporta .zip o .dpk, máx 20MB',
    fileName: 'Nombre',
    size: 'Tamaño',
  },
  // Configuración
  config: {
    second: 's',
    millisecond: 'ms',
    min: 'minutos',
    notsave: 'No guardar',
    save: 'Guardar',
    noVoice: 'Sin voz',
    no: 'No',
    yes: 'Sí',
    // Básico
    basicConfiguration: 'Configuración básica',
    displaySettings: 'Pantalla',
    informationDisplay: 'Mostrar información',
    audioSettings: 'Audio',
    languageAndThemes: 'Idioma y tema',
    autoAdjustScreenBrightness: 'Brillo automático',
    screenBrightness: 'Brillo de pantalla',
    autoTurnOffScreen: 'Apagado automático',
    autoTurnOffScreenTime: 'Tiempo de apagado',
    autoScreenSaver: 'Salvapantallas automático',
    autoScreenSaverTime: 'Tiempo de salvapantallas',
    displayDeviceSn: 'Mostrar SN',
    displayIp: 'Mostrar IP',
    displayIdentityCard: 'Mostrar credencial en la nube',
    volume: 'Volumen',
    language: 'Idioma',
    displayCode: "Mostrar código de miniapp",
    themeMode: "Tema",
    cn: 'Chino',
    en: 'Inglés',
    es: 'Español',
    fr: 'Francés',
    de: 'Alemán',
    ru: 'Ruso',
    ar: 'Árabe',
    pt: 'Port.',
    ko: 'Coreano',
    standardMode: 'Modo estándar',
    simpleMode: 'Modo simple',
    firstLogin: 'Primer inicio de sesión',
    backlight: 'Luz de fondo',
    brightness: 'Luz blanca',
    nirBrightness: 'Luz IR',
    never: 'Nunca',
    min1: '1 minuto',
    min2: '2 minutos',
    min3: '3 minutos',
    min4: '4 minutos',
    min5: '5 minutos',
    // Red
    networkConfiguration: 'Red',
    otherConfiguration: 'Otros',
    ipConfiguration: 'IP',
    devicePassword: 'Contraseña del dispositivo',
    protocolPassword: 'Contraseña de protocolo',
    networkType: 'Tipo de red',
    ethernet: 'Ethernet',
    wifiName: 'Wi‑Fi',
    wifiPassword: 'Contraseña Wi‑Fi',
    dhcpModeSelection: 'Modo DHCP',
    dhcpMode: 'Automático',
    customNetworkConfiguration: 'Manual',
    ipAddress: 'IP',
    gateway: 'Puerta de enlace',
    subnetMask: 'Máscara de subred',
    dnsServer: 'DNS',
    mac: 'MAC de red',
    // MQTT
    mqttRelatedConfiguration: 'MQTT',
    mqttConnectionInformation: 'Conexión MQTT',
    sessionConfiguration: 'Sesión',
    serverAddress: 'Servidor',
    clientID: 'ID de cliente',
    userName: 'Usuario',
    userPassword: 'Contraseña',
    topicPrefix: 'Prefijo de tema',
    onlineChecking: 'Verificación en línea',
    onlineCheckingTimeout: "Tiempo de espera",
    cleanSession: 'Limpiar sesión',
    clientIdSuffix: 'Sufijo de ID',
    willTopic: 'Tema de voluntad',
    enterpriseWechat:'Modo WeChat Enterprise sin efecto',
    // Rostro
    faceRelatedConfiguration: 'Configuración facial',
    functionalInformation: 'Función',
    prompt: 'Mensaje',
    faceSimilarityThreshold: 'Umbral de similitud',
    livenessDetectionFunction: 'Detección de vivacidad',
    livenessDetectionThreshold: 'Umbral de vivacidad',
    infraredImageDisplay: "Mostrar infrarrojo",
    maskRecognition: "Reconocer mascarilla",
    strangerVoice: "Voz de desconocido",
    voiceMode: "Modo de voz",
    voiceModeDate: 'Saludo personalizado',
    imageSaveType: "Tipo de guardado",
    saveStrangerImage: "Guardar imagen de desconocido",
    fullView: "Panorámica",
    face: "Rostro",
    broadcastPleaseRegisterFirst: 'Reproducir â€œRegistre primero”',
    broadcastHelloStranger: 'Reproducir â€œDesconocido”',
    broadcastName: 'Reproducir nombre',
    broadcastGreeting: 'Reproducir saludo personalizado',
    greeting: 'Saludo',
    broadcastWelcome: 'Reproducir "Bienvenido"',
    recognitionSwitch: 'Interruptor de re-detección',
    // Sistema
    systemRelatedConfiguration: 'Sistema',
    functionSwitch: 'Interruptores',
    cardSwipingSwitch: 'Tarjeta',
    passwordSwitch: 'Contraseña',
    strangerImage: 'Imagen de desconocido',
    cloudCertificateSwitch: 'Certificado en la nube',
    physicalCardNumber: 'Número de tarjeta física',
    cloudCertificateAcquisition: 'Obtención de certificado en la nube',
    heartbeatConfig: 'Heartbeat',
    heartbeatSwitch: 'Heartbeat on/off',
    heartRateInterval: 'Intervalo',
    heartbeatTopic: 'Tema heartbeat',
    heartbeatContent: 'Contenido',
    basicInformation: 'Información básica',
    deviceMac: 'MAC',
    uuid: 'UUID',
    sn: 'SN',
    model: 'Modelo',
    version: "Versión",
    appVersion: "Versión de firmware",
    releaseTime: "Hora de actualización",
    totaldisk: 'Espacio total',
    freedisk: 'Espacio restante',
    // Paso
    passageConfiguration: 'Paso',
    functionConfiguration: 'Funciones',
    numberOfPassageRecords: 'Máx. registros',
    durationOfRelayOpening: 'Duración del relé',
    alarmSwitch: 'Alarma',
    fireAlarmSwitch: 'Alarma de incendios',
    fireAlarmStatus: 'Estado de incendios',
    normal: 'Normal',
    warning: 'Aviso',
    tamperSwitch: 'Antimanipulación',
    uploadToCloudSwitch: 'Interruptor de carga facial',
    // Reloj
    clockConfiguration: 'Reloj',
    timeSynchronizationSwitch: 'Sincronización horaria',
    timeSynchronizationServerIP: 'Servidor de tiempo',
    timedSynchronizationTime: 'Hora de sincronización',
    timeZone: 'Zona horaria',
    setDeviceTime: 'Configurar hora',
    restartAfterSetting: 'El dispositivo se reiniciará',
    // Activación de certificado en la nube
    cloudCertificateActivation: 'Activación de certificado',
    activationKey: 'Clave de activación',
    cloudTips1: 'Ingrese la clave sin espacios',
    cloudTips2: 'Tras activar, conectará al servicio de autenticación',
    confirmActivation: 'Confirmar activación',
    activationInProgress: 'Activando...',
    activationFailed: 'Activación fallida',
    activationSuccessful: 'Activación exitosa',
    // Cambio de contraseña
    passwordModification: 'Cambiar contraseña',
    password: 'Contraseña',
    oldPassword: 'Contraseña anterior',
    newPassword: 'Nueva contraseña',
    confirmPassword: 'Confirmar contraseña',
    passwordRule: 'Recomendación de contraseña',
    passwordLength: 'Longitud â‰¥6',
    cannotBeTheSame: 'No puede ser todos los caracteres iguales',
    cannotOrder: 'No incluir 3+ números/letras consecutivos',
    cannotWeakPassword: 'No usar contraseñas débiles comunes',
    submit: 'Enviar',
    saveConfig: 'Guardar',
    // Mensajes
    msg_please_enter: 'Ingrese contenido',
    msg_inputPassword: 'Ingrese contraseña',
    msg_oldPasswordError: 'Contraseña anterior incorrecta',
    msg_password_mismatch: 'Las contraseñas no coinciden',
    msg_password_min_length: 'Mínimo 6 caracteres',
    msg_is_weak_password: 'Contraseña débil, cambie',
    msg_pswChangeSuccessAndLogin: 'Cambio exitoso, inicie sesión',
    msg_pswChangeSuccess: 'Cambio de contraseña exitoso',
    msg_pswChangeFail: 'Cambio de contraseña fallido',
    msg_saveSuccess: 'Guardado exitoso',
    msg_saveFail: 'Guardado fallido',
    msg_formFilled: 'Revise el formulario',
    msg_number_0_23: 'Solo 0-23',
    msg_number_0_24: 'Solo 0-24',
    msg_noChange: 'No hay cambios de configuración para guardar',
    // Configuración de recursos
    resourceConfiguration: 'Configuración de recursos',
    backgroundImage: 'Imagen de fondo',
    selectImage: 'Seleccionar imagen',
    uploadBackground: 'Subir fondo',
    uploading: 'Subiendo...',
    backgroundUploadTip: 'Suba una imagen PNG; se convertirá a Base64 y se enviará al dispositivo',
    backgroundResolutionMismatch: 'La resolución de la imagen debe ser {n}',
    backgroundRequired: 'Seleccione una imagen de fondo',
    backgroundImageOnlyPNG: 'Solo imágenes PNG',
    backgroundSizeLimit: 'La imagen no debe exceder 5MB',
    backgroundParseFailed: 'Error al leer imagen, reintente',
    backgroundImageSelected: 'Imagen seleccionada',
    backgroundSuccess: 'Fondo subido',
    backgroundFailed: 'Error al subir fondo',
    scanSettings: 'Configuración de escaneo',
    scanSwitch: 'Interruptor de Escaneo',
    scanInterval: 'Intervalo de escaneo',
  },
  // Gestión de personal
  person: {
    idCard: 'Número de ID',
    userType: 'Tipo de persona',
    administrator: 'Administrador',
    userId: 'ID',
    user: 'Usuario',
    voucher: 'Credencial',
    permission: 'Permiso',
    addUser: 'Añadir persona',
    name: 'Nombre',
    editUser: 'Editar persona',
    placeholderUserId: 'Ingrese ID de usuario',
    placeholderName: 'Ingrese nombre',
    userNotExist: 'La persona no existe',
    oneClickClear: 'Borrar todo',
    clearTips: 'Se borrarán todos los datos, Â¿continuar?',
    clearSuccess: 'Borrado exitoso',
    clearFailed: 'Borrado fallido',
  },
  voucher: {
    password: 'Contraseña',
    card: 'Tarjeta',
    face: 'Rostro',
    finger: 'Huella',
    code: 'Código',
    codeType: 'Tipo de código',
    passthroughCode: 'Código de paso',
    staticCode: 'Código estático',
    dynamicCode: 'Código dinámico',
    credentialId: 'ID de credencial',
    credentialValue: 'Valor de credencial',
    placeholderCode: 'Por favor ingrese certificado de código',
    placeholderPwd: 'Ingrese la credencial de contraseña',
    placeholderCard: 'Ingrese la credencial de tarjeta',
    validPassword: 'Ingrese 6 dígitos',
    validCard: 'Ingrese 8 dígitos o letras',
    photoRegistration: 'Registro de foto',
    featureValueRegistration: 'Registro de rasgos',
    fingerRegistration: 'Registro de huella',
    fingerFeatureRegistration: 'Registro por valor de característica',
    fingerInput: 'Coloque el dedo en el lector de huellas',
    fingerRemainingTime: 'Tiempo restante',
    fingerInputting: 'Registrando...',
    startFingerInput: 'Iniciar registro de huella',
    fingerInputTips: 'Ingrese el valor de característica de la huella',
    fingerWaitInput: 'En espera de registro',
    fingerInputNow: 'Registrando huella...',
    fingerInputSuccess: 'Registro exitoso',
    fingerInputFailed: 'No se pudo iniciar el registro de huella',
    fingerReTry: 'Fallo en el registro de huella, inténtelo de nuevo',
    fingerFilled: 'Registro de huella exitoso, el valor de característica se ha rellenado automáticamente',
    fingerFailed: 'Fallo en el registro de huella',
    fingerTimeout: 'Tiempo de espera agotado',
    fingerInputTimeout: 'Tiempo de espera del registro de huella, inténtelo de nuevo',
    fingerError: 'Registro fallido',
    fingerInputError: 'Fallo en el registro de huella, inténtelo de nuevo',
    fingerInputed: 'Huella registrada',
    fingerReInput: 'Volver a registrar la huella',
  },
  permission: {
    deletePermission: 'Eliminar permiso',
    addPermission: 'Añadir permiso',
    permissionId: 'ID de permiso',
    userId: 'ID de usuario',
    timeRange: 'Intervalo de tiempo',
    extra: 'Extra',
    effectiveType: 'Tipo de vigencia',
    effectiveTime: 'Tiempo efectivo',
    effectiveWeek: 'Semana efectiva',
    timePeriod: 'Periodo',
    addTimePeriod: 'Añadir periodo',
    modify_previous_time: 'Primero modifique el periodo previo',
    cannot_be_earlier: 'La hora final no puede ser menor que la inicial',
    times_cannot_overlap: 'Los tiempos no pueden superponerse',
    choose_time_range: 'Seleccione rango de tiempo',
    unlimitedMode: 'Sin límite',
    usualMode: 'Modo habitual',
    dailyMode: 'Modo diario',
    weeklyRepetitionMode: 'Repetición semanal',
    time_range: 'Rango',
  },
  common: {
    startDate: 'Fecha inicio',
    endDate: 'Fecha fin',
    to: 'a',
    cancel: 'Cancelar',
    confirm: 'Confirmar',
    close: 'Cerrar',
    delete: 'Eliminar',
    edit: 'Editar',
    batchDelete: 'Eliminación masiva',
    startTime: 'Hora inicio',
    endTime: 'Hora fin',
    monday: 'Lunes',
    tuseday: 'Martes',
    wednesday: 'Miércoles',
    thursday: 'Jueves',
    friday: 'Viernes',
    saterday: 'Sábado',
    sunday: 'Domingo',
    placeholder: 'Por favor ingrese',
    placeholderSelect: 'Por favor seleccione',
    closeTips: '¿Confirmar cierre?',
    deleteTips: '¿Confirmar eliminación?',
    deleteSuccess: 'Eliminado con Ã©xito',
    addSuccess: 'Añadido con Ã©xito',
    editSuccess: 'Editado con Ã©xito',
    saveSuccess: 'Guardado con Ã©xito',
    tips: 'Aviso',
    operation: 'Operación',
    query: 'Consulta',
    reset: 'Reset',
    noData: 'Sin datos por ahora',
    export: 'Exportar',
    success: 'Éxito',
    failure: 'Fallo',
    incorrectFormat: 'Formato incorrecto',
    integerFormat: 'Debe ser entero â‰¥0',
    positiveIntegerFormat: 'Debe ser entero >0',
    noDataSaved: 'No hay datos para guardar',
    chinese: 'Chino',
    english: 'Inglés',
    spanish: 'Español',
    french: 'Francés',
    german: 'Alemán',
    russian: 'Ruso',
    arabic: 'Árabe',
    portuguese: 'Portugués',
    korean: 'Coreano',
    detail: 'Detalle',
    clearTips: '¿Confirmar borrado?',
    clearSuccess: 'Borrado exitosamente',
  },
  log: {
    accessMethod: 'Método de acceso',
    passingTime: 'Hora de paso',
    accessPass: 'Credencial de paso',
    accessResult: 'Resultado',
    accessPhoto: 'Foto',
    viewPhotos: 'Ver fotos'
  },
  error: {
    networkError: 'Error de red, verifique su conexión',
    timeout: 'Tiempo de espera agotado, reintente',
    serverError: 'Error interno del servidor, reintente',
    notFound: 'El recurso no existe',
    unauthorized: 'No autorizado, inicie sesión',
    noResponse: 'No hay respuesta del servidor',
    unknownError: 'Error de solicitud, código:',
    requestFailed: 'Solicitud fallida'
  },
  security: {
    keyId: 'ID de clave',
    keyType: 'Tipo de clave',
    keyEncoding: 'Codificación de clave',
    keyValue: 'Valor de clave',
    startTime: 'Hora de inicio',
    expirationTime: 'Hora de expiración',
    newKey: 'Agregar clave',
    clearKey: 'Borrar clave',
    validTime: 'Tiempo válido',
  }
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/fr.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,435 @@
export default {
  // Connexion
  login: {
    lang: 'Langue',
    systemname: 'Login Facial',
    username: 'Utilisateur',
    username_label: "Veuillez saisir l'utilisateur",
    pwd: 'Mot de passe',
    pwd_label: 'Veuillez saisir le mot de passe',
    pwd_info: 'Entrez un mot de passe valide',
    success_msg: 'Connexion réussie',
    error_name: 'Mot de passe incorrect',
    error_res: "Ne répond pas aux exigences de connexion",
    login: 'Se connecter',
  },
  // Barre latérale
  aside: {
    systemname: 'Console',
    quit: 'Quitter',
    deviceControl: 'Contrôle',
    basicSetting: 'Réglages',
    workerSetting: 'Personnel',
    deviceMonitoring: 'Surveillance',
    recordManagement: 'Journal',
    securityManagement: 'Des clés',
    tips: 'Conseil',
    tips_msg: 'Voulez-vous vous déconnecter ?',
  },
  // Contrôle
  control: {
    remoteControl: 'Distance',
    restart: 'Redémarrer',
    clickToRestart: 'Cliquer pour redémarrer',
    restartConfirm: 'Confirmer le redémarrage ?',
    restartSuccess: 'Redémarrage réussi',
    restartFailed: 'Redémarrage Ã©choué',
    remoteOpen: 'Ouverture Ã  distance',
    clickToOpen: 'Cliquer pour ouvrir',
    openConfirm: "Confirmer l'ouverture Ã  distance ?",
    remoteOpenSuccess: 'Ouverture réussie',
    remoteOpenFailed: "Ouverture Ã©chouée",
    reset: 'Réinitialiser',
    clickToReset: 'Cliquer pour réinitialiser',
    resetConfirm: 'Confirmer la réinitialisation ?',
    resetWillOut: "Déco après réinit.",
    resetSuccess: 'Réinitialisation réussie',
    resetFailed: 'Réinitialisation Ã©chouée',
    firmwareUpgrade: 'Mise Ã  jour du firmware',
    upgradeConfig: 'Config. mise Ã  jour',
    firmwareUrl: 'URL du firmware',
    md5Checksum: 'Valeur md5',
    startUpgrade: 'Démarrer la mise Ã  jour',
    urlRequired: "L'URL est requise",
    md5Required: 'Le md5 est requis',
    urlInvalid: 'Entrez une URL valide',
    md5Invalid: 'Entrez un md5 valide',
    upgradeConfirm: 'Confirmer la mise Ã  jour ?',
    upgradeSuccess: 'Mise Ã  jour réussie',
    clearFile: "Effacer le fichier",
    uploading: 'Téléversement...',
    uploadAndUpgrade: 'Téléverser et mettre Ã  jour',
    restartTips: 'Redémarrage sécurisé, sans perte de données',
    restarting: 'Redémarrage...',
    remoteTips: 'Contrôle distant de la porte',
    opening: 'Ouverture...',
    resetTips: 'Restauration usine, effacera les données',
    reseting: 'Réinitialisation...',
    urlUpgrade: 'Mise Ã  jour par URL',
    fileUpgrade: 'Mise Ã  jour par fichier',
    uploadFile: 'Cliquez pour téléverser le firmware',
    formatFile: 'Supporte .zip ou .dpk, max 20MB',
    fileName: 'Nom du fichier',
    size: 'Taille',
  },
  // Configuration
  config: {
    second: 's',
    millisecond: 'ms',
    min: 'minutes',
    notsave: 'Ne pas enregistrer',
    save: 'Enregistrer',
    noVoice: 'Sans voix',
    no: 'Non',
    yes: 'Oui',
    basicConfiguration: 'Configuration de base',
    displaySettings: 'Affichage',
    informationDisplay: "Affichage d'information",
    audioSettings: 'Audio',
    languageAndThemes: 'Langue et thème',
    autoAdjustScreenBrightness: 'Luminosité auto',
    screenBrightness: 'Luminosité Ã©cran',
    autoTurnOffScreen: 'Extinction auto',
    autoTurnOffScreenTime: "Temps d'extinction",
    autoScreenSaver: 'Économiseur auto',
    autoScreenSaverTime: 'Délai Ã©conomiseur',
    displayDeviceSn: 'Afficher SN',
    displayIp: 'Afficher IP',
    displayIdentityCard: 'Afficher certificat cloud',
    volume: 'Volume',
    language: 'Langue',
    displayCode: "Afficher code miniapp",
    themeMode: "Thème",
    cn: 'Chinois',
    en: 'Anglais',
    es: 'Espagnol',
    fr: 'Français',
    de: 'Allemand',
    ru: 'Russe',
    ar: 'Arabe',
    pt: 'Port.',
    ko: 'Coréen',
    standardMode: 'Mode standard',
    simpleMode: 'Mode simple',
    firstLogin: 'Première connexion',
    backlight: 'Rétroéclairage',
    brightness: 'Lumière blanche',
    nirBrightness: 'Lumière IR',
    never: 'Jamais',
    min1: '1 minute',
    min2: '2 minutes',
    min3: '3 minutes',
    min4: '4 minutes',
    min5: '5 minutes',
    networkConfiguration: 'Réseau',
    otherConfiguration: 'Autres',
    ipConfiguration: 'Adresse IP',
    devicePassword: 'Mot de passe appareil',
    protocolPassword: 'Mot de passe protocole',
    networkType: 'Type de réseau',
    ethernet: 'Ethernet',
    wifiName: 'Wi‑Fi',
    wifiPassword: 'Mot de passe Wi‑Fi',
    dhcpModeSelection: 'Mode DHCP',
    dhcpMode: 'Automatique',
    customNetworkConfiguration: 'Manuel',
    ipAddress: 'IP',
    gateway: 'Passerelle',
    subnetMask: 'Masque',
    dnsServer: 'DNS',
    mac: 'MAC',
    mqttRelatedConfiguration: 'MQTT',
    mqttConnectionInformation: 'Connexion MQTT',
    sessionConfiguration: 'Session',
    serverAddress: 'Serveur',
    clientID: 'ID client',
    userName: "Nom d'utilisateur",
    userPassword: 'Mot de passe',
    topicPrefix: 'Préfixe de topic',
    onlineChecking: 'Vérification en ligne',
    onlineCheckingTimeout: "Délai d'attente",
    cleanSession: 'Session propre',
    clientIdSuffix: "Suffixe d'ID client",
    willTopic: 'Topic Will',
    enterpriseWechat:'Mode WeChat Entreprise sans effet',
    faceRelatedConfiguration: 'Configuration faciale',
    functionalInformation: 'Fonction',
    prompt: 'Invite',
    faceSimilarityThreshold: 'Seuil de similarité',
    livenessDetectionFunction: 'Détection de vivacité',
    livenessDetectionThreshold: 'Seuil de vivacité',
    infraredImageDisplay: "Affichage infrarouge",
    maskRecognition: "Reconnaissance de masque",
    strangerVoice: "Voix d'inconnu",
    voiceMode: "Mode voix",
    voiceModeDate: 'Salutation personnalisée',
    imageSaveType: "Type de sauvegarde",
    saveStrangerImage: "Sauver image d'inconnu",
    fullView: "Panorama",
    face: "Visage",
    broadcastPleaseRegisterFirst: 'Diffuser "Veuillez vous enregistrer"',
    broadcastHelloStranger: 'Diffuser "Inconnu"',
    broadcastName: 'Diffuser le nom',
    broadcastGreeting: 'Diffuser salutation personnalisée',
    greeting: 'Salutation',
    broadcastWelcome: 'Diffuser "Bienvenue"',
    recognitionSwitch: 'Interrupteur de re-détection',
    systemRelatedConfiguration: 'Système',
    functionSwitch: 'Commutateurs',
    cardSwipingSwitch: 'Carte',
    passwordSwitch: 'Mot de passe',
    strangerImage: 'Image inconnue',
    cloudCertificateSwitch: 'Certificat cloud',
    physicalCardNumber: 'Numéro de carte physique',
    cloudCertificateAcquisition: 'Obtention certificat cloud',
    heartbeatConfig: 'Heartbeat',
    heartbeatSwitch: 'Activer heartbeat',
    heartRateInterval: 'Intervalle',
    heartbeatTopic: 'Topic heartbeat',
    heartbeatContent: 'Contenu',
    basicInformation: 'Infos de base',
    deviceMac: 'MAC',
    uuid: 'UUID',
    sn: 'SN',
    model: 'Modèle',
    version: "Version",
    appVersion: "Version firmware",
    releaseTime: "Date de mise Ã  jour",
    totaldisk: 'Volume total',
    freedisk: 'Espace restant',
    passageConfiguration: 'Passage',
    functionConfiguration: 'Fonctions',
    numberOfPassageRecords: 'Nb. max de passages',
    durationOfRelayOpening: "Durée d'ouverture du relais",
    alarmSwitch: 'Alarme',
    fireAlarmSwitch: "Alarme incendie",
    fireAlarmStatus: 'État incendie',
    normal: 'Normal',
    warning: 'Alerte',
    tamperSwitch: 'Anti-sabotage',
    uploadToCloudSwitch: 'Interrupteur de téléversement facial',
    clockConfiguration: 'Horloge',
    timeSynchronizationSwitch: 'Synchronisation horaire',
    timeSynchronizationServerIP: 'Serveur de temps',
    timedSynchronizationTime: 'Heure de synchro',
    timeZone: 'Fuseau horaire',
    setDeviceTime: "Régler l'heure",
    restartAfterSetting: "L'appareil redémarrera",
    cloudCertificateActivation: 'Activation du certificat',
    activationKey: "Clé d'activation",
    cloudTips1: 'Entrez la clé sans espaces',
    cloudTips2: "Après activation, connexion au service cloud",
    confirmActivation: "Confirmer l'activation",
    activationInProgress: 'Activation...',
    activationFailed: 'Échec activation',
    activationSuccessful: 'Activation réussie',
    passwordModification: 'Modifier le mot de passe',
    password: 'Mot de passe',
    oldPassword: 'Ancien mot de passe',
    newPassword: 'Nouveau mot de passe',
    confirmPassword: 'Confirmer mot de passe',
    passwordRule: 'Recommandations',
    passwordLength: 'Longueur â‰¥6',
    cannotBeTheSame: 'Tous les caractères ne peuvent Ãªtre identiques',
    cannotOrder: 'Pas de 3+ nombres/lettres consécutifs',
    cannotWeakPassword: 'Pas de mot de passe faible',
    submit: 'Soumettre',
    saveConfig: 'Enregistrer',
    msg_please_enter: 'Veuillez saisir',
    msg_inputPassword: 'Veuillez saisir le mot de passe',
    msg_oldPasswordError: 'Ancien mot de passe incorrect',
    msg_password_mismatch: 'Les mots de passe ne correspondent pas',
    msg_password_min_length: 'Au moins 6 caractères',
    msg_is_weak_password: 'Mot de passe faible, changez-le',
    msg_pswChangeSuccessAndLogin: 'Mot de passe changé, reconnectez-vous',
    msg_pswChangeSuccess: 'Mot de passe modifié',
    msg_pswChangeFail: 'Échec de modification',
    msg_saveSuccess: 'Enregistré',
    msg_saveFail: "Échec de l'enregistrement",
    msg_formFilled: 'Vérifiez le formulaire',
    msg_number_0_23: 'Supporte seulement 0-23',
    msg_number_0_24: 'Supporte seulement 0-24',
    msg_noChange: 'Aucun changement de configuration Ã  enregistrer',
    resourceConfiguration: 'Configuration des ressources',
    backgroundImage: 'Image de fond',
    selectImage: 'Choisir une image',
    uploadBackground: 'Téléverser le fond',
    uploading: 'Téléversement...',
    backgroundUploadTip: 'Téléversez une image PNG avec un nombre de pixels Ã©gal Ã  {n}; convertie en Base64 puis envoyée',
    backgroundResolutionMismatch: "La résolution de l'image doit Ãªtre {n}",
    backgroundRequired: "Veuillez choisir l'image de fond",
    backgroundImageOnlyPNG: 'Seulement PNG',
    backgroundSizeLimit: "La taille ne doit pas dépasser 5MB",
    backgroundParseFailed: "Échec de lecture de l'image",
    backgroundImageSelected: 'Image sélectionnée',
    backgroundSuccess: 'Fond téléversé',
    backgroundFailed: 'Échec du téléversement',
    scanSettings: 'Paramètres de scan',
    scanSwitch: 'Interrupteur de Scan',
    scanInterval: 'Intervalle de scan',
  },
  person: {
    idCard: "Numéro d'identité",
    userType: 'Type de personne',
    administrator: 'Administrateur',
    userId: 'ID',
    user: 'Utilisateur',
    voucher: 'Credential',
    permission: 'Permission',
    addUser: 'Ajouter une personne',
    name: 'Nom',
    editUser: 'Modifier personne',
    placeholderUserId: "Entrez l'ID utilisateur",
    placeholderName: 'Entrez le nom',
    userNotExist: "La personne n'existe pas",
    oneClickClear: 'Tout effacer',
    clearTips: 'Cette action effacera toutes les données, continuer ?',
    clearSuccess: 'Effacé avec succès',
    clearFailed: 'Échec de suppression',
  },
  voucher: {
    password: 'Mot de passe',
    card: 'Carte',
    face: 'Visage',
    finger: 'Empreinte',
    code: 'Code',
    codeType: 'Type de code',
    passthroughCode: 'Code de transit',
    staticCode: 'Code statique',
    dynamicCode: 'Code dynamique',
    credentialId: 'ID d’identifiant',
    credentialValue: 'Valeur d’identifiant',
    placeholderCode: 'Veuillez saisir le certificat de code',
    placeholderPwd: 'Entrez le mot de passe',
    placeholderCard: 'Entrez la carte',
    validPassword: 'Entrez 6 chiffres',
    validCard: 'Entrez 8 chiffres ou lettres',
    photoRegistration: "Enregistrement d'image",
    featureValueRegistration: 'Enregistrement de caractéristiques',
    fingerRegistration: "Enregistrement d'empreinte",
    fingerFeatureRegistration: 'Enregistrement par valeur de caractéristique',
    fingerInput: "Veuillez poser votre doigt sur le lecteur d'empreintes",
    fingerRemainingTime: 'Temps restant',
    fingerInputting: 'En cours...',
    startFingerInput: "Démarrer l'enregistrement d'empreinte",
    fingerInputTips: "Veuillez saisir la valeur de caractéristique de l'empreinte",
    fingerWaitInput: 'En attente',
    fingerInputNow: "Enregistrement de l'empreinte...",
    fingerInputSuccess: 'Enregistrement réussi',
    fingerInputFailed: "Échec du démarrage de l'enregistrement d'empreinte",
    fingerReTry: "Échec de l'enregistrement d'empreinte, veuillez réessayer",
    fingerFilled: "Enregistrement réussi, la valeur de caractéristique a Ã©té remplie automatiquement",
    fingerFailed: "Échec de l'enregistrement d'empreinte",
    fingerTimeout: 'Délai dépassé',
    fingerInputTimeout: "Délai dépassé pour l'enregistrement d'empreinte, veuillez réessayer",
    fingerError: 'Échec de la saisie',
    fingerInputError: "Échec de l'enregistrement d'empreinte, veuillez réessayer",
    fingerInputed: 'Empreinte déjà enregistrée',
    fingerReInput: "Réenregistrer l'empreinte",
  },
  permission: {
    deletePermission: 'Supprimer permission',
    addPermission: 'Ajouter permission',
    permissionId: 'ID permission',
    userId: 'ID utilisateur',
    timeRange: 'Plage horaire',
    extra: 'Extra',
    effectiveType: 'Type de validité',
    effectiveTime: 'Temps effectif',
    effectiveWeek: 'Semaine effective',
    timePeriod: 'Période',
    addTimePeriod: 'Ajouter une période',
    modify_previous_time: 'Modifiez la période précédente',
    cannot_be_earlier: "L'heure de fin ne peut Ãªtre avant le début",
    times_cannot_overlap: 'Les horaires ne peuvent se chevaucher',
    choose_time_range: 'Choisissez une plage horaire',
    unlimitedMode: 'Illimité',
    usualMode: 'Mode habituel',
    dailyMode: 'Mode quotidien',
    weeklyRepetitionMode: 'Répétition hebdo',
    time_range: 'Plage horaire',
  },
  common: {
    startDate: 'Date début',
    endDate: 'Date fin',
    to: 'à',
    cancel: 'Annuler',
    confirm: 'Confirmer',
    close: 'Fermer',
    delete: 'Supprimer',
    edit: 'Modifier',
    batchDelete: 'Suppression multiple',
    startTime: 'Heure début',
    endTime: 'Heure fin',
    monday: 'Lundi',
    tuseday: 'Mardi',
    wednesday: 'Mercredi',
    thursday: 'Jeudi',
    friday: 'Vendredi',
    saterday: 'Samedi',
    sunday: 'Dimanche',
    placeholder: 'Veuillez saisir',
    placeholderSelect: 'Veuillez sélectionner',
    closeTips: 'Confirmer la fermeture ?',
    deleteTips: 'Confirmer la suppression ?',
    deleteSuccess: 'Suppression réussie',
    addSuccess: 'Ajout réussi',
    editSuccess: 'Modification réussie',
    saveSuccess: 'Enregistré avec succès',
    tips: 'Conseil',
    operation: 'Opération',
    query: 'Rechercher',
    reset: 'Reset',
    noData: 'Pas de données',
    export: 'Exporter',
    success: 'Succès',
    failure: 'Échec',
    incorrectFormat: 'Format incorrect',
    integerFormat: 'Doit Ãªtre un entier â‰¥0',
    positiveIntegerFormat: 'Doit Ãªtre un entier >0',
    noDataSaved: 'Aucune donnée Ã  enregistrer',
    chinese: 'Chinois',
    english: 'Anglais',
    spanish: 'Espagnol',
    french: 'Français',
    german: 'Allemand',
    russian: 'Russe',
    arabic: 'Arabe',
    portuguese: 'Portugais',
    korean: 'Coréen',
    detail: 'Détail',
    clearTips: 'Confirmer l\'effacement?',
    clearSuccess: 'Effacé avec succès',
  },
  log: {
    accessMethod: "Méthode d'accès",
    passingTime: 'Heure de passage',
    accessPass: 'Credential',
    accessResult: 'Résultat',
    accessPhoto: 'Photo',
    viewPhotos: 'Voir photos'
  },
  error: {
    networkError: 'Échec réseau, vérifiez la connexion',
    timeout: 'Délai dépassé, réessayez',
    serverError: 'Erreur interne serveur, réessayez',
    notFound: "La ressource n'existe pas",
    unauthorized: 'Non autorisé, reconnectez-vous',
    noResponse: 'Impossible de contacter le serveur',
    unknownError: 'Échec de la requête, code :',
    requestFailed: 'Requête Ã©chouée'
  },
  security: {
    keyId: 'ID de clé',
    keyType: 'Type de clé',
    keyEncoding: 'Encodage de clé',
    keyValue: 'Valeur de clé',
    startTime: 'Heure de début',
    expirationTime: 'Heure d\'expiration',
    newKey: 'Ajouter une clé',
    clearKey: 'Effacer la clé',
    validTime: 'Durée de validité',
  }
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/index.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
import Vue from 'vue' // å¼•å…¥vue
import VueI18n from 'vue-i18n' // å¼•å…¥i18n模块
import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui英文包
import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN' // element-ui中文包
import locale from 'element-ui/lib/locale' // å¼•å…¥elementuiui语言包模块
import enLocale from './en' // æœ¬åœ°è‹±æ–‡åŒ…
import zhLocale from './zh' // æœ¬åœ°ä¸­æ–‡åŒ…
import esLocale from './es'
import frLocale from './fr'
import deLocale from './de'
import ruLocale from './ru'
import arLocale from './ar'
import ptLocale from './pt'
import koLocale from './ko'
Vue.use(VueI18n) // vue使用i18n模块
// å¼•入本地
const messages = {
  EN: {
    ...enLocale, // es6的拓展运算符,相当于解析出每个对象
    ...elementEnLocale
  },
  CN: {
    ...zhLocale,
    ...elementZhLocale
  },
  ES: {
    ...esLocale,
    ...elementEnLocale
  },
  FR: {
    ...frLocale,
    ...elementEnLocale
  },
  DE: {
    ...deLocale,
    ...elementEnLocale
  },
  RU: {
    ...ruLocale,
    ...elementEnLocale
  },
  AR: {
    ...arLocale,
    ...elementEnLocale
  },
  PT: {
    ...ptLocale,
    ...elementEnLocale
  },
  KO: {
    ...koLocale,
    ...elementEnLocale
  }
}
let publicConfig = sessionStorage.getItem('publicConfig')
let { language } = publicConfig ? JSON.parse(publicConfig) : {}
// åˆ›å»ºå›½é™…化实例
const i18n = new VueI18n({
  locale: language, // set locale,默认中文
  messages // set locale messages。语言包
})
locale.i18n((key, value) => i18n.t(key, value))
export default i18n
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/ko.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,431 @@
export default {
  // ë¡œê·¸ì¸
  login: {
    lang: '언어',
    systemname: '로그인',
    username: '사용자',
    username_label: '사용자명을 ìž…력하세요',
    pwd: '비밀번호',
    pwd_label: '비밀번호를 ìž…력하세요',
    pwd_info: '올바른 ë¹„밀번호 í˜•식을 ìž…력하세요',
    success_msg: '로그인 ì„±ê³µ',
    error_name: '비밀번호가 ì˜¬ë°”르지 ì•ŠìŠµë‹ˆë‹¤',
    error_res: '로그인 ìš”구사항을 ì¶©ì¡±í•˜ì§€ ì•ŠìŠµë‹ˆë‹¤',
    login: '로그인',
  },
  // ì‚¬ì´ë“œë°”
  aside: {
    systemname: '관리',
    quit: '로그아웃',
    deviceControl: '제어',
    basicSetting: '설정',
    workerSetting: '인원',
    deviceMonitoring: '모니터링',
    recordManagement: '기록',
    securityManagement: '키 ê´€ë¦¬',
    tips: '알림',
    tips_msg: '로그아웃하시겠습니까?',
  },
  control: {
    remoteControl: '원격',
    restart: '재부팅',
    clickToRestart: '클릭하여 ìž¬ë¶€íŒ…',
    restartConfirm: '재부팅을 í™•인하시겠습니까?',
    restartSuccess: '재부팅 ì„±ê³µ',
    restartFailed: '재부팅 ì‹¤íŒ¨',
    remoteOpen: '원격 ê°œë°©',
    clickToOpen: '클릭하여 ê°œë°©',
    openConfirm: '원격 ê°œë°©ì„ í™•인하시겠습니까?',
    remoteOpenSuccess: '개방 ì„±ê³µ',
    remoteOpenFailed: '개방 ì‹¤íŒ¨',
    reset: '장치 ì´ˆê¸°í™”',
    clickToReset: '클릭하여 ì´ˆê¸°í™”',
    resetConfirm: '초기화를 í™•인하시겠습니까?',
    resetWillOut: "리셋 í›„ ë¡œê·¸ì•„웃",
    resetSuccess: '초기화 ì„±ê³µ',
    resetFailed: '초기화 ì‹¤íŒ¨',
    firmwareUpgrade: '펌웨어 ì—…그레이드',
    upgradeConfig: '업그레이드 ì„¤ì •',
    firmwareUrl: '펌웨어 URL',
    md5Checksum: 'md5 ê°’',
    startUpgrade: '업그레이드 ì‹œìž‘',
    urlRequired: 'URL은 í•„수입니다',
    md5Required: 'md5는 í•„수입니다',
    urlInvalid: '올바른 URL을 ìž…력하세요',
    md5Invalid: '올바른 md5를 ìž…력하세요',
    upgradeConfirm: '업그레이드를 ì§„행할까요?',
    upgradeSuccess: '업그레이드 ì„±ê³µ',
    clearFile: "파일 ì§€ìš°ê¸°",
    uploading: '업로드 ì¤‘...',
    uploadAndUpgrade: '업로드 í›„ ì—…그레이드',
    restartTips: '안전한 ìž¬ë¶€íŒ…, ë°ì´í„° ì†ì‹¤ ì—†ìŒ',
    restarting: '재부팅 ì¤‘...',
    remoteTips: '출입 ì›ê²© ì œì–´',
    opening: '개방 ì¤‘...',
    resetTips: '공장 ì´ˆê¸°í™”, ëª¨ë“  ë°ì´í„° ì‚­ì œ',
    reseting: '초기화 ì¤‘...',
    urlUpgrade: 'URL ì—…그레이드',
    fileUpgrade: '파일 ì—…그레이드',
    uploadFile: '펌웨어 íŒŒì¼ ì—…로드',
    formatFile: '.zip ë˜ëŠ” .dpk ì§€ì›, ìµœëŒ€ 20MB',
    fileName: '파일명',
    size: '크기',
  },
  config: {
    second: '초',
    millisecond: '밀리초',
    min: '분',
    notsave: '저장 ì•ˆ í•¨',
    save: '저장',
    noVoice: '음성 ì—†ìŒ',
    no: '아니오',
    yes: '예',
    basicConfiguration: '기본 ì„¤ì •',
    displaySettings: '디스플레이 ì„¤ì •',
    informationDisplay: '정보 í‘œì‹œ',
    audioSettings: '오디오 ì„¤ì •',
    languageAndThemes: '언어 ë° í…Œë§ˆ',
    autoAdjustScreenBrightness: '자동 ë°ê¸° ì¡°ì ˆ',
    screenBrightness: '화면 ë°ê¸°',
    autoTurnOffScreen: '자동 í™”ë©´ ë„기',
    autoTurnOffScreenTime: '화면 ë„기 ì‹œê°„',
    autoScreenSaver: '자동 í™”면보호기',
    autoScreenSaverTime: '화면보호기 ì‹œê°„',
    displayDeviceSn: 'SN í‘œì‹œ',
    displayIp: 'IP í‘œì‹œ',
    displayIdentityCard: '클라우드 ì¸ì¦ í‘œì‹œ',
    volume: '볼륨',
    language: '언어',
    displayCode: "미니앱 ì½”드 í‘œì‹œ",
    themeMode: "테마",
    cn: '중국어',
    en: '영어',
    es: '스페인어',
    fr: '프랑스어',
    de: '독일어',
    ru: '러시아어',
    ar: '아랍어',
    pt: '포르투갈',
    ko: '한국어',
    standardMode: '표준 ëª¨ë“œ',
    simpleMode: '심플 ëª¨ë“œ',
    firstLogin: '첫 ë¡œê·¸ì¸',
    backlight: 'ë°°ê²½ ì¡°ëª…',
    brightness: '화이트 ë¼ì´íЏ',
    nirBrightness: '적외선 ë¼ì´íЏ',
    never: '안 í•¨',
    min1: '1분',
    min2: '2분',
    min3: '3분',
    min4: '4분',
    min5: '5분',
    networkConfiguration: '네트워크 ì„¤ì •',
    otherConfiguration: '기타 ì„¤ì •',
    ipConfiguration: 'IP ì„¤ì •',
    devicePassword: '장치 ë¹„밀번호',
    protocolPassword: '프로토콜 ë¹„밀번호',
    networkType: '네트워크 ìœ í˜•',
    ethernet: '이더넷',
    wifiName: 'Wi‑Fi ì´ë¦„',
    wifiPassword: 'Wi‑Fi ë¹„밀번호',
    dhcpModeSelection: 'DHCP ëª¨ë“œ',
    dhcpMode: '자동',
    customNetworkConfiguration: '수동 ì„¤ì •',
    ipAddress: 'IP ì£¼ì†Œ',
    gateway: '게이트웨이',
    subnetMask: '서브넷 ë§ˆìŠ¤í¬',
    dnsServer: 'DNS ì„œë²„',
    mac: '네트워크 MAC',
    mqttRelatedConfiguration: 'MQTT ì„¤ì •',
    mqttConnectionInformation: 'MQTT ì—°ê²° ì •ë³´',
    sessionConfiguration: '세션 ì„¤ì •',
    serverAddress: '서버 ì£¼ì†Œ',
    clientID: '클라이언트 ID',
    userName: '사용자명',
    userPassword: '비밀번호',
    topicPrefix: '토픽 ì ‘두사',
    onlineChecking: '온라인 í™•인',
    onlineCheckingTimeout: '타임아웃',
    cleanSession: '클린 ì„¸ì…˜',
    clientIdSuffix: '클라이언트 ID ì ‘미사',
    willTopic: 'Will í† í”½',
    enterpriseWechat:'Enterprise WeChat ëª¨ë“œ ì ìš© ì•ˆ ë¨',
    faceRelatedConfiguration: '얼굴 ì„¤ì •',
    functionalInformation: '기능 ì •ë³´',
    prompt: '프롬프트',
    faceSimilarityThreshold: '유사도 ìž„계값',
    livenessDetectionFunction: '라이브니스 ê°ì§€',
    livenessDetectionThreshold: '라이브니스 ìž„계값',
    infraredImageDisplay: "적외선 ì´ë¯¸ì§€ í‘œì‹œ",
    maskRecognition: "마스크 ì¸ì‹",
    strangerVoice: "낯선이 ìŒì„±",
    voiceMode: "음성 ëª¨ë“œ",
    voiceModeDate: '맞춤 ì¸ì‚¬',
    imageSaveType: "이미지 ì €ìž¥ ìœ í˜•",
    saveStrangerImage: "낯선이 ì´ë¯¸ì§€ ì €ìž¥",
    fullView: "전경",
    face: "얼굴",
    broadcastPleaseRegisterFirst: '"등록 í›„ ì´ìš©" ìž¬ìƒ',
    broadcastHelloStranger: '"낯선이" ìž¬ìƒ',
    broadcastName: '이름 ìž¬ìƒ',
    broadcastGreeting: '맞춤 ì¸ì‚¬ ìž¬ìƒ',
    greeting: '인사말',
    broadcastWelcome: '"환영합니다" ìž¬ìƒ',
    recognitionSwitch: '재검사 ìŠ¤ìœ„ì¹˜',
    systemRelatedConfiguration: '시스템 ì„¤ì •',
    functionSwitch: '기능 ìŠ¤ìœ„ì¹˜',
    cardSwipingSwitch: '카드',
    passwordSwitch: '비밀번호',
    strangerImage: '낯선이 ì´ë¯¸ì§€',
    cloudCertificateSwitch: '클라우드 ì¸ì¦',
    physicalCardNumber: '물리 ì¹´ë“œ ë²ˆí˜¸',
    cloudCertificateAcquisition: '클라우드 ì¸ì¦ íšë“',
    heartbeatConfig: '하트비트',
    heartbeatSwitch: '하트비트 ìŠ¤ìœ„ì¹˜',
    heartRateInterval: '인터벌',
    heartbeatTopic: '하트비트 í† í”½',
    heartbeatContent: '하트비트 ë‚´ìš©',
    basicInformation: '기본 ì •ë³´',
    deviceMac: 'MAC ì£¼ì†Œ',
    uuid: 'UUID',
    sn: 'SN',
    model: '모델',
    version: "버전",
    appVersion: "펌웨어 ë²„ì „",
    releaseTime: "업데이트 ì‹œê°„",
    totaldisk: '총 ê³µê°„',
    freedisk: '잔여 ê³µê°„',
    passageConfiguration: '출입 ì„¤ì •',
    functionConfiguration: '기능 ì„¤ì •',
    numberOfPassageRecords: '최대 ì¶œìž… ê¸°ë¡ ìˆ˜',
    durationOfRelayOpening: '릴레이 ê°œë°© ì‹œê°„',
    alarmSwitch: '경보',
    fireAlarmSwitch: '화재 ê²½ë³´',
    fireAlarmStatus: '화재 ìƒíƒœ',
    normal: '정상',
    warning: '경고',
    tamperSwitch: '방범 ì•ŒëžŒ',
    uploadToCloudSwitch: '얼굴 ì—…로드 ìŠ¤ìœ„ì¹˜',
    clockConfiguration: '시계 ì„¤ì •',
    timeSynchronizationSwitch: '시간 ë™ê¸°í™”',
    timeSynchronizationServerIP: '시간 ì„œë²„ IP',
    timedSynchronizationTime: '동기화 ì‹œê°„',
    timeZone: '시간대',
    setDeviceTime: '장치 ì‹œê°„ ì„¤ì •',
    restartAfterSetting: '설정 í›„ ìž¥ì¹˜ ìž¬ë¶€íŒ…',
    cloudCertificateActivation: '클라우드 ì¸ì¦ í™œì„±í™”',
    activationKey: '활성화 í‚¤',
    cloudTips1: '공백 ì—†ì´ í‚¤ë¥¼ ìž…력하세요',
    cloudTips2: '활성화 í›„ í´ë¼ìš°ë“œ ì„œë¹„스에 ì—°ê²°ë©ë‹ˆë‹¤',
    confirmActivation: '활성화 í™•인',
    activationInProgress: '활성화 ì¤‘...',
    activationFailed: '활성화 ì‹¤íŒ¨',
    activationSuccessful: '활성화 ì„±ê³µ',
    passwordModification: '비밀번호 ë³€ê²½',
    password: '비밀번호',
    oldPassword: '현재 ë¹„밀번호',
    newPassword: '새 ë¹„밀번호',
    confirmPassword: '비밀번호 í™•인',
    passwordRule: '비밀번호 ê¶Œìž¥ ì‚¬í•­',
    passwordLength: '길이 â‰¥6',
    cannotBeTheSame: '모두 ê°™ì€ ë¬¸ìž ë¶ˆê°€',
    cannotOrder: '연속 3개 ì´ìƒ ìˆ«ìž/소문자 ë¶ˆê°€',
    cannotWeakPassword: '일반적인 ì•½í•œ ë¹„밀번호 ê¸ˆì§€',
    submit: '제출',
    saveConfig: '설정 ì €ìž¥',
    msg_please_enter: '내용을 ìž…력하세요',
    msg_inputPassword: '비밀번호를 ìž…력하세요',
    msg_oldPasswordError: '현재 ë¹„밀번호가 í‹€ë ¸ìŠµë‹ˆë‹¤',
    msg_password_mismatch: '비밀번호가 ì¼ì¹˜í•˜ì§€ ì•ŠìŠµë‹ˆë‹¤',
    msg_password_min_length: '비밀번호는 ìµœì†Œ 6자',
    msg_is_weak_password: '약한 ë¹„밀번호입니다. ë³€ê²½í•˜ì„¸ìš”',
    msg_pswChangeSuccessAndLogin: '비밀번호 ë³€ê²½ ì™„료, ë‹¤ì‹œ ë¡œê·¸ì¸í•˜ì„¸ìš”',
    msg_pswChangeSuccess: '비밀번호 ë³€ê²½ ì„±ê³µ',
    msg_pswChangeFail: '비밀번호 ë³€ê²½ ì‹¤íŒ¨',
    msg_saveSuccess: '저장 ì„±ê³µ',
    msg_saveFail: '저장 ì‹¤íŒ¨',
    msg_formFilled: '양식 ìž…력을 í™•인하세요',
    msg_number_0_23: '0~23만 ì§€ì›',
    msg_number_0_24: '0~24만 ì§€ì›',
    msg_noChange: '저장할 êµ¬ì„± ë³€ê²½ ì‚¬í•­ì´ ì—†ìŠµë‹ˆë‹¤',
    resourceConfiguration: '리소스 ì„¤ì •',
    backgroundImage: 'ë°°ê²½ ì´ë¯¸ì§€',
    selectImage: '이미지 ì„ íƒ',
    uploadBackground: 'ë°°ê²½ ì—…로드',
    uploading: '업로드 ì¤‘...',
    backgroundUploadTip: '픽셀 ìˆ˜ê°€ {n}인 PNG í˜•식의 ì´ë¯¸ì§€ë¥¼ ì—…로드하세요. ì´ë¯¸ì§€ëŠ” Base64 í˜•식으로 ë³€í™˜ëœ í›„ ê¸°ê¸°ë¡œ ì—…로드됩니다.',
    backgroundResolutionMismatch: '이미지 í•´ìƒë„는 {n} ì´ì–´ì•¼ í•©ë‹ˆë‹¤',
    backgroundRequired: 'ë°°ê²½ ì´ë¯¸ì§€ë¥¼ ì„ íƒí•˜ì„¸ìš”',
    backgroundImageOnlyPNG: 'PNG ì´ë¯¸ì§€ë§Œ í—ˆìš©',
    backgroundSizeLimit: '이미지 í¬ê¸°ëŠ” 5MB ì´í•˜',
    backgroundParseFailed: '이미지 ì½ê¸° ì‹¤íŒ¨, ë‹¤ì‹œ ì‹œë„',
    backgroundImageSelected: '이미지가 ì„ íƒë˜ì—ˆìŠµë‹ˆë‹¤',
    backgroundSuccess: 'ë°°ê²½ ì—…로드 ì„±ê³µ',
    backgroundFailed: 'ë°°ê²½ ì—…로드 ì‹¤íŒ¨',
    scanSettings: '스캔 ì„¤ì •',
    scanSwitch: '스캔 ìŠ¤ìœ„ì¹˜',
    scanInterval: '스캔 ê°„격',
  },
  person: {
    idCard: '신분증 ë²ˆí˜¸',
    userType: '사용자 ìœ í˜•',
    administrator: '관리자',
    userId: 'ID',
    user: '사용자',
    voucher: '자격 ì¦ëª…',
    permission: '권한',
    addUser: '사용자 ì¶”ê°€',
    name: '이름',
    editUser: '사용자 ìˆ˜ì •',
    placeholderUserId: '사용자 ID를 ìž…력하세요',
    placeholderName: '이름을 ìž…력하세요',
    userNotExist: '사용자가 ì¡´ìž¬í•˜ì§€ ì•ŠìŠµë‹ˆë‹¤',
    oneClickClear: '모두 ì‚­ì œ',
    clearTips: '모든 ë°ì´í„°ê°€ ì‚­ì œë©ë‹ˆë‹¤. ê³„속하시겠습니까?',
    clearSuccess: '삭제 ì™„료',
    clearFailed: '삭제 ì‹¤íŒ¨',
  },
  voucher: {
    password: '비밀번호',
    card: '카드',
    face: '얼굴',
    finger: '지문',
    code: '코드',
    codeType: '코드 ìœ í˜•',
    passthroughCode: '통과 ì½”드',
    staticCode: '정적 ì½”드',
    dynamicCode: '동적 ì½”드',
    credentialId: '자격 ì¦ëª… ID',
    credentialValue: '자격 ì¦ëª… ê°’',
    placeholderCode: '코드 ì¸ì¦ì„œë¥¼ ìž…력하세요',
    placeholderPwd: '비밀번호 ìžê²©ì„ ìž…ë ¥',
    placeholderCard: '카드 ìžê²©ì„ ìž…ë ¥',
    validPassword: '6자리 ìˆ«ìžë¥¼ ìž…ë ¥',
    validCard: '8자리 ìˆ«ìž ë˜ëŠ” ë¬¸ìž ìž…ë ¥',
    photoRegistration: '사진 ë“±ë¡',
    featureValueRegistration: '특징값 ë“±ë¡',
    fingerRegistration: '지문 ë“±ë¡',
    fingerFeatureRegistration: '특징값 ë“±ë¡',
    fingerInput: '손가락을 ì§€ë¬¸ ì„¼ì„œì— ì˜¬ë ¤ì£¼ì„¸ìš”',
    fingerRemainingTime: '남은 ì‹œê°„',
    fingerInputting: '등록 ì¤‘...',
    startFingerInput: '지문 ë“±ë¡ ì‹œìž‘',
    fingerInputTips: '지문 íŠ¹ì§•ê°’ì„ ìž…력하세요',
    fingerWaitInput: '등록 ëŒ€ê¸°',
    fingerInputNow: '지문을 ë“±ë¡í•˜ëŠ” ì¤‘...',
    fingerInputSuccess: '등록 ì„±ê³µ',
    fingerInputFailed: '지문 ë“±ë¡ ì‹œìž‘ ì‹¤íŒ¨',
    fingerReTry: '지문 ë“±ë¡ ì‹¤íŒ¨, ë‹¤ì‹œ ì‹œë„í•´ ì£¼ì„¸ìš”',
    fingerFilled: '지문 ë“±ë¡ ì„±ê³µ, íŠ¹ì§•ê°’ì´ ìžë™ìœ¼ë¡œ ìž…력되었습니다',
    fingerFailed: '지문 ë“±ë¡ ì‹¤íŒ¨',
    fingerTimeout: '시간 ì´ˆê³¼',
    fingerInputTimeout: '지문 ë“±ë¡ ì‹œê°„ ì´ˆê³¼, ë‹¤ì‹œ ì‹œë„í•´ ì£¼ì„¸ìš”',
    fingerError: '등록 ì‹¤íŒ¨',
    fingerInputError: '지문 ë“±ë¡ ì‹¤íŒ¨, ë‹¤ì‹œ ì‹œë„í•´ ì£¼ì„¸ìš”',
    fingerInputed: '지문이 ì´ë¯¸ ë“±ë¡ë˜ì—ˆìŠµë‹ˆë‹¤',
    fingerReInput: '지문 ìž¬ë“±ë¡',
  },
  permission: {
    deletePermission: '권한 ì‚­ì œ',
    addPermission: '권한 ì¶”ê°€',
    permissionId: '권한 ID',
    userId: '사용자 ID',
    timeRange: '시간 ë²”위',
    extra: '추가',
    effectiveType: '유효 ìœ í˜•',
    effectiveTime: '유효 ì‹œê°„',
    effectiveWeek: '유효 ì£¼',
    timePeriod: '시간대',
    addTimePeriod: '시간대 ì¶”ê°€',
    modify_previous_time: '먼저 ì´ì „ ì‹œê°„대를 ìˆ˜ì •하세요',
    cannot_be_earlier: '종료 ì‹œê°„이 ì‹œìž‘ ì‹œê°„보다 ë¹ ë¥¼ ìˆ˜ ì—†ìŠµë‹ˆë‹¤',
    times_cannot_overlap: '시간대가 ê²¹ì¹  ìˆ˜ ì—†ìŠµë‹ˆë‹¤',
    choose_time_range: '시간 ë²”위를 ì„ íƒí•˜ì„¸ìš”',
    unlimitedMode: '무제한 ëª¨ë“œ',
    usualMode: '일반 ëª¨ë“œ',
    dailyMode: '일일 ëª¨ë“œ',
    weeklyRepetitionMode: '주간 ë°˜ë³µ ëª¨ë“œ',
    time_range: '시간 ë²”위',
  },
  common: {
    startDate: '시작 ë‚ ì§œ',
    endDate: '종료 ë‚ ì§œ',
    to: '부터',
    cancel: '취소',
    confirm: '확인',
    close: '닫기',
    delete: '삭제',
    edit: '수정',
    batchDelete: '일괄 ì‚­ì œ',
    startTime: '시작 ì‹œê°„',
    endTime: '종료 ì‹œê°„',
    monday: '월요일',
    tuseday: '화요일',
    wednesday: '수요일',
    thursday: '목요일',
    friday: '금요일',
    saterday: '토요일',
    sunday: '일요일',
    placeholder: '입력하세요',
    placeholderSelect: '선택하세요',
    closeTips: '닫기를 í™•인하시겠습니까?',
    deleteTips: '삭제를 í™•인하시겠습니까?',
    deleteSuccess: '삭제 ì„±ê³µ',
    addSuccess: '추가 ì„±ê³µ',
    editSuccess: '수정 ì„±ê³µ',
    saveSuccess: '저장 ì„±ê³µ',
    tips: '알림',
    operation: '작업',
    query: '조회',
    reset: '초기화',
    noData: '데이터 ì—†ìŒ',
    export: '내보내기',
    success: '성공',
    failure: '실패',
    incorrectFormat: '형식이 ì˜¬ë°”르지 ì•ŠìŠµë‹ˆë‹¤',
    integerFormat: '0 ì´ìƒì˜ ì •수여야 í•©ë‹ˆë‹¤',
    positiveIntegerFormat: '0보다 í° ì •수여야 í•©ë‹ˆë‹¤',
    noDataSaved: '저장할 ë°ì´í„°ê°€ ì—†ìŠµë‹ˆë‹¤',
    chinese: '중국어',
    english: '영어',
    spanish: '스페인어',
    french: '프랑스어',
    german: '독일어',
    russian: '러시아어',
    arabic: '아랍어',
    portuguese: '포르투갈어',
    korean: '한국어',
    detail: '세부 ì •ë³´',
    clearTips: '지우시겠습니까?',
    clearSuccess: '성공적으로 ì§€ì›Œì§',
  },
  log: {
    accessMethod: '출입 ë°©ì‹',
    passingTime: '출입 ì‹œê°„',
    accessPass: '출입 ì¦ëª…',
    accessResult: 'ê²°ê³¼',
    accessPhoto: '사진',
    viewPhotos: '사진 ë³´ê¸°'
  },
  error: {
    networkError: '네트워크 ì˜¤ë¥˜, ì—°ê²°ì„ í™•인하세요',
    timeout: '요청 ì‹œê°„ ì´ˆê³¼, ë‹¤ì‹œ ì‹œë„하세요',
    serverError: '서버 ë‚´ë¶€ ì˜¤ë¥˜, ë‚˜ì¤‘에 ë‹¤ì‹œ ì‹œë„',
    notFound: '요청한 ìžì›ì´ ì—†ìŠµë‹ˆë‹¤',
    unauthorized: '인증되지 ì•Šì•˜ìŠµë‹ˆë‹¤. ë‹¤ì‹œ ë¡œê·¸ì¸í•˜ì„¸ìš”',
    noResponse: '서버 ì‘답이 ì—†ìŠµë‹ˆë‹¤',
    unknownError: '요청 ì‹¤íŒ¨, ì˜¤ë¥˜ ì½”드:',
    requestFailed: '요청에 ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤'
  },
  security: {
    keyId: '키 ID',
    keyType: '키 ìœ í˜•',
    keyEncoding: '키 ì¸ì½”딩',
    keyValue: '키 ê°’',
    startTime: '시작 ì‹œê°„',
    expirationTime: '만료 ì‹œê°„',
    newKey: '키 ì¶”ê°€',
    clearKey: '키 ì§€ìš°ê¸°',
    validTime: '유효 ì‹œê°„',
  }
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/pt.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,430 @@
export default {
  // Login
  login: {
    lang: 'Idioma',
    systemname: 'Login',
    username: 'Usuário',
    username_label: 'Digite o usuário',
    pwd: 'Senha',
    pwd_label: 'Digite a senha',
    pwd_info: 'Insira uma senha válida',
    success_msg: 'Login bem-sucedido',
    error_name: 'Senha incorreta',
    error_res: 'Não atende aos requisitos de login',
    login: 'Entrar',
  },
  aside: {
    systemname: 'Painel',
    quit: 'Sair',
    deviceControl: 'Controle',
    basicSetting: 'Config.',
    workerSetting: 'Pessoas',
    deviceMonitoring: 'Monitor',
    recordManagement: 'Registros',
    securityManagement: 'Chaves',
    tips: 'Aviso',
    tips_msg: 'Deseja sair?',
  },
  control: {
    remoteControl: 'Remoto',
    restart: 'Reiniciar',
    clickToRestart: 'Clique para reiniciar',
    restartConfirm: 'Confirmar reinício?',
    restartSuccess: 'Reinício bem-sucedido',
    restartFailed: 'Reinício falhou',
    remoteOpen: 'Abrir porta remotamente',
    clickToOpen: 'Clique para abrir',
    openConfirm: 'Confirmar abertura remota?',
    remoteOpenSuccess: 'Abertura bem-sucedida',
    remoteOpenFailed: 'Abertura falhou',
    reset: 'Redefinir dispositivo',
    clickToReset: 'Clique para redefinir',
    resetConfirm: 'Confirmar redefinição?',
    resetWillOut: "Após reset, sair",
    resetSuccess: 'Redefinição concluída',
    resetFailed: 'Redefinição falhou',
    firmwareUpgrade: 'Atualizar firmware',
    upgradeConfig: 'Config. de atualização',
    firmwareUrl: 'URL do firmware',
    md5Checksum: 'Valor md5',
    startUpgrade: 'Iniciar atualização',
    urlRequired: 'URL Ã© obrigatória',
    md5Required: 'md5 Ã© obrigatório',
    urlInvalid: 'Informe uma URL válida',
    md5Invalid: 'Informe um md5 válido',
    upgradeConfirm: 'Confirmar atualização?',
    upgradeSuccess: 'Atualização bem-sucedida',
    clearFile: "Limpar arquivo",
    uploading: 'Enviando...',
    uploadAndUpgrade: 'Enviar e atualizar',
    restartTips: 'Reinício seguro, sem perder dados',
    restarting: 'Reiniciando...',
    remoteTips: 'Controle remoto de acesso',
    opening: 'Abrindo...',
    resetTips: 'Restaurar padrão, apagará dados',
    reseting: 'Redefinindo...',
    urlUpgrade: 'Atualização por URL',
    fileUpgrade: 'Atualização por arquivo',
    uploadFile: 'Clique para enviar firmware',
    formatFile: 'Suporta .zip ou .dpk, até 20MB',
    fileName: 'Nome do arquivo',
    size: 'Tamanho',
  },
  config: {
    second: 's',
    millisecond: 'ms',
    min: 'minutos',
    notsave: 'Não salvar',
    save: 'Salvar',
    noVoice: 'Sem voz',
    no: 'Não',
    yes: 'Sim',
    basicConfiguration: 'Configuração básica',
    displaySettings: 'Exibição',
    informationDisplay: 'Mostrar informações',
    audioSettings: 'Áudio',
    languageAndThemes: 'Idioma e tema',
    autoAdjustScreenBrightness: 'Brilho automático',
    screenBrightness: 'Brilho da tela',
    autoTurnOffScreen: 'Desligar tela automaticamente',
    autoTurnOffScreenTime: 'Tempo para desligar',
    autoScreenSaver: 'Protetor de tela automático',
    autoScreenSaverTime: 'Tempo do protetor',
    displayDeviceSn: 'Mostrar SN',
    displayIp: 'Mostrar IP',
    displayIdentityCard: 'Mostrar certificado em nuvem',
    volume: 'Volume',
    language: 'Idioma',
    displayCode: "Mostrar código da miniapp",
    themeMode: "Tema",
    cn: 'Chinês',
    en: 'Inglês',
    es: 'Espanhol',
    fr: 'Francês',
    de: 'Alemão',
    ru: 'Russo',
    ar: 'Árabe',
    pt: 'Português',
    ko: 'Coreano',
    standardMode: 'Modo padrão',
    simpleMode: 'Modo simples',
    firstLogin: 'Primeiro login',
    backlight: 'Luz de fundo',
    brightness: 'Luz branca',
    nirBrightness: 'Luz IR',
    never: 'Nunca',
    min1: '1 minuto',
    min2: '2 minutos',
    min3: '3 minutos',
    min4: '4 minutos',
    min5: '5 minutos',
    networkConfiguration: 'Rede',
    otherConfiguration: 'Outros',
    ipConfiguration: 'IP',
    devicePassword: 'Senha do dispositivo',
    protocolPassword: 'Senha do protocolo',
    networkType: 'Tipo de rede',
    ethernet: 'Ethernet',
    wifiName: 'Wi‑Fi',
    wifiPassword: 'Senha Wi‑Fi',
    dhcpModeSelection: 'Modo DHCP',
    dhcpMode: 'Automático',
    customNetworkConfiguration: 'Manual',
    ipAddress: 'Endereço IP',
    gateway: 'Gateway',
    subnetMask: 'Máscara de sub-rede',
    dnsServer: 'Servidor DNS',
    mac: 'MAC de rede',
    mqttRelatedConfiguration: 'MQTT',
    mqttConnectionInformation: 'Conexão MQTT',
    sessionConfiguration: 'Sessão',
    serverAddress: 'Servidor',
    clientID: 'ID do cliente',
    userName: 'Usuário',
    userPassword: 'Senha',
    topicPrefix: 'Prefixo do tópico',
    onlineChecking: 'Verificação online',
    onlineCheckingTimeout: 'Tempo limite',
    cleanSession: 'Sessão limpa',
    clientIdSuffix: 'Sufixo do ID',
    willTopic: 'Tópico Will',
    enterpriseWechat:'Modo WeChat Enterprise sem efeito',
    faceRelatedConfiguration: 'Config. de rosto',
    functionalInformation: 'Função',
    prompt: 'Aviso',
    faceSimilarityThreshold: 'Limite de similaridade',
    livenessDetectionFunction: 'Detecção de vivacidade',
    livenessDetectionThreshold: 'Limite de vivacidade',
    infraredImageDisplay: "Exibir infravermelho",
    maskRecognition: "Reconhecimento de máscara",
    strangerVoice: "Voz de desconhecido",
    voiceMode: "Modo de voz",
    voiceModeDate: 'Saudação personalizada',
    imageSaveType: "Tipo de salvamento",
    saveStrangerImage: "Salvar imagem de desconhecido",
    fullView: "Panorâmico",
    face: "Rosto",
    broadcastPleaseRegisterFirst: 'Reproduzir "Registre-se primeiro"',
    broadcastHelloStranger: 'Reproduzir "Desconhecido"',
    broadcastName: 'Reproduzir nome',
    broadcastGreeting: 'Reproduzir saudação personalizada',
    greeting: 'Saudação',
    broadcastWelcome: 'Reproduzir "Bem-vindo"',
    recognitionSwitch: 'Interruptor de re-detecção',
    systemRelatedConfiguration: 'Sistema',
    functionSwitch: 'Interruptores',
    cardSwipingSwitch: 'Cartão',
    passwordSwitch: 'Senha',
    strangerImage: 'Imagem de desconhecido',
    cloudCertificateSwitch: 'Certificado em nuvem',
    physicalCardNumber: 'Número do cartão físico',
    cloudCertificateAcquisition: 'Obter certificado em nuvem',
    heartbeatConfig: 'Heartbeat',
    heartbeatSwitch: 'Ativar heartbeat',
    heartRateInterval: 'Intervalo',
    heartbeatTopic: 'Tópico heartbeat',
    heartbeatContent: 'Conteúdo',
    basicInformation: 'Informações básicas',
    deviceMac: 'MAC',
    uuid: 'UUID',
    sn: 'SN',
    model: 'Modelo',
    version: "Versão",
    appVersion: "Versão do firmware",
    releaseTime: "Hora da atualização",
    totaldisk: 'Espaço total',
    freedisk: 'Espaço restante',
    passageConfiguration: 'Passagem',
    functionConfiguration: 'Funções',
    numberOfPassageRecords: 'Máx. registros',
    durationOfRelayOpening: 'Duração do relé',
    alarmSwitch: 'Alarme',
    fireAlarmSwitch: 'Alarme de incêndio',
    fireAlarmStatus: 'Status de incêndio',
    normal: 'Normal',
    warning: 'Alerta',
    tamperSwitch: 'Anti-violação',
    uploadToCloudSwitch: 'Interruptor de upload facial',
    clockConfiguration: 'Relógio',
    timeSynchronizationSwitch: 'Sincronizar hora',
    timeSynchronizationServerIP: 'Servidor de tempo',
    timedSynchronizationTime: 'Hora de sincronização',
    timeZone: 'Fuso horário',
    setDeviceTime: 'Ajustar hora',
    restartAfterSetting: 'O dispositivo reiniciará',
    cloudCertificateActivation: 'Ativar certificado',
    activationKey: 'Chave de ativação',
    cloudTips1: 'Digite a chave sem espaços',
    cloudTips2: 'Após ativar, conectará ao serviço em nuvem',
    confirmActivation: 'Confirmar ativação',
    activationInProgress: 'Ativando...',
    activationFailed: 'Falha na ativação',
    activationSuccessful: 'Ativação bem-sucedida',
    passwordModification: 'Alterar senha',
    password: 'Senha',
    oldPassword: 'Senha antiga',
    newPassword: 'Nova senha',
    confirmPassword: 'Confirmar senha',
    passwordRule: 'Recomendação de senha',
    passwordLength: 'Comprimento â‰¥6',
    cannotBeTheSame: 'Todos caracteres não podem ser iguais',
    cannotOrder: 'Sem 3+ números/letras consecutivos',
    cannotWeakPassword: 'Não usar senhas fracas comuns',
    submit: 'Enviar',
    saveConfig: 'Salvar',
    msg_please_enter: 'Por favor, insira',
    msg_inputPassword: 'Digite a senha',
    msg_oldPasswordError: 'Senha antiga incorreta',
    msg_password_mismatch: 'Senhas não coincidem',
    msg_password_min_length: 'Mínimo 6 caracteres',
    msg_is_weak_password: 'Senha fraca, altere',
    msg_pswChangeSuccessAndLogin: 'Senha alterada, faça login',
    msg_pswChangeSuccess: 'Senha alterada',
    msg_pswChangeFail: 'Falha ao alterar senha',
    msg_saveSuccess: 'Salvo com sucesso',
    msg_saveFail: 'Falha ao salvar',
    msg_formFilled: 'Verifique o formulário',
    msg_number_0_23: 'Apenas 0-23',
    msg_number_0_24: 'Apenas 0-24',
    msg_noChange: 'Nenhuma alteração de configuração para salvar',
    resourceConfiguration: 'Configuração de recursos',
    backgroundImage: 'Imagem de fundo',
    selectImage: 'Selecionar imagem',
    uploadBackground: 'Enviar fundo',
    uploading: 'Enviando...',
    backgroundUploadTip: 'Por favor, envie uma imagem em formato PNG com pixels de {n}, que será convertida para o formato Base64 e enviada ao dispositivo',
    backgroundResolutionMismatch: 'A resolução da imagem deve ser {n}',
    backgroundRequired: 'Selecione uma imagem de fundo',
    backgroundImageOnlyPNG: 'Apenas PNG',
    backgroundSizeLimit: 'Imagem não pode exceder 5MB',
    backgroundParseFailed: 'Falha ao ler imagem',
    backgroundImageSelected: 'Imagem selecionada',
    backgroundSuccess: 'Fundo enviado',
    backgroundFailed: 'Falha ao enviar fundo',
    scanSettings: 'Configurações de digitalização',
    scanSwitch: 'Interruptor de Digitalização',
    scanInterval: 'Intervalo de digitalização',
  },
  person: {
    idCard: 'Número de identificação',
    userType: 'Tipo de pessoa',
    administrator: 'Administrador',
    userId: 'ID',
    user: 'Usuário',
    voucher: 'Credencial',
    permission: 'Permissão',
    addUser: 'Adicionar pessoa',
    name: 'Nome',
    editUser: 'Editar pessoa',
    placeholderUserId: 'Informe o ID do usuário',
    placeholderName: 'Informe o nome',
    userNotExist: 'Pessoa não existe',
    oneClickClear: 'Limpar tudo',
    clearTips: 'Isto apagará todos os dados, continuar?',
    clearSuccess: 'Limpo com sucesso',
    clearFailed: 'Falha ao limpar',
  },
  voucher: {
    password: 'Senha',
    card: 'Cartão',
    face: 'Rosto',
    finger: 'Impressão digital',
    code: 'Código',
    codeType: 'Tipo de código',
    passthroughCode: 'Código de passagem',
    staticCode: 'Código estático',
    dynamicCode: 'Código dinâmico',
    credentialId: 'ID da credencial',
    credentialValue: 'Valor da credencial',
    placeholderCode: 'Por favor insira o certificado de código',
    placeholderPwd: 'Informe a senha',
    placeholderCard: 'Informe o cartão',
    validPassword: 'Insira 6 dígitos',
    validCard: 'Insira 8 dígitos ou letras',
    photoRegistration: 'Registro de foto',
    featureValueRegistration: 'Registro de características',
    fingerRegistration: 'Cadastro de digital',
    fingerFeatureRegistration: 'Cadastro por valor de característica',
    fingerInput: 'Coloque o dedo no leitor de digitais',
    fingerRemainingTime: 'Tempo restante',
    fingerInputting: 'Cadastrando...',
    startFingerInput: 'Iniciar cadastro de digital',
    fingerInputTips: 'Informe o valor de característica da digital',
    fingerWaitInput: 'Aguardando cadastro',
    fingerInputNow: 'Cadastrando digital...',
    fingerInputSuccess: 'Cadastro bem-sucedido',
    fingerInputFailed: 'Falha ao iniciar o cadastro de digital',
    fingerReTry: 'Falha no cadastro de digital, tente novamente',
    fingerFilled: 'Cadastro de digital bem-sucedido, o valor de característica foi preenchido automaticamente',
    fingerFailed: 'Falha no cadastro de digital',
    fingerTimeout: 'Tempo esgotado',
    fingerInputTimeout: 'Tempo esgotado no cadastro de digital, tente novamente',
    fingerError: 'Cadastro falhou',
    fingerInputError: 'Falha no cadastro de digital, tente novamente',
    fingerInputed: 'Digital já cadastrada',
    fingerReInput: 'Cadastrar digital novamente',
  },
  permission: {
    deletePermission: 'Excluir permissão',
    addPermission: 'Adicionar permissão',
    permissionId: 'ID da permissão',
    userId: 'ID do usuário',
    timeRange: 'Intervalo de tempo',
    extra: 'Extra',
    effectiveType: 'Tipo de vigência',
    effectiveTime: 'Tempo efetivo',
    effectiveWeek: 'Semana efetiva',
    timePeriod: 'Período',
    addTimePeriod: 'Adicionar período',
    modify_previous_time: 'Altere primeiro o período anterior',
    cannot_be_earlier: 'Hora final não pode ser menor que inicial',
    times_cannot_overlap: 'Horários não podem se sobrepor',
    choose_time_range: 'Selecione o intervalo',
    unlimitedMode: 'Ilimitado',
    usualMode: 'Modo usual',
    dailyMode: 'Modo diário',
    weeklyRepetitionMode: 'Repetição semanal',
    time_range: 'Faixa de tempo',
  },
  common: {
    startDate: 'Data inicial',
    endDate: 'Data final',
    to: 'a',
    cancel: 'Cancelar',
    confirm: 'Confirmar',
    close: 'Fechar',
    delete: 'Excluir',
    edit: 'Editar',
    batchDelete: 'Excluir em lote',
    startTime: 'Hora inicial',
    endTime: 'Hora final',
    monday: 'Segunda',
    tuseday: 'Terça',
    wednesday: 'Quarta',
    thursday: 'Quinta',
    friday: 'Sexta',
    saterday: 'Sábado',
    sunday: 'Domingo',
    placeholder: 'Por favor insira',
    placeholderSelect: 'Por favor selecione',
    closeTips: 'Confirmar fechamento?',
    deleteTips: 'Confirmar exclusão?',
    deleteSuccess: 'Excluído com sucesso',
    addSuccess: 'Adicionado com sucesso',
    editSuccess: 'Editado com sucesso',
    saveSuccess: 'Salvo com sucesso',
    tips: 'Dica',
    operation: 'Operação',
    query: 'Consulta',
    reset: 'Reset',
    noData: 'Sem dados',
    export: 'Exportar',
    success: 'Sucesso',
    failure: 'Falha',
    incorrectFormat: 'Formato incorreto',
    integerFormat: 'Deve ser inteiro â‰¥0',
    positiveIntegerFormat: 'Deve ser inteiro >0',
    noDataSaved: 'Sem dados para salvar',
    chinese: 'Chinês',
    english: 'Inglês',
    spanish: 'Espanhol',
    french: 'Francês',
    german: 'Alemão',
    russian: 'Russo',
    arabic: 'Árabe',
    portuguese: 'Português',
    korean: 'Coreano',
    detail: 'Detalhe',
    clearTips: 'Confirmar limpeza?',
    clearSuccess: 'Limpo com sucesso',
  },
  log: {
    accessMethod: 'Método de acesso',
    passingTime: 'Hora de passagem',
    accessPass: 'Credencial de acesso',
    accessResult: 'Resultado',
    accessPhoto: 'Foto',
    viewPhotos: 'Ver fotos'
  },
  error: {
    networkError: 'Falha de rede, verifique a conexão',
    timeout: 'Tempo esgotado, tente novamente',
    serverError: 'Erro interno, tente mais tarde',
    notFound: 'Recurso não existe',
    unauthorized: 'Não autorizado, faça login',
    noResponse: 'Sem resposta do servidor',
    unknownError: 'Falha na solicitação, código:',
    requestFailed: 'Solicitação falhou'
  },
  security: {
    keyId: 'ID da chave',
    keyType: 'Tipo de chave',
    keyEncoding: 'Codificação da chave',
    keyValue: 'Valor da chave',
    startTime: 'Hora de início',
    expirationTime: 'Hora de expiração',
    newKey: 'Adicionar chave',
    clearKey: 'Limpar chave',
    validTime: 'Tempo válido',
  }
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/ru.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,430 @@
export default {
  // Ð’ход
  login: {
    lang: 'Язык',
    systemname: 'Вход',
    username: 'Пользователь',
    username_label: 'Введите Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚еля',
    pwd: 'Пароль',
    pwd_label: 'Введите Ð¿Ð°Ñ€Ð¾Ð»ÑŒ',
    pwd_info: 'Введите ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚ный Ð¿Ð°Ñ€Ð¾Ð»ÑŒ',
    success_msg: 'Вход Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½',
    error_name: 'Неверный Ð¿Ð°Ñ€Ð¾Ð»ÑŒ',
    error_res: 'Не ÑÐ¾Ð¾Ñ‚ветствует Ñ‚ребованиям Ð²Ñ…ода',
    login: 'Войти',
  },
  aside: {
    systemname: 'Панель',
    quit: 'Выйти',
    deviceControl: 'Управление',
    basicSetting: 'Настройки',
    workerSetting: 'Персонал',
    deviceMonitoring: 'Мониторинг',
    recordManagement: 'Журналы',
    securityManagement: 'ключами',
    tips: 'Подсказка',
    tips_msg: 'Выйти Ð¸Ð· ÑÐ¸ÑÑ‚емы?',
  },
  control: {
    remoteControl: 'Удалённое',
    restart: 'Перезапуск',
    clickToRestart: 'Нажмите Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿ÑƒÑÐºÐ°',
    restartConfirm: 'Подтвердить Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿ÑƒÑÐº?',
    restartSuccess: 'Перезапуск ÑƒÑÐ¿ÐµÑˆÐµÐ½',
    restartFailed: 'Перезапуск Ð½ÐµÑƒÐ´Ð°Ñ‡ÐµÐ½',
    remoteOpen: 'Удалённое Ð¾Ñ‚крытие',
    clickToOpen: 'Нажмите Ð´Ð»Ñ Ð¾Ñ‚крытия',
    openConfirm: 'Подтвердить ÑƒÐ´Ð°Ð»Ñ‘нное Ð¾Ñ‚крытие?',
    remoteOpenSuccess: 'Открыто ÑƒÑÐ¿ÐµÑˆÐ½Ð¾',
    remoteOpenFailed: 'Открыть Ð½Ðµ ÑƒÐ´Ð°Ð»Ð¾ÑÑŒ',
    reset: 'Сброс ÑƒÑÑ‚ройства',
    clickToReset: 'Нажмите Ð´Ð»Ñ ÑÐ±Ñ€Ð¾ÑÐ°',
    resetConfirm: 'Подтвердить ÑÐ±Ñ€Ð¾Ñ?',
    resetWillOut: "Выход Ð¿Ð¾ÑÐ»Ðµ ÑÐ±Ñ€Ð¾ÑÐ°",
    resetSuccess: 'Сброс ÑƒÑÐ¿ÐµÑˆÐµÐ½',
    resetFailed: 'Сброс Ð½ÐµÑƒÐ´Ð°Ñ‡ÐµÐ½',
    firmwareUpgrade: 'Обновление Ð¿Ñ€Ð¾ÑˆÐ¸Ð²ÐºÐ¸',
    upgradeConfig: 'Настройки Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ',
    firmwareUrl: 'URL Ð¿Ñ€Ð¾ÑˆÐ¸Ð²ÐºÐ¸',
    md5Checksum: 'md5',
    startUpgrade: 'Начать Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ðµ',
    urlRequired: 'URL Ð¾Ð±ÑÐ·Ð°Ñ‚елен',
    md5Required: 'md5 Ð¾Ð±ÑÐ·Ð°Ñ‚елен',
    urlInvalid: 'Введите ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚ный URL',
    md5Invalid: 'Введите ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚ный md5',
    upgradeConfirm: 'Подтвердить Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ðµ?',
    upgradeSuccess: 'Обновление ÑƒÑÐ¿ÐµÑˆÐ½Ð¾',
    clearFile: "Очистить Ñ„айл",
    uploading: 'Загрузка...',
    uploadAndUpgrade: 'Загрузить Ð¸ Ð¾Ð±Ð½Ð¾Ð²Ð¸Ñ‚ÑŒ',
    restartTips: 'Безопасный Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿ÑƒÑÐº, Ð´Ð°Ð½Ð½Ñ‹Ðµ Ð½Ðµ Ð¿Ð¾Ñ‚еряются',
    restarting: 'Перезапуск...',
    remoteTips: 'Удалённый ÐºÐ¾Ð½Ñ‚роль Ð´Ð¾ÑÑ‚упа',
    opening: 'Открывается...',
    resetTips: 'Сброс Ðº Ð·Ð°Ð²Ð¾Ð´ÑÐºÐ¸Ð¼, Ð´Ð°Ð½Ð½Ñ‹Ðµ ÑƒÐ´Ð°Ð»ÑÑ‚ся',
    reseting: 'Сбрасывается...',
    urlUpgrade: 'Обновление Ð¿Ð¾ URL',
    fileUpgrade: 'Обновление Ñ„айлом',
    uploadFile: 'Кликните Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ Ð¿Ñ€Ð¾ÑˆÐ¸Ð²ÐºÐ¸',
    formatFile: 'Поддержка .zip Ð¸Ð»Ð¸ .dpk, Ð´Ð¾ 20MB',
    fileName: 'Имя Ñ„айла',
    size: 'Размер',
  },
  config: {
    second: 'с',
    millisecond: 'мс',
    min: 'минут',
    notsave: 'Не ÑÐ¾Ñ…ранять',
    save: 'Сохранить',
    noVoice: 'Без Ð³Ð¾Ð»Ð¾ÑÐ°',
    no: 'Нет',
    yes: 'Да',
    basicConfiguration: 'Базовая ÐºÐ¾Ð½Ñ„игурация',
    displaySettings: 'Настройки ÑÐºÑ€Ð°Ð½Ð°',
    informationDisplay: 'Отображение Ð¸Ð½Ñ„о',
    audioSettings: 'Аудио',
    languageAndThemes: 'Язык Ð¸ Ñ‚ема',
    autoAdjustScreenBrightness: 'Автояркость',
    screenBrightness: 'Яркость ÑÐºÑ€Ð°Ð½Ð°',
    autoTurnOffScreen: 'Автовыключение ÑÐºÑ€Ð°Ð½Ð°',
    autoTurnOffScreenTime: 'Время Ð¾Ñ‚ключения',
    autoScreenSaver: 'Авто Ð·Ð°ÑÑ‚авка',
    autoScreenSaverTime: 'Время Ð·Ð°ÑÑ‚авки',
    displayDeviceSn: 'Показать SN',
    displayIp: 'Показать IP',
    displayIdentityCard: 'Показать Ð¾Ð±Ð»Ð°Ñ‡Ð½Ñ‹Ð¹ ÑÐµÑ€Ñ‚ификат',
    volume: 'Громкость',
    language: 'Язык',
    displayCode: "Показать ÐºÐ¾Ð´ Ð¼Ð¸Ð½Ð¸-приложения",
    themeMode: "Тема",
    cn: 'Китайский',
    en: 'Английский',
    es: 'Испанский',
    fr: 'Французский',
    de: 'Немецкий',
    ru: 'Русский',
    ar: 'Арабский',
    pt: 'Порт.',
    ko: 'Корейский',
    standardMode: 'Стандартный Ñ€ÐµÐ¶Ð¸Ð¼',
    simpleMode: 'Простой Ñ€ÐµÐ¶Ð¸Ð¼',
    firstLogin: 'Первый Ð²Ñ…од',
    backlight: 'Подсветка',
    brightness: 'Белая Ð¿Ð¾Ð´ÑÐ²ÐµÑ‚ка',
    nirBrightness: 'ИК-подсветка',
    never: 'Никогда',
    min1: '1 Ð¼Ð¸Ð½ÑƒÑ‚а',
    min2: '2 Ð¼Ð¸Ð½ÑƒÑ‚Ñ‹',
    min3: '3 Ð¼Ð¸Ð½ÑƒÑ‚Ñ‹',
    min4: '4 Ð¼Ð¸Ð½ÑƒÑ‚Ñ‹',
    min5: '5 Ð¼Ð¸Ð½ÑƒÑ‚',
    networkConfiguration: 'Сеть',
    otherConfiguration: 'Прочее',
    ipConfiguration: 'IP Ð½Ð°ÑÑ‚ройки',
    devicePassword: 'Пароль ÑƒÑÑ‚ройства',
    protocolPassword: 'Пароль Ð¿Ñ€Ð¾Ñ‚окола',
    networkType: 'Тип ÑÐµÑ‚и',
    ethernet: 'Ethernet',
    wifiName: 'Wi‑Fi',
    wifiPassword: 'Пароль Wi‑Fi',
    dhcpModeSelection: 'Режим DHCP',
    dhcpMode: 'Авто',
    customNetworkConfiguration: 'Ручной',
    ipAddress: 'IP-адрес',
    gateway: 'Шлюз',
    subnetMask: 'Маска Ð¿Ð¾Ð´ÑÐµÑ‚и',
    dnsServer: 'DNS ÑÐµÑ€Ð²ÐµÑ€',
    mac: 'MAC',
    mqttRelatedConfiguration: 'MQTT',
    mqttConnectionInformation: 'Подключение MQTT',
    sessionConfiguration: 'Сессия',
    serverAddress: 'Адрес ÑÐµÑ€Ð²ÐµÑ€Ð°',
    clientID: 'ID ÐºÐ»Ð¸ÐµÐ½Ñ‚а',
    userName: 'Имя Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚еля',
    userPassword: 'Пароль',
    topicPrefix: 'Префикс Ñ‚опика',
    onlineChecking: 'Онлайн-проверка',
    onlineCheckingTimeout: 'Тайм-аут',
    cleanSession: 'Очистить ÑÐµÑÑÐ¸ÑŽ',
    clientIdSuffix: 'Суффикс ID',
    willTopic: 'Will-топик',
    enterpriseWechat:'Режим Enterprise WeChat Ð½Ðµ Ð´ÐµÐ¹ÑÑ‚вует',
    faceRelatedConfiguration: 'Настройки Ð»Ð¸Ñ†Ð°',
    functionalInformation: 'Функция',
    prompt: 'Подсказка',
    faceSimilarityThreshold: 'Порог ÑÑ…ожести',
    livenessDetectionFunction: 'Проверка Ð¶Ð¸Ð²Ð¾ÑÑ‚и',
    livenessDetectionThreshold: 'Порог Ð¶Ð¸Ð²Ð¾ÑÑ‚и',
    infraredImageDisplay: "Показ Ð˜Ðš-изображения",
    maskRecognition: "Распознавание Ð¼Ð°ÑÐºÐ¸",
    strangerVoice: "Голос Ð½ÐµÐ·Ð½Ð°ÐºÐ¾Ð¼Ñ†Ð°",
    voiceMode: "Режим Ð³Ð¾Ð»Ð¾ÑÐ°",
    voiceModeDate: 'Пользовательское Ð¿Ñ€Ð¸Ð²ÐµÑ‚ствие',
    imageSaveType: "Тип ÑÐ¾Ñ…ранения",
    saveStrangerImage: "Сохранять Ð»Ð¸Ñ†Ð¾ Ð½ÐµÐ·Ð½Ð°ÐºÐ¾Ð¼Ñ†Ð°",
    fullView: "Панорама",
    face: "Лицо",
    broadcastPleaseRegisterFirst: 'Проигрывать "Сначала Ð·Ð°Ñ€ÐµÐ³Ð¸ÑÑ‚рируйтесь"',
    broadcastHelloStranger: 'Проигрывать "Незнакомец"',
    broadcastName: 'Проигрывать Ð¸Ð¼Ñ',
    broadcastGreeting: 'Проигрывать ÑÐ²Ð¾Ñ‘ Ð¿Ñ€Ð¸Ð²ÐµÑ‚ствие',
    greeting: 'Приветствие',
    broadcastWelcome: 'Проигрывать "Добро Ð¿Ð¾Ð¶Ð°Ð»Ð¾Ð²Ð°Ñ‚ÑŒ"',
    recognitionSwitch: 'Переключатель Ð¿Ð¾Ð²Ñ‚орной Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐºÐ¸',
    systemRelatedConfiguration: 'Система',
    functionSwitch: 'Переключатели',
    cardSwipingSwitch: 'Карта',
    passwordSwitch: 'Пароль',
    strangerImage: 'Фото Ð½ÐµÐ·Ð½Ð°ÐºÐ¾Ð¼Ñ†Ð°',
    cloudCertificateSwitch: 'Облачный ÑÐµÑ€Ñ‚ификат',
    physicalCardNumber: 'Номер ÐºÐ°Ñ€Ñ‚Ñ‹',
    cloudCertificateAcquisition: 'Получение Ð¾Ð±Ð»Ð°Ñ‡Ð½Ð¾Ð³Ð¾ ÑÐµÑ€Ñ‚ификата',
    heartbeatConfig: 'Heartbeat',
    heartbeatSwitch: 'Вкл./выкл. heartbeat',
    heartRateInterval: 'Интервал',
    heartbeatTopic: 'Топик heartbeat',
    heartbeatContent: 'Содержимое',
    basicInformation: 'Базовая Ð¸Ð½Ñ„ормация',
    deviceMac: 'MAC',
    uuid: 'UUID',
    sn: 'SN',
    model: 'Модель',
    version: "Версия",
    appVersion: "Версия Ð¿Ñ€Ð¾ÑˆÐ¸Ð²ÐºÐ¸",
    releaseTime: "Время Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ",
    totaldisk: 'Общая Ð¿Ð»Ð¾Ñ‰Ð°Ð´ÑŒ',
    freedisk: 'Оставшееся Ð¿Ñ€Ð¾ÑÑ‚ранство',
    passageConfiguration: 'Проход',
    functionConfiguration: 'Функции',
    numberOfPassageRecords: 'Макс. Ð·Ð°Ð¿Ð¸ÑÐµÐ¹ Ð¿Ñ€Ð¾Ñ…ода',
    durationOfRelayOpening: 'Время Ñ€ÐµÐ»Ðµ',
    alarmSwitch: 'Сигнализация',
    fireAlarmSwitch: 'Пожарная ÑÐ¸Ð³Ð½Ð°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ',
    fireAlarmStatus: 'Статус Ð¿Ð¾Ð¶Ð°Ñ€Ð°',
    normal: 'Норма',
    warning: 'Предупреждение',
    tamperSwitch: 'Тамперовка',
    uploadToCloudSwitch: 'Переключатель Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ Ð»Ð¸Ñ†Ð°',
    clockConfiguration: 'Часы',
    timeSynchronizationSwitch: 'Синхронизация Ð²Ñ€ÐµÐ¼ÐµÐ½Ð¸',
    timeSynchronizationServerIP: 'Сервер Ð²Ñ€ÐµÐ¼ÐµÐ½Ð¸',
    timedSynchronizationTime: 'Время ÑÐ¸Ð½Ñ…ронизации',
    timeZone: 'Часовой Ð¿Ð¾ÑÑ',
    setDeviceTime: 'Установить Ð²Ñ€ÐµÐ¼Ñ',
    restartAfterSetting: 'После ÑƒÑÑ‚ановки ÑƒÑÑ‚ройство Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿ÑƒÑÑ‚ится',
    cloudCertificateActivation: 'Активация ÑÐµÑ€Ñ‚ификата',
    activationKey: 'Ключ Ð°ÐºÑ‚ивации',
    cloudTips1: 'Введите ÐºÐ»ÑŽÑ‡ Ð±ÐµÐ· Ð¿Ñ€Ð¾Ð±ÐµÐ»Ð¾Ð²',
    cloudTips2: 'После Ð°ÐºÑ‚ивации Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚ся Ðº Ð¾Ð±Ð»Ð°Ñ‡Ð½Ð¾Ð¼Ñƒ ÑÐµÑ€Ð²Ð¸ÑÑƒ',
    confirmActivation: 'Подтвердить Ð°ÐºÑ‚ивацию',
    activationInProgress: 'Активация...',
    activationFailed: 'Сбой Ð°ÐºÑ‚ивации',
    activationSuccessful: 'Активация ÑƒÑÐ¿ÐµÑˆÐ½Ð°',
    passwordModification: 'Изменение Ð¿Ð°Ñ€Ð¾Ð»Ñ',
    password: 'Пароль',
    oldPassword: 'Старый Ð¿Ð°Ñ€Ð¾Ð»ÑŒ',
    newPassword: 'Новый Ð¿Ð°Ñ€Ð¾Ð»ÑŒ',
    confirmPassword: 'Подтвердите Ð¿Ð°Ñ€Ð¾Ð»ÑŒ',
    passwordRule: 'Рекомендации Ð¿Ð¾ Ð¿Ð°Ñ€Ð¾Ð»ÑŽ',
    passwordLength: 'Длина â‰¥6',
    cannotBeTheSame: 'Все ÑÐ¸Ð¼Ð²Ð¾Ð»Ñ‹ Ð½Ðµ Ð¼Ð¾Ð³ÑƒÑ‚ Ð±Ñ‹Ñ‚ÑŒ Ð¾Ð´Ð¸Ð½Ð°ÐºÐ¾Ð²Ñ‹Ð¼Ð¸',
    cannotOrder: 'Не Ð¼ÐµÐ½ÐµÐµ 3 Ð¿Ð¾Ð´Ñ€ÑÐ´ Ñ†Ð¸Ñ„Ñ€/букв',
    cannotWeakPassword: 'Не Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÑŒ ÑÐ»Ð°Ð±Ñ‹Ðµ Ð¿Ð°Ñ€Ð¾Ð»Ð¸',
    submit: 'Отправить',
    saveConfig: 'Сохранить',
    msg_please_enter: 'Введите ÑÐ¾Ð´ÐµÑ€Ð¶Ð°Ð½Ð¸Ðµ',
    msg_inputPassword: 'Введите Ð¿Ð°Ñ€Ð¾Ð»ÑŒ',
    msg_oldPasswordError: 'Старый Ð¿Ð°Ñ€Ð¾Ð»ÑŒ Ð½ÐµÐ²ÐµÑ€ÐµÐ½',
    msg_password_mismatch: 'Пароли Ð½Ðµ ÑÐ¾Ð²Ð¿Ð°Ð´Ð°ÑŽÑ‚',
    msg_password_min_length: 'Минимум 6 ÑÐ¸Ð¼Ð²Ð¾Ð»Ð¾Ð²',
    msg_is_weak_password: 'Слабый Ð¿Ð°Ñ€Ð¾Ð»ÑŒ, ÑÐ¼ÐµÐ½Ð¸Ñ‚е',
    msg_pswChangeSuccessAndLogin: 'Пароль Ð¸Ð·Ð¼ÐµÐ½Ñ‘н, Ð²Ð¾Ð¹Ð´Ð¸Ñ‚е ÑÐ½Ð¾Ð²Ð°',
    msg_pswChangeSuccess: 'Пароль Ð¸Ð·Ð¼ÐµÐ½Ñ‘н',
    msg_pswChangeFail: 'Не ÑƒÐ´Ð°Ð»Ð¾ÑÑŒ Ð¸Ð·Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ Ð¿Ð°Ñ€Ð¾Ð»ÑŒ',
    msg_saveSuccess: 'Успешно ÑÐ¾Ñ…ранено',
    msg_saveFail: 'Сохранение Ð½Ðµ ÑƒÐ´Ð°Ð»Ð¾ÑÑŒ',
    msg_formFilled: 'Проверьте Ñ„орму',
    msg_number_0_23: 'Только 0-23',
    msg_number_0_24: 'Только 0-24',
    msg_noChange: 'Нет Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹ ÐºÐ¾Ð½Ñ„игурации Ð´Ð»Ñ ÑÐ¾Ñ…ранения',
    resourceConfiguration: 'Настройки Ñ€ÐµÑÑƒÑ€ÑÐ¾Ð²',
    backgroundImage: 'Фоновое Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ðµ',
    selectImage: 'Выбрать Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ðµ',
    uploadBackground: 'Загрузить Ñ„он',
    uploading: 'Загрузка...',
    backgroundUploadTip: 'Загрузите Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ðµ Ð² Ñ„ормате PNG Ñ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸ÐµÐ¼ {n} Ð¿Ð¸ÐºÑÐµÐ»ÐµÐ¹, Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ðµ Ð±ÑƒÐ´ÐµÑ‚ Ð¿Ñ€ÐµÐ¾Ð±Ñ€Ð°Ð·Ð¾Ð²Ð°Ð½Ð¾ Ð² Base64 Ð¸ Ð·Ð°Ð³Ñ€ÑƒÐ¶ÐµÐ½Ð¾ Ð½Ð° ÑƒÑÑ‚ройство',
    backgroundResolutionMismatch: 'Разрешение Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð´Ð¾Ð»Ð¶Ð½Ð¾ Ð±Ñ‹Ñ‚ÑŒ {n}',
    backgroundRequired: 'Выберите Ñ„оновое Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ðµ',
    backgroundImageOnlyPNG: 'Только PNG',
    backgroundSizeLimit: 'Размер â‰¤ 5MB',
    backgroundParseFailed: 'Не ÑƒÐ´Ð°Ð»Ð¾ÑÑŒ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚ать Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ðµ',
    backgroundImageSelected: 'Изображение Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð¾',
    backgroundSuccess: 'Фон Ð·Ð°Ð³Ñ€ÑƒÐ¶ÐµÐ½',
    backgroundFailed: 'Ошибка Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ Ñ„она',
    scanSettings: 'Настройки ÑÐºÐ°Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ',
    scanSwitch: 'Переключатель ÑÐºÐ°Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ',
    scanInterval: 'Интервал ÑÐºÐ°Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ',
  },
  person: {
    idCard: 'Номер ÑƒÐ´Ð¾ÑÑ‚оверения',
    userType: 'Тип Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚еля',
    administrator: 'Администратор',
    userId: 'ID',
    user: 'Пользователь',
    voucher: 'Удостоверение',
    permission: 'Разрешение',
    addUser: 'Добавить Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚еля',
    name: 'Имя',
    editUser: 'Редактировать',
    placeholderUserId: 'Введите ID',
    placeholderName: 'Введите Ð¸Ð¼Ñ',
    userNotExist: 'Пользователь Ð½Ðµ Ð½Ð°Ð¹Ð´ÐµÐ½',
    oneClickClear: 'Очистить Ð²ÑÐµ',
    clearTips: 'Будут ÑƒÐ´Ð°Ð»ÐµÐ½Ñ‹ Ð²ÑÐµ Ð´Ð°Ð½Ð½Ñ‹Ðµ, Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶Ð¸Ñ‚ÑŒ?',
    clearSuccess: 'Успешно Ð¾Ñ‡Ð¸Ñ‰ÐµÐ½Ð¾',
    clearFailed: 'Не ÑƒÐ´Ð°Ð»Ð¾ÑÑŒ Ð¾Ñ‡Ð¸ÑÑ‚ить',
  },
  voucher: {
    password: 'Пароль',
    card: 'Карта',
    face: 'Лицо',
    finger: 'Отпечаток',
    code: 'Код',
    codeType: 'Тип ÐºÐ¾Ð´Ð°',
    passthroughCode: 'Сквозной ÐºÐ¾Ð´',
    staticCode: 'Статический ÐºÐ¾Ð´',
    dynamicCode: 'Динамический ÐºÐ¾Ð´',
    credentialId: 'ID ÑƒÑ‡ÐµÑ‚ных Ð´Ð°Ð½Ð½Ñ‹Ñ…',
    credentialValue: 'Значение ÑƒÑ‡ÐµÑ‚ных Ð´Ð°Ð½Ð½Ñ‹Ñ…',
    placeholderCode: 'Пожалуйста, Ð²Ð²ÐµÐ´Ð¸Ñ‚е ÐºÐ¾Ð´ ÑÐµÑ€Ñ‚ификата',
    placeholderPwd: 'Введите Ð¿Ð°Ñ€Ð¾Ð»ÑŒ',
    placeholderCard: 'Введите ÐºÐ°Ñ€Ñ‚у',
    validPassword: 'Введите 6 Ñ†Ð¸Ñ„Ñ€',
    validCard: 'Введите 8 Ñ†Ð¸Ñ„Ñ€ Ð¸Ð»Ð¸ Ð±ÑƒÐºÐ²',
    photoRegistration: 'Регистрация Ñ„ото',
    featureValueRegistration: 'Регистрация Ð¿Ñ€Ð¸Ð·Ð½Ð°ÐºÐ¾Ð²',
    fingerRegistration: 'Регистрация Ð¾Ñ‚печатка',
    fingerFeatureRegistration: 'Регистрация Ð¿Ð¾ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸ÑŽ Ð¿Ñ€Ð¸Ð·Ð½Ð°ÐºÐ°',
    fingerInput: 'Приложите Ð¿Ð°Ð»ÐµÑ† Ðº ÑÐºÐ°Ð½ÐµÑ€Ñƒ Ð¾Ñ‚печатков',
    fingerRemainingTime: 'Оставшееся Ð²Ñ€ÐµÐ¼Ñ',
    fingerInputting: 'Идёт Ð²Ð²Ð¾Ð´...',
    startFingerInput: 'Начать Ð²Ð²Ð¾Ð´ Ð¾Ñ‚печатка',
    fingerInputTips: 'Введите Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ Ð¿Ñ€Ð¸Ð·Ð½Ð°ÐºÐ° Ð¾Ñ‚печатка',
    fingerWaitInput: 'Ожидание Ð²Ð²Ð¾Ð´Ð°',
    fingerInputNow: 'Идёт Ð²Ð²Ð¾Ð´ Ð¾Ñ‚печатка...',
    fingerInputSuccess: 'Ввод ÑƒÑÐ¿ÐµÑˆÐµÐ½',
    fingerInputFailed: 'Не ÑƒÐ´Ð°Ð»Ð¾ÑÑŒ Ð·Ð°Ð¿ÑƒÑÑ‚ить Ð²Ð²Ð¾Ð´ Ð¾Ñ‚печатка',
    fingerReTry: 'Ошибка Ð²Ð²Ð¾Ð´Ð° Ð¾Ñ‚печатка, Ð¿Ð¾Ð¿Ñ€Ð¾Ð±ÑƒÐ¹Ñ‚е ÐµÑ‰Ñ‘ Ñ€Ð°Ð·',
    fingerFilled: 'Ввод Ð¾Ñ‚печатка ÑƒÑÐ¿ÐµÑˆÐµÐ½, Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ Ð¿Ñ€Ð¸Ð·Ð½Ð°ÐºÐ° Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¾ Ð°Ð²Ñ‚оматически',
    fingerFailed: 'Ошибка Ð²Ð²Ð¾Ð´Ð° Ð¾Ñ‚печатка',
    fingerTimeout: 'Время Ð²Ñ‹ÑˆÐ»Ð¾',
    fingerInputTimeout: 'Время Ð²Ð²Ð¾Ð´Ð° Ð¾Ñ‚печатка Ð¸ÑÑ‚екло, Ð¿Ð¾Ð¿Ñ€Ð¾Ð±ÑƒÐ¹Ñ‚е ÐµÑ‰Ñ‘ Ñ€Ð°Ð·',
    fingerError: 'Ввод Ð½Ðµ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½',
    fingerInputError: 'Ошибка Ð²Ð²Ð¾Ð´Ð° Ð¾Ñ‚печатка, Ð¿Ð¾Ð¿Ñ€Ð¾Ð±ÑƒÐ¹Ñ‚е ÐµÑ‰Ñ‘ Ñ€Ð°Ð·',
    fingerInputed: 'Отпечаток ÑƒÐ¶Ðµ Ð·Ð°Ñ€ÐµÐ³Ð¸ÑÑ‚рирован',
    fingerReInput: 'Повторно Ð²Ð²ÐµÑÑ‚и Ð¾Ñ‚печаток',
  },
  permission: {
    deletePermission: 'Удалить Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ðµ',
    addPermission: 'Добавить Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ðµ',
    permissionId: 'ID Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ',
    userId: 'ID Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚еля',
    timeRange: 'Временной Ð´Ð¸Ð°Ð¿Ð°Ð·Ð¾Ð½',
    extra: 'Дополнительно',
    effectiveType: 'Тип Ð´ÐµÐ¹ÑÑ‚вия',
    effectiveTime: 'Время Ð´ÐµÐ¹ÑÑ‚вия',
    effectiveWeek: 'Неделя Ð´ÐµÐ¹ÑÑ‚вия',
    timePeriod: 'Период',
    addTimePeriod: 'Добавить Ð¿ÐµÑ€Ð¸Ð¾Ð´',
    modify_previous_time: 'Сначала Ð¸Ð·Ð¼ÐµÐ½Ð¸Ñ‚е Ð¿Ñ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð¸Ð¹ Ð¿ÐµÑ€Ð¸Ð¾Ð´',
    cannot_be_earlier: 'Конец Ð½Ðµ Ð¼Ð¾Ð¶ÐµÑ‚ Ð±Ñ‹Ñ‚ÑŒ Ñ€Ð°Ð½ÑŒÑˆÐµ Ð½Ð°Ñ‡Ð°Ð»Ð°',
    times_cannot_overlap: 'Время Ð½Ðµ Ð´Ð¾Ð»Ð¶Ð½Ð¾ Ð¿ÐµÑ€ÐµÑÐµÐºÐ°Ñ‚ься',
    choose_time_range: 'Выберите Ð´Ð¸Ð°Ð¿Ð°Ð·Ð¾Ð½',
    unlimitedMode: 'Без Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ð¹',
    usualMode: 'Обычный Ñ€ÐµÐ¶Ð¸Ð¼',
    dailyMode: 'Ежедневный',
    weeklyRepetitionMode: 'Еженедельный',
    time_range: 'Диапазон Ð²Ñ€ÐµÐ¼ÐµÐ½Ð¸',
  },
  common: {
    startDate: 'Дата Ð½Ð°Ñ‡Ð°Ð»Ð°',
    endDate: 'Дата Ð¾ÐºÐ¾Ð½Ñ‡Ð°Ð½Ð¸Ñ',
    to: 'до',
    cancel: 'Отмена',
    confirm: 'Подтвердить',
    close: 'Закрыть',
    delete: 'Удалить',
    edit: 'Изменить',
    batchDelete: 'Пакетное ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ðµ',
    startTime: 'Время Ð½Ð°Ñ‡Ð°Ð»Ð°',
    endTime: 'Время Ð¾ÐºÐ¾Ð½Ñ‡Ð°Ð½Ð¸Ñ',
    monday: 'Понедельник',
    tuseday: 'Вторник',
    wednesday: 'Среда',
    thursday: 'Четверг',
    friday: 'Пятница',
    saterday: 'Суббота',
    sunday: 'Воскресенье',
    placeholder: 'Пожалуйста, Ð²Ð²ÐµÐ´Ð¸Ñ‚е',
    placeholderSelect: 'Пожалуйста, Ð²Ñ‹Ð±ÐµÑ€Ð¸Ñ‚е',
    closeTips: 'Подтвердить Ð·Ð°ÐºÑ€Ñ‹Ñ‚ие?',
    deleteTips: 'Подтвердить ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ðµ?',
    deleteSuccess: 'Удалено ÑƒÑÐ¿ÐµÑˆÐ½Ð¾',
    addSuccess: 'Успешно Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¾',
    editSuccess: 'Успешно Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¾',
    saveSuccess: 'Сохранено',
    tips: 'Подсказка',
    operation: 'Операция',
    query: 'Запрос',
    reset: 'Сброс',
    noData: 'Нет Ð´Ð°Ð½Ð½Ñ‹Ñ…',
    export: 'Экспорт',
    success: 'Успех',
    failure: 'Неудача',
    incorrectFormat: 'Неверный Ñ„ормат',
    integerFormat: 'Целое Ñ‡Ð¸ÑÐ»Ð¾ â‰¥0',
    positiveIntegerFormat: 'Целое Ñ‡Ð¸ÑÐ»Ð¾ >0',
    noDataSaved: 'Нет Ð´Ð°Ð½Ð½Ñ‹Ñ… Ð´Ð»Ñ ÑÐ¾Ñ…ранения',
    chinese: 'Китайский',
    english: 'Английский',
    spanish: 'Испанский',
    french: 'Французский',
    german: 'Немецкий',
    russian: 'Русский',
    arabic: 'Арабский',
    portuguese: 'Португальский',
    korean: 'Корейский',
    detail: 'Подробности',
    clearTips: 'Подтвердить Ð¾Ñ‡Ð¸ÑÑ‚ку?',
    clearSuccess: 'Успешно Ð¾Ñ‡Ð¸Ñ‰ÐµÐ½Ð¾',
  },
  log: {
    accessMethod: 'Способ Ð´Ð¾ÑÑ‚упа',
    passingTime: 'Время Ð¿Ñ€Ð¾Ñ…ода',
    accessPass: 'Удостоверение',
    accessResult: 'Результат',
    accessPhoto: 'Фото',
    viewPhotos: 'Просмотр Ñ„ото'
  },
  error: {
    networkError: 'Сбой ÑÐµÑ‚и, Ð¿Ñ€Ð¾Ð²ÐµÑ€ÑŒÑ‚е Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ðµ',
    timeout: 'Тайм-аут, Ð¿Ð¾Ð¿Ñ€Ð¾Ð±ÑƒÐ¹Ñ‚е ÑÐ½Ð¾Ð²Ð°',
    serverError: 'Внутренняя Ð¾ÑˆÐ¸Ð±ÐºÐ° ÑÐµÑ€Ð²ÐµÑ€Ð°',
    notFound: 'Ресурс Ð½Ðµ Ð½Ð°Ð¹Ð´ÐµÐ½',
    unauthorized: 'Не Ð°Ð²Ñ‚оризован, Ð²Ð¾Ð¹Ð´Ð¸Ñ‚е ÑÐ½Ð¾Ð²Ð°',
    noResponse: 'Нет Ð¾Ñ‚вета ÑÐµÑ€Ð²ÐµÑ€Ð°',
    unknownError: 'Ошибка Ð·Ð°Ð¿Ñ€Ð¾ÑÐ°, ÐºÐ¾Ð´:',
    requestFailed: 'Запрос Ð½Ðµ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½'
  },
  security: {
    keyId: 'ID ÐºÐ»ÑŽÑ‡Ð°',
    keyType: 'Тип ÐºÐ»ÑŽÑ‡Ð°',
    keyEncoding: 'Кодировка ÐºÐ»ÑŽÑ‡Ð°',
    keyValue: 'Значение ÐºÐ»ÑŽÑ‡Ð°',
    startTime: 'Время Ð½Ð°Ñ‡Ð°Ð»Ð°',
    expirationTime: 'Время Ð¸ÑÑ‚ечения',
    newKey: 'Добавить ÐºÐ»ÑŽÑ‡',
    clearKey: 'Очистить ÐºÐ»ÑŽÑ‡',
    validTime: 'Срок Ð´ÐµÐ¹ÑÑ‚вия',
  }
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/language/zh.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,452 @@
export default {
  // ç™»å½•页
  login: {
    lang: '语言',
    systemname: '人脸设备后台登录',
    username: '用户名',
    username_label: '请输入用户名',
    pwd: '密码',
    pwd_label: '请输入密码',
    pwd_info: '请输入正确密码格式',
    success_msg: '登录成功',
    error_name: '密码错误',
    error_res: '不符合登录要求',
    login: '登录',
  },
  // ä¾§è¾¹æ éƒ¨åˆ†
  aside: {
    systemname: '人脸设备后台',
    quit: '退出',
    deviceControl: '设备控制',
    basicSetting: '基础设置',
    workerSetting: '人员设置',
    deviceMonitoring: '设备监控',
    recordManagement: '记录管理',
    securityManagement: '密钥管理',
    tips: '提示',
    tips_msg: '是否退出登录',
  },
  // è®¾å¤‡æŽ§åˆ¶é¡µé¢
  control: {
    remoteControl: '远程控制',
    restart: '设备重启',
    clickToRestart: '点击重启设备',
    restartConfirm: '确定要重启设备吗?',
    restartSuccess: '重启成功',
    restartFailed: '重启失败',
    remoteOpen: '远程开门',
    clickToOpen: '点击远程开门',
    openConfirm: '确定要远程开门吗?',
    remoteOpenSuccess: '远程开门成功',
    remoteOpenFailed: '远程开门失败',
    reset: '设备重置',
    clickToReset: '点击重置设备',
    resetConfirm: '确定要重置设备吗?',
    resetWillOut: "重置后将退出登录",
    resetSuccess: '设备重置成功',
    resetFailed: '设备重置失败',
    firmwareUpgrade: '设备升级',
    upgradeConfig: '升级配置',
    firmwareUrl: '固件地址',
    md5Checksum: 'md5值',
    startUpgrade: '开始升级',
    urlRequired: '文件地址必填',
    md5Required: 'md5值必填',
    urlInvalid: '请输入正确的地址',
    md5Invalid: '请输入正确的md5',
    upgradeConfirm: '确定升级设备吗?',
    upgradeSuccess: '设备升级成功',
    clearFile: "清除文件",
    uploading: '上传升级中...',
    uploadAndUpgrade: '上传并升级',
    restartTips: '安全重启设备系统,不会丢失数据',
    restarting: '重启中...',
    remoteTips: '远程控制门禁设备开启',
    opening: '开门中...',
    resetTips: '恢复出厂设置,清除所有数据',
    reseting: '重置中...',
    urlUpgrade: 'URL升级方式',
    fileUpgrade: '文件上传升级',
    uploadFile: '点击上传固件文件',
    formatFile: '支持 .zip æˆ– .dpk æ ¼å¼æ–‡ä»¶ï¼Œæœ€å¤§ 20MB',
    fileName: '文件名',
    size: '大小',
  },
  // åŸºç¡€è®¾ç½®é¡µé¢
  config: {
    second: '秒',
    millisecond: '毫秒',
    min: '分钟',
    notsave: '不保存',
    save: '保存',
    noVoice: '无语音',
    no: '否',
    yes: '是',
    // åŸºç¡€é…ç½®
    basicConfiguration: '基础配置',
    displaySettings: '显示设置',
    informationDisplay: '信息显示',
    audioSettings: '音频设置',
    languageAndThemes: '语言与主题',
    autoAdjustScreenBrightness: '自动调节屏幕亮度',
    screenBrightness: '屏幕亮度',
    autoTurnOffScreen: '自动息屏',
    autoTurnOffScreenTime: '自动息屏时间',
    autoScreenSaver: '自动屏保',
    autoScreenSaverTime: '自动屏保时间',
    displayDeviceSn: '显示SN',
    displayIp: '显示IP',
    displayIdentityCard: '显示云证',
    volume: '音量',
    language: '语言',
    displayCode: "显示小程序码",
    themeMode: "工作主题",
    cn: '中文',
    en: '英文',
    es: '西班牙语',
    fr: '法语',
    de: '德语',
    ru: '俄语',
    ar: '阿拉伯语',
    pt: '葡萄牙语',
    ko: '韩语',
    standardMode: '标准模式',
    simpleMode: '简约模式',
    firstLogin: '第一次登录后台',
    backlight: '屏幕背光',
    brightness: '白色补光灯',
    nirBrightness: '红外补光灯',
    never: '永不',
    min1: '1分钟',
    min2: '2分钟',
    min3: '3分钟',
    min4: '4分钟',
    min5: '5分钟',
    // ç½‘络配置
    networkConfiguration: '网络配置',
    otherConfiguration: '其他配置',
    ipConfiguration: 'IP配置',
    devicePassword: '设备密码 ',
    protocolPassword: '通信协议密码',
    networkType: '网络类型 ',
    ethernet: '以太网',
    wifiName: 'Wi-Fi名',
    wifiPassword: 'Wi-Fi密码',
    dhcpModeSelection: 'DHCP模式',
    dhcpMode: '自动获取',
    customNetworkConfiguration: '手动配置',
    ipAddress: 'ip地址',
    gateway: '网关',
    subnetMask: '子网掩码',
    dnsServer: 'DNS服务器',
    mac: '网络mac',
    // MQTT配置
    mqttRelatedConfiguration: 'MQTT配置',
    mqttConnectionInformation: 'MQTT连接信息',
    sessionConfiguration: '会话配置',
    serverAddress: '服务器地址',
    clientID: '客户端ID',
    userName: '用户名',
    userPassword: '用户密码',
    topicPrefix: '主题前缀',
    onlineChecking: '在线验证',
    onlineCheckingTimeout: "在线验证超时",
    cleanSession: '清除会话',
    clientIdSuffix: '客户端ID后缀',
    willTopic: '遗嘱主题',
    enterpriseWechat:'企微模式无效',
    // äººè„¸é…ç½®
    faceRelatedConfiguration: '人脸配置',
    functionalInformation: '功能信息',
    prompt: '提示语',
    faceSimilarityThreshold: '人脸相似度阈值',
    livenessDetectionFunction: '活体检测功能',
    livenessDetectionThreshold: '活体检测阈值',
    infraredImageDisplay: "红外图像显示",
    maskRecognition: "口罩识别",
    strangerVoice: "陌生人语音",
    voiceMode: "语音模式",
    voiceModeDate: '自定义问候语',
    imageSaveType: "图像保存类型",
    saveStrangerImage: "保存陌生人图像",
    fullView: "全景",
    face: "人脸",
    broadcastPleaseRegisterFirst: '播放请先注册刷脸凭证',
    broadcastHelloStranger: '播放未登记人员',
    broadcastName: '播放名字',
    broadcastGreeting: '播放自定义问候语',
    greeting: '问候语  ',
    broadcastWelcome: '播放欢迎光临',
    recognitionSwitch: '重检开关',
    // ç³»ç»Ÿé…ç½®
    systemRelatedConfiguration: '系统配置',
    functionSwitch: '功能开关',
    cardSwipingSwitch: '刷卡开关',
    passwordSwitch: '密码开关',
    strangerImage: '陌生人图片开关',
    cloudCertificateSwitch: '云证开关',
    physicalCardNumber: '物理卡号',
    cloudCertificateAcquisition: '云证获取',
    heartbeatConfig: '心跳设置',
    heartbeatSwitch: '心跳开关',
    heartRateInterval: '心跳间隔',
    heartbeatTopic: '心跳主题',
    heartbeatContent: '心跳内容',
    basicInformation: '基础信息',
    deviceMac: 'mac地址',
    uuid: '设备uuid',
    sn: '设备sn号',
    model: '设备型号',
    version: "版本号",
    appVersion: "固件版本号",
    releaseTime: "更新时间",
    totaldisk: '设备总空间',
    freedisk: '剩余空间',
    // é€šè¡Œé…ç½®
    passageConfiguration: '通行配置',
    functionConfiguration: '功能配置',
    numberOfPassageRecords: '通行记录最大数量',
    durationOfRelayOpening: '继电器打开时长',
    alarmSwitch: '报警开关',
    fireAlarmSwitch: '火警开关',
    fireAlarmStatus: '火警状态',
    normal: '正常',
    warning: '预警',
    tamperSwitch: '防拆报警开关',
    uploadToCloudSwitch: '人脸上报开关',
    // æ—¶é’Ÿé…ç½®
    clockConfiguration: '时钟配置',
    timeSynchronizationSwitch: '对时开关',
    timeSynchronizationServerIP: '对时服务器IP',
    timedSynchronizationTime: '定时同步时间',
    timeZone: '时区',
    setDeviceTime: '设置设备时间',
    restartAfterSetting: '操作后设备自动重启',
    // äº‘证激活
    cloudCertificateActivation: '云证激活',
    activationKey: '激活密钥',
    cloudTips1: '请输入激活密钥,确保没有空格',
    cloudTips2: '激活成功后设备将连接到云认证服务',
    confirmActivation: '确认激活',
    activationInProgress: '激活中...',
    activationFailed: '激活失败',
    activationSuccessful: '激活成功',
    // å¯†ç ä¿®æ”¹
    passwordModification: '密码修改',
    password: '密码',
    oldPassword: '旧密码',
    newPassword: '新密码',
    confirmPassword: '确认密码',
    passwordRule: '密码规则推荐',
    passwordLength: '长度≥6',
    cannotBeTheSame: '不能所有字符相同',
    cannotOrder: '不能包含至少3个连续数字或小写字母序列(升序或降序)',
    cannotWeakPassword: '不能是常见弱密码,包括',
    submit: '提交',
    saveConfig: '保存设置',
    // js中msg提示语
    msg_please_enter: '请输入内容',
    msg_inputPassword: '请输入密码',
    msg_oldPasswordError: '旧密码错误',
    msg_password_mismatch: '两次输入的密码不一致',
    msg_password_min_length: '密码长度至少为6位',
    msg_is_weak_password: '此密码为弱密码,请重新设置',
    msg_pswChangeSuccessAndLogin: '密码修改成功,请重新登录',
    msg_pswChangeSuccess: '密码修改成功',
    msg_pswChangeFail: '密码修改失败',
    msg_saveSuccess: '保存成功',
    msg_saveFail: '保存失败',
    msg_formFilled: '请检查表单填写是否正确',
    msg_number_0_23: '仅支持0到23',
    msg_number_0_24: '仅支持0到24',
    msg_noChange: '没有需要保存的配置变更',
    // èµ„源配置
    resourceConfiguration: '资源配置',
    backgroundImage: '背景图片',
    selectImage: '选择图片',
    uploadBackground: '上传背景',
    uploading: '上传中...',
    backgroundUploadTip: '请上传 åƒç´ ä¸º{n}的PNG格式 çš„图片,图片将转换为 Base64 æ ¼å¼åŽä¸Šä¼ åˆ°è®¾å¤‡',
    backgroundResolutionMismatch: '图片分辨率需为 {n}',
    backgroundRequired: '请先选择背景图片',
    backgroundImageOnlyPNG: '请上传 PNG æ ¼å¼çš„图片',
    backgroundSizeLimit: '图片大小不能超过 5MB',
    backgroundParseFailed: '图片读取失败,请重试',
    backgroundImageSelected: '图片选择成功',
    backgroundSuccess: '背景上传成功',
    backgroundFailed: '背景上传失败',
    // æ‰«ç è®¾ç½®
    scanSettings: '扫码设置',
    scanSwitch: '扫码开关',
    scanInterval: '扫码间隔',
  },
  // äººå‘˜è®¾ç½®
  person: {
    idCard: '身份证号',
    userType: '人员类型',
    administrator: '管理员',
    userId: '人员ID',
    user: '人员',
    voucher: '凭证',
    permission: '权限',
    addUser: '添加人员',
    name: '姓名',
    editUser: '编辑人员',
    placeholderUserId: '请输入人员ID',
    placeholderName: '请输入人员姓名',
    userNotExist: '人员不存在',
    oneClickClear: '一键清空',
    clearTips: '此操作将永久删除所有人员、凭证和权限数据,是否继续?',
    clearSuccess: '清空成功',
    clearFailed: '清空失败',
  },
  voucher: {
    password: '密码',
    card: '卡片',
    face: '人脸',
    finger: '指纹',
    code: '码',
    codeType: '码类型',
    passthroughCode: '透传码',
    staticCode: '静态码',
    dynamicCode: '动态码',
    credentialId: '凭证ID',
    credentialValue: '凭证值',
    placeholderCode: '请输入码凭证',
    placeholderPwd: '请输入密码凭证',
    placeholderCard: '请输入卡片凭证',
    validPassword: '请输入6位数字',
    validCard: '请输入数字或字母',
    photoRegistration: '照片注册',
    featureValueRegistration: '特征值注册',
    fingerRegistration: '指纹注册',
    fingerFeatureRegistration: '特征值注册',
    fingerInput: '请将手指放在指纹采集器上',
    fingerRemainingTime: '剩余时间',
    fingerInputting: '录入中...',
    startFingerInput: '开始录入指纹',
    fingerInputTips: '请输入指纹特征值',
    fingerWaitInput: '等待录入',
    fingerInputNow: '正在录入指纹...',
    fingerInputSuccess: '录入成功',
    fingerInputFailed: '指纹录入启动失败',
    fingerReTry: '指纹录入失败,请重试',
    fingerFilled: '指纹录入成功,特征值已自动填充',
    fingerFailed: '指纹录入失败',
    fingerTimeout: '录入超时',
    fingerInputTimeout: '指纹录入超时,请重试',
    fingerError: '录入失败',
    fingerInputError: '指纹录入失败,请重试',
    fingerInputed: '指纹已录入',
    fingerReInput: '重新录入指纹',
  },
  permission: {
    deletePermission: '删除权限',
    addPermission: '添加权限',
    permissionId: '权限ID',
    userId: '人员ID',
    timeRange: '时间区间',
    extra: '额外属性',
    effectiveType: '有效类型',
    effectiveTime: '生效时段',
    effectiveWeek: '生效周期',
    timePeriod: '时间段',
    addTimePeriod: '添加时间段',
    modify_previous_time: '请先修改上一个添加的时间段',
    cannot_be_earlier: '结束时间不可小于开始时间',
    times_cannot_overlap: '所选时间不可重叠',
    choose_time_range: '请选择生效时间范围',
    unlimitedMode: '无限制',
    usualMode: '通常模式',
    dailyMode: '每日模式',
    weeklyRepetitionMode: '周重复模式',
    time_range: '时段',
  },
  common: {
    startDate: '开始日期',
    endDate: '结束日期',
    to: '至',
    cancel: '取消',
    confirm: '确认',
    close: '关闭',
    delete: '删除',
    edit: '编辑',
    batchDelete: '批量删除',
    startTime: '开始时间',
    endTime: '结束时间',
    monday: '星期一',
    tuseday: '星期二',
    wednesday: '星期三',
    thursday: '星期四',
    friday: '星期五',
    saterday: '星期六',
    sunday: '星期日',
    placeholder: '请输入',
    placeholderSelect: '请选择',
    closeTips: '确认关闭吗?',
    deleteTips: '确认删除吗?',
    deleteSuccess: '删除成功',
    addSuccess: '新增成功',
    editSuccess: '编辑成功',
    saveSuccess: '保存成功',
    tips: '提示',
    operation: '操作',
    query: '查询',
    reset: '重置',
    noData: '暂无数据',
    export: '导出',
    success: '成功',
    failure: '失败',
    incorrectFormat: '格式不正确',
    integerFormat: '只能为大于等于0的整数',
    positiveIntegerFormat: '只能为大于0的整数',
    noDataSaved: '没有数据需要保存',
    chinese: '中文',
    english: '英文',
    spanish: '西班牙语',
    french: '法语',
    german: '德语',
    russian: '俄语',
    arabic: '阿拉伯语',
    portuguese: '葡萄牙语',
    korean: '韩语',
    detail: '详情',
    clearTips: '确认清空吗?',
    clearSuccess: '清空成功',
  },
  log: {
    accessMethod: '通行方式',
    passingTime: '通行时间',
    accessPass: '通行凭证',
    accessResult: '通行结果',
    accessPhoto: '通行照片',
    viewPhotos: '查看照片'
  },
  error: {
    networkError: '网络请求失败,请检查网络连接',
    timeout: '请求超时,请检查网络连接或稍后重试',
    serverError: '服务器内部错误,请稍后重试',
    notFound: '请求的资源不存在',
    unauthorized: '未授权,请重新登录',
    noResponse: '无法连接到服务器,请检查网络连接或服务器状态',
    unknownError: '请求失败,错误代码:',
    requestFailed: '请求失败'
  },
  // å¯†é’¥ç®¡ç†
  security: {
    keyId: '密钥ID',
    keyType: '密钥类型',
    keyEncoding: '密钥编码',
    keyValue: '密钥值',
    startTime: '开始时间',
    expirationTime: '过期时间',
    newKey: '新增密钥',
    clearKey: '清空密钥',
    validTime: '有效时间',
  }
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/main.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import http from './utils/request'
import i18n from './language'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
// å¼•入字体图标
// fontclass
import './assets/font/iconfont.css'
// Symbol
import './assets/font/iconfont'
import Blob from '@/excel/Blob.js'
import Export2Excel from '@/excel/Export2Excel.js'
import modelPermission from '@/directives/model-permission'
// å¼¹æ¡†æŒ‰é’®
import {
  Message
} from 'element-ui';
let publicConfig = sessionStorage.getItem('publicConfig')
let { language } = publicConfig ? JSON.parse(publicConfig) : {}
if (language === 'CN' && document.title !== '人脸系统') {
  document.title = '人脸系统'
}
if (language === 'EN' && document.title !== 'Face device system') {
  document.title = 'Face device system'
}
Vue.prototype.$Message = Message
Vue.use(ElementUI);
Vue.config.productionTip = false
Vue.prototype.$http = http
Vue.directive('model-permission', modelPermission)
new Vue({
  Export2Excel,
  Blob,
  i18n,
  router,
  render: h => h(App),
}).$mount('#app')
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/router.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
import Vue from 'vue'
import Router from 'vue-router'
import login from './views/login/index.vue'
import home from './views/home/index.vue'
import config from './views/config/index.vue'
import control from './views/control/index.vue'
import person from './views/person/index.vue'
import monitor from './views/monitor/index.vue'
import record from './views/record/index.vue'
import security from './views/security/index.vue'
Vue.use(Router)
export default new Router({
    routes: [{
        path: '/',
        name: 'login',
        redirect: "login"
    },
    {
        path: '/login',
        name: 'login',
        component: login
    },
    {
        path: '/home',
        name: 'home',
        component: home,
        redirect: '/config',
        children: [
            {
                path: '/monitor',
                name: 'monitor',
                component: monitor
            }, {
                path: '/config',
                name: 'config',
                component: config,
            }, {
                path: '/control',
                name: 'control',
                component: control,
            }, {
                path: '/person',
                name: 'person',
                component: person,
            }, {
                path: '/record',
                name: 'record',
                component: record,
            }, {
                path: '/security',
                name: 'security',
                component: security,
            }],
    },
    ]
})
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/utils/bus.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,5 @@
//全局的event bus,用于发送和接收消息
import Vue from 'vue'
const bus = new Vue()
export default bus
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/utils/export.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,99 @@
/*
 * @Author: your name
 * @Date: 2020-09-14 15:13:28
 * @LastEditTime: 2020-09-24 11:39:53
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \webadmin\src\utils\export.js
 */
import {
  MessageBox,
  Message,
  Notification
} from 'element-ui'
import axios from 'axios'
// å¯¼å‡º
export function outExcel(name, url, data, info) {
  MessageBox.confirm(name, "提示", {
      type: "warning"
    }).then(async () => {
      Notification.info({
        title: '提示',
        message: '正在导出,请稍后 <i class="el-icon-loading" style="font-size:20px"></i>',
        dangerouslyUseHTMLString: true,
        position: 'bottom-left',
        duration: 0
      })
      const time = data;
      let formData = new FormData();
      if (time != null) {
        for (var p in time) {
          formData.append(p, time[p]);
        }
      }
      axios({
          method: "post",
          url: process.env.VUE_APP_BASE_API + url,
          data: formData,
          headers: {
            Authorization: "token " + JSON.parse(sessionStorage.getItem("UserInfo")).token,
            "Content-Type": "multipart/form-data"
          },
          responseType: "blob"
        })
        .then(data => {
          if (data.data.type === "application/json") {
            var reader = new FileReader();
            reader.onloadend = function () {
              let res = JSON.parse(reader.result);
              if (res && res.msg) {
                Message.warning(res.msg + "," + res.data);
                setTimeout(() => {
                  Notification.closeAll()
                }, 1000);
              }
            };
            reader.readAsText(data.data);
            return;
          }
          let url = window.URL.createObjectURL(new Blob([data.data]));
          let link = document.createElement("a");
          link.style.display = "none";
          link.href = url;
          link.setAttribute("download", info);
          document.body.appendChild(link);
          link.click();
          if (info.indexOf('模板') !== -1) {
            Message.success('模板下载成功')
            setTimeout(() => {
              Notification.closeAll()
            }, 1000);
          } else {
            Notification.closeAll()
            Notification.success({
              title: '提示',
              message: '导出成功',
              position: 'bottom-left',
              duration: 2000
            })
            setTimeout(() => {
              Notification.closeAll()
            }, 2000);
            // Message.success('导出成功')
          }
        })
        .catch(() => {
          if (info.indexOf('模板') !== -1) {
            Message.error('模板下载失败')
          } else {
            Message.error('导出失败')
          }
        });
    })
    .catch(() => {
      return false;
    });
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/utils/index.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,118 @@
export function parseTime (time) {
  if (time) {
    var date = new Date(time)
    var year = date.getFullYear()
    /* åœ¨æ—¥æœŸæ ¼å¼ä¸­ï¼Œæœˆä»½æ˜¯ä»Ž0开始的,因此要加0
     * ä½¿ç”¨ä¸‰å…ƒè¡¨è¾¾å¼åœ¨å°äºŽ10的前面加0,以达到格式统一  å¦‚ 09:11:05
     * */
    var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
    var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
    var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
    var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
    var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
    // æ‹¼æŽ¥
    return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
  } else {
    return ''
  }
}
export function removeEmptyValues(obj) {
  const result = {};
  for (const key in obj) {
    if (obj[key] != null && obj[key] !== '') {
      result[key] = obj[key];
    }
  }
  return result;
}
export function resetObjectValues(obj) {
  Object.keys(obj).forEach(key => {
    obj[key] = '';
  });
  return obj;
}
export function generateRandomString(length = 16) {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  let result = '';
  for (let i = 0; i < length; i++) {
    result += chars.charAt(Math.floor(Math.random() * chars.length));
  }
  return result;
}
/**
 * èŠ‚æµå‡½æ•°ä¼˜åŒ–ç‰ˆ
 * @param {Function} func éœ€è¦èŠ‚æµçš„å‡½æ•°
 * @param {number} wait èŠ‚æµæ—¶é—´é—´éš”(毫秒)
 * @param {Object} [options={}] é…ç½®é€‰é¡¹
 * @param {boolean} [options.leading=true] æ˜¯å¦å…è®¸é¦–次立即执行
 * @param {boolean} [options.trailing=true] æ˜¯å¦å…è®¸æœ€åŽä¸€æ¬¡å»¶è¿Ÿæ‰§è¡Œ
 */
export function throttle(func, wait, options = {}) {
  let timeout, context, args;
  let previous = 0;
  const later = () => {
    previous = options.leading === false ? 0 : Date.now();
    timeout = null;
    func.apply(context, args);
    if (!timeout) context = args = null;
  };
  const throttled = function() {
    const now = Date.now();
    if (!previous && options.leading === false) previous = now;
    const remaining = wait - (now - previous);
    context = this;
    args = arguments;
    if (remaining <= 0 || remaining > wait) {
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
      previous = now;
      func.apply(context, args);
      if (!timeout) context = args = null;
    } else if (!timeout && options.trailing !== false) {
      timeout = setTimeout(later, remaining);
    }
  };
  return throttled;
}
/**
* æ ¹æ®æµè§ˆå™¨è¯­è¨€ï¼Œæ˜ å°„到 messages ä¸­å¯¹åº”的键(EN, CN, ES, ...)
* @returns {string} è¯­è¨€é”®ï¼Œå¦‚ 'EN', 'CN'
*/
export function getBrowserLocale() {
  const navLang = navigator.language
  console.log('浏览器语言:', navLang)
  // æå–语言前缀(前两个字母)
  let langPrefix = navLang.substring(0, 2).toLowerCase()
  // ç‰¹æ®Šå¤„理中文:无论 zh-CN / zh-TW / zh-HK éƒ½æ˜ å°„为 CN
  if (langPrefix === 'zh') {
    return 'CN'
  }
  // å…¶ä»–语言映射(支持所有你已配置的语言)
  const map = {
    en: 'EN',
    es: 'ES',
    fr: 'FR',
    de: 'DE',
    ru: 'RU',
    ar: 'AR',
    pt: 'PT',
    ko: 'KO'
  }
  // å¦‚果前缀在映射表中,返回对应的大写键,否则返回默认语言(这里默认英文)
  return map[langPrefix] || 'EN'
}
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/utils/request.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,91 @@
// src/api/request.js
import axios from 'axios'
import { Message } from 'element-ui'
import router from '../router'
import i18n from '../language/index'
// // æœ¬åœ°è°ƒè¯•
// const baseURL = '/api'
// ç”Ÿäº§æ‰“包
const baseURL = (() => {
  const { protocol, hostname } = window.location;
  return `${protocol}//${hostname}:8080`; // å›ºå®šåŽç«¯ç«¯å£
})();
// åˆ›å»ºaxios实例
const service = axios.create({
  baseURL,
  timeout: 100000
})
// è¯·æ±‚拦截器
service.interceptors.request.use(
  config => {
    // å¯ä»¥åœ¨è¿™é‡Œæ·»åŠ token等
    let token = sessionStorage.getItem("token")
    if (token) {
      config.headers['Authorization'] = token
    }
    return config
  },
  error => {
    return Promise.reject(error)
  }
)
// å“åº”拦截器
service.interceptors.response.use(
  response => {
    const res = response.data
    if (res.code == 401) {
      Message.error(i18n.t('error.unauthorized'))
      // è§¦å‘登出操作
      sessionStorage.removeItem('token')
      router.push('/login')
    } else {
      return res
    }
  },
  error => {
    console.error('请求错误:', error)
    let errorMessage = i18n.t('error.networkError')
    if (error.code === 'ECONNABORTED' || error.message.includes('timeout')) {
      errorMessage = i18n.t('error.timeout')
      // è§¦å‘登出操作
      sessionStorage.removeItem('token')
      router.push('/login')
    } else if (error.response) {
      const status = error.response.status
      switch (status) {
        case 500:
          errorMessage = i18n.t('error.serverError')
          // è§¦å‘登出操作
          sessionStorage.removeItem('token')
          router.push('/login')
          break
        case 404:
          errorMessage = i18n.t('error.notFound')
          break
        case 401:
          errorMessage = i18n.t('error.unauthorized')
          // è§¦å‘登出操作
          sessionStorage.removeItem('token')
          router.push('/login')
          break
        default:
          errorMessage = i18n.t('error.unknownError') + `: ${status}`
      }
      if (error.response.data && error.response.data.message) {
        errorMessage = error.response.data.message
      }
    } else if (error.request) {
      errorMessage = i18n.t('error.noResponse')
    }
    Message.error(errorMessage)
    return Promise.reject(error)
  }
)
export default service
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/utils/timezones.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,790 @@
const timezones = {
  "Pacific/Midway": {
      "utc_offset": "-11:00",
      "name": {
          "zh": "萨摩亚标准时间(中途岛)",
          "en": "Samoa Standard Time (Midway)",
          "ja": "サモア標準時(ミッドウェイ)",
          "ko": "사모아 í‘œì¤€ì‹œ (미드웨이)",
          "es": "Hora Estándar de Samoa (Midway)",
          "fr": "Heure Standard de Samoa (Midway)",
          "de": "Samoa-Standardzeit (Midway)",
          "ru": "Самоанское ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ (Мидуэй)",
          "ar": "التوقيت Ø§Ù„قياسي Ù„ساموا (ميدواي)",
          "pt": "Hora Padrão de Samoa (Midway)"
      }
  },
  "America/Adak": {
      "utc_offset": "-10:00",
      "name": {
          "zh": "夏威夷-阿留申标准时间(阿达克)",
          "en": "Hawaii-Aleutian Standard Time (Adak)",
          "ja": "ハワイ・アリューシャン標準時(アダック)",
          "ko": "하와이-알류샨 í‘œì¤€ì‹œ (아닥)",
          "es": "Hora Estándar de Hawái-Aleutianas (Adak)",
          "fr": "Heure Standard d'Hawaï-Aléoutiennes (Adak)",
          "de": "Hawaii-Aleuten-Standardzeit (Adak)",
          "ru": "Гавайско-Алеутское ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ (Адак)",
          "ar": "التوقيت Ø§Ù„قياسي Ù„هاواي-ألوشيان (أداك)",
          "pt": "Hora Padrão do Havaí-Aleutas (Adak)"
      }
  },
  "Pacific/Honolulu": {
      "utc_offset": "-10:00",
      "name": {
          "zh": "夏威夷标准时间",
          "en": "Hawaii Standard Time",
          "ja": "ハワイ標準時",
          "ko": "하와이 í‘œì¤€ì‹œ",
          "es": "Hora Estándar de Hawái",
          "fr": "Heure Standard d'Hawaï",
          "de": "Hawaii-Standardzeit",
          "ru": "Гавайское ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ",
          "ar": "التوقيت Ø§Ù„قياسي Ù„هاواي",
          "pt": "Hora Padrão do Havaí"
      }
  },
  "America/Anchorage": {
      "utc_offset": "-09:00",
      "name": {
          "zh": "阿拉斯加标准时间",
          "en": "Alaska Standard Time",
          "ja": "アラスカ標準時",
          "ko": "알래스카 í‘œì¤€ì‹œ",
          "es": "Hora Estándar de Alaska",
          "fr": "Heure Standard de l'Alaska",
          "de": "Alaska-Standardzeit",
          "ru": "Аляскинское ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ",
          "ar": "التوقيت Ø§Ù„قياسي Ù„ألاسكا",
          "pt": "Hora Padrão do Alasca"
      }
  },
  "America/Los_Angeles": {
      "utc_offset": "-08:00",
      "name": {
          "zh": "太平洋标准时间(洛杉矶)",
          "en": "Pacific Standard Time (Los Angeles)",
          "ja": "太平洋標準時(ロサンゼルス)",
          "ko": "태평양 í‘œì¤€ì‹œ (로스앤젤레스)",
          "es": "Hora Estándar del Pacífico (Los Ãngeles)",
          "fr": "Heure Standard du Pacifique (Los Angeles)",
          "de": "Pazifische Standardzeit (Los Angeles)",
          "ru": "Тихоокеанское ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ (Лос-Анджелес)",
          "ar": "توقيت Ø§Ù„محيط Ø§Ù„هادئ (لوس Ø£Ù†Ø¬Ù„وس)",
          "pt": "Hora Padrão do Pacífico (Los Angeles)"
      }
  },
  "America/Denver": {
      "utc_offset": "-07:00",
      "name": {
          "zh": "山地标准时间(丹佛)",
          "en": "Mountain Standard Time (Denver)",
          "ja": "山岳部標準時(デンバー)",
          "ko": "산악 í‘œì¤€ì‹œ (덴버)",
          "es": "Hora Estándar de la Montaña (Denver)",
          "fr": "Heure Standard des Rocheuses (Denver)",
          "de": "Rocky-Mountain-Standardzeit (Denver)",
          "ru": "Горное ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ (Денвер)",
          "ar": "التوقيت Ø§Ù„جبلي (دنفر)",
          "pt": "Hora Padrão das Montanhas (Denver)"
      }
  },
  "America/Chicago": {
      "utc_offset": "-06:00",
      "name": {
          "zh": "中部标准时间(芝加哥)",
          "en": "Central Standard Time (Chicago)",
          "ja": "中部標準時(シカゴ)",
          "ko": "중부 í‘œì¤€ì‹œ (시카고)",
          "es": "Hora Estándar Central (Chicago)",
          "fr": "Heure Standard du Centre (Chicago)",
          "de": "Central-Standardzeit (Chicago)",
          "ru": "Центральное ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ (Чикаго)",
          "ar": "التوقيت Ø§Ù„مركزي (شيكاغو)",
          "pt": "Hora Padrão Central (Chicago)"
      }
  },
  "America/New_York": {
      "utc_offset": "-05:00",
      "name": {
          "zh": "东部标准时间(纽约)",
          "en": "Eastern Standard Time (New York)",
          "ja": "東部標準時(ニューヨーク)",
          "ko": "동부 í‘œì¤€ì‹œ (뉴욕)",
          "es": "Hora Estándar del Este (Nueva York)",
          "fr": "Heure Standard de l'Est (New York)",
          "de": "Östliche Standardzeit (New York)",
          "ru": "Восточное ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ (Нью-Йорк)",
          "ar": "التوقيت Ø§Ù„شرقي (نيويورك)",
          "pt": "Hora Padrão do Leste (Nova York)"
      }
  },
  "America/Toronto": {
      "utc_offset": "-05:00",
      "name": {
          "zh": "东部标准时间(多伦多)",
          "en": "Eastern Standard Time (Toronto)",
          "ja": "東部標準時(トロント)",
          "ko": "동부 í‘œì¤€ì‹œ (토론토)",
          "es": "Hora Estándar del Este (Toronto)",
          "fr": "Heure Standard de l'Est (Toronto)",
          "de": "Östliche Standardzeit (Toronto)",
          "ru": "Восточное ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ (Торонто)",
          "ar": "التوقيت Ø§Ù„شرقي (تورونتو)",
          "pt": "Hora Padrão do Leste (Toronto)"
      }
  },
  "America/Mexico_City": {
      "utc_offset": "-06:00",
      "name": {
          "zh": "墨西哥中部时间(墨西哥城)",
          "en": "Central Time (Mexico City)",
          "ja": "中部時間(メキシコシティ)",
          "ko": "중부 ì‹œê°„ (멕시코시티)",
          "es": "Hora Central (Ciudad de México)",
          "fr": "Heure du Centre (Mexico)",
          "de": "Zentralzeit (Mexiko-Stadt)",
          "ru": "Центральное Ð²Ñ€ÐµÐ¼Ñ (Мехико)",
          "ar": "التوقيت Ø§Ù„مركزي (مكسيكو Ø³ÙŠØªÙŠ)",
          "pt": "Hora Central (Cidade do México)"
      }
  },
  "America/Bogota": {
      "utc_offset": "-05:00",
      "name": {
          "zh": "哥伦比亚时间(波哥大)",
          "en": "Colombia Time (Bogotá)",
          "ja": "コロンビア時間(ボゴタ)",
          "ko": "콜롬비아 ì‹œê°„ (보고타)",
          "es": "Hora de Colombia (Bogotá)",
          "fr": "Heure de Colombie (Bogotá)",
          "de": "Kolumbianische Zeit (Bogotá)",
          "ru": "Колумбийское Ð²Ñ€ÐµÐ¼Ñ (Богота)",
          "ar": "توقيت ÙƒÙˆÙ„ومبيا (بوغوتا)",
          "pt": "Hora da Colômbia (Bogotá)"
      }
  },
  "America/Lima": {
      "utc_offset": "-05:00",
      "name": {
          "zh": "秘鲁时间(利马)",
          "en": "Peru Time (Lima)",
          "ja": "ペルー時間(リマ)",
          "ko": "페루 ì‹œê°„ (리마)",
          "es": "Hora de Perú (Lima)",
          "fr": "Heure du Pérou (Lima)",
          "de": "Peruanische Zeit (Lima)",
          "ru": "Перуанское Ð²Ñ€ÐµÐ¼Ñ (Лима)",
          "ar": "توقيت Ø¨ÙŠØ±Ùˆ (ليما)",
          "pt": "Hora do Peru (Lima)"
      }
  },
  "America/Santiago": {
      "utc_offset": "-04:00",
      "name": {
          "zh": "智利时间(圣地亚哥)",
          "en": "Chile Time (Santiago)",
          "ja": "チリ時間(サンティアゴ)",
          "ko": "칠레 ì‹œê°„ (산티아고)",
          "es": "Hora de Chile (Santiago)",
          "fr": "Heure du Chili (Santiago)",
          "de": "Chilenische Zeit (Santiago)",
          "ru": "Чилийское Ð²Ñ€ÐµÐ¼Ñ (Сантьяго)",
          "ar": "توقيت ØªØ´ÙŠÙ„ÙŠ (سانتياغو)",
          "pt": "Hora do Chile (Santiago)"
      }
  },
  "America/Argentina/Buenos_Aires": {
      "utc_offset": "-03:00",
      "name": {
          "zh": "阿根廷时间(布宜诺斯艾利斯)",
          "en": "Argentina Time (Buenos Aires)",
          "ja": "アルゼンチン時間(ブエノスアイレス)",
          "ko": "아르헨티나 ì‹œê°„ (부에노스아이레스)",
          "es": "Hora de Argentina (Buenos Aires)",
          "fr": "Heure d'Argentine (Buenos Aires)",
          "de": "Argentinische Zeit (Buenos Aires)",
          "ru": "Аргентинское Ð²Ñ€ÐµÐ¼Ñ (Буэнос-Айрес)",
          "ar": "توقيت Ø§Ù„أرجنتين (بوينس Ø¢ÙŠØ±Ø³)",
          "pt": "Hora da Argentina (Buenos Aires)"
      }
  },
  "America/Sao_Paulo": {
      "utc_offset": "-03:00",
      "name": {
          "zh": "巴西时间(圣保罗)",
          "en": "Brazil Time (São Paulo)",
          "ja": "ブラジル時間(サンパウロ)",
          "ko": "브라질 ì‹œê°„ (상파울루)",
          "es": "Hora de Brasil (São Paulo)",
          "fr": "Heure du Brésil (São Paulo)",
          "de": "Brasilianische Zeit (São Paulo)",
          "ru": "Бразильское Ð²Ñ€ÐµÐ¼Ñ (Сан-Паулу)",
          "ar": "توقيت Ø§Ù„برازيل (ساو Ø¨Ø§ÙˆÙ„Ùˆ)",
          "pt": "Hora do Brasil (São Paulo)"
      }
  },
  "Atlantic/Azores": {
      "utc_offset": "-01:00",
      "name": {
          "zh": "亚速尔群岛时间",
          "en": "Azores Time",
          "ja": "アゾレス時間",
          "ko": "아조레스 ì‹œê°„",
          "es": "Hora de las Azores",
          "fr": "Heure des Açores",
          "de": "Azoren-Zeit",
          "ru": "Азорское Ð²Ñ€ÐµÐ¼Ñ",
          "ar": "توقيت Ø§Ù„أزور",
          "pt": "Hora dos Açores"
      }
  },
  "Europe/London": {
      "utc_offset": "+00:00",
      "name": {
          "zh": "格林尼治标准时间(伦敦)",
          "en": "Greenwich Mean Time (London)",
          "ja": "グリニッジ標準時(ロンドン)",
          "ko": "그리니치 í‘œì¤€ì‹œ (런던)",
          "es": "Hora del Meridiano de Greenwich (Londres)",
          "fr": "Heure de Greenwich (Londres)",
          "de": "Mittlere Greenwich-Zeit (London)",
          "ru": "Среднее Ð²Ñ€ÐµÐ¼Ñ Ð¿Ð¾ Ð“ринвичу (Лондон)",
          "ar": "توقيت ØºØ±ÙŠÙ†ØªØ´ (لندن)",
          "pt": "Hora Média de Greenwich (Londres)"
      }
  },
  "Europe/Lisbon": {
      "utc_offset": "+00:00",
      "name": {
          "zh": "葡萄牙时间(里斯本)",
          "en": "Portugal Time (Lisbon)",
          "ja": "ポルトガル時間(リスボン)",
          "ko": "포르투갈 ì‹œê°„ (리스본)",
          "es": "Hora de Portugal (Lisboa)",
          "fr": "Heure du Portugal (Lisbonne)",
          "de": "Portugiesische Zeit (Lissabon)",
          "ru": "Португальское Ð²Ñ€ÐµÐ¼Ñ (Лиссабон)",
          "ar": "توقيت Ø§Ù„برتغال (لشبونة)",
          "pt": "Hora de Portugal (Lisboa)"
      }
  },
  "Europe/Berlin": {
      "utc_offset": "+01:00",
      "name": {
          "zh": "中欧时间(柏林)",
          "en": "Central European Time (Berlin)",
          "ja": "中央ヨーロッパ時間(ベルリン)",
          "ko": "중앙유럽 ì‹œê°„ (베를린)",
          "es": "Hora Central Europea (Berlín)",
          "fr": "Heure d'Europe Centrale (Berlin)",
          "de": "Mitteleuropäische Zeit (Berlin)",
          "ru": "Центральноевропейское Ð²Ñ€ÐµÐ¼Ñ (Берлин)",
          "ar": "توقيت ÙˆØ³Ø· Ø£ÙˆØ±ÙˆØ¨Ø§ (برلين)",
          "pt": "Hora da Europa Central (Berlim)"
      }
  },
  "Europe/Paris": {
      "utc_offset": "+01:00",
      "name": {
          "zh": "中欧时间(巴黎)",
          "en": "Central European Time (Paris)",
          "ja": "中央ヨーロッパ時間(パリ)",
          "ko": "중앙유럽 ì‹œê°„ (파리)",
          "es": "Hora Central Europea (París)",
          "fr": "Heure d'Europe Centrale (Paris)",
          "de": "Mitteleuropäische Zeit (Paris)",
          "ru": "Центральноевропейское Ð²Ñ€ÐµÐ¼Ñ (Париж)",
          "ar": "توقيت ÙˆØ³Ø· Ø£ÙˆØ±ÙˆØ¨Ø§ (باريس)",
          "pt": "Hora da Europa Central (Paris)"
      }
  },
  "Europe/Madrid": {
      "utc_offset": "+01:00",
      "name": {
          "zh": "中欧时间(马德里)",
          "en": "Central European Time (Madrid)",
          "ja": "中央ヨーロッパ時間(マドリード)",
          "ko": "중앙유럽 ì‹œê°„ (마드리드)",
          "es": "Hora Central Europea (Madrid)",
          "fr": "Heure d'Europe Centrale (Madrid)",
          "de": "Mitteleuropäische Zeit (Madrid)",
          "ru": "Центральноевропейское Ð²Ñ€ÐµÐ¼Ñ (Мадрид)",
          "ar": "توقيت ÙˆØ³Ø· Ø£ÙˆØ±ÙˆØ¨Ø§ (مدريد)",
          "pt": "Hora da Europa Central (Madri)"
      }
  },
  "Europe/Rome": {
      "utc_offset": "+01:00",
      "name": {
          "zh": "中欧时间(罗马)",
          "en": "Central European Time (Rome)",
          "ja": "中央ヨーロッパ時間(ローマ)",
          "ko": "중앙유럽 ì‹œê°„ (로마)",
          "es": "Hora Central Europea (Roma)",
          "fr": "Heure d'Europe Centrale (Rome)",
          "de": "Mitteleuropäische Zeit (Rom)",
          "ru": "Центральноевропейское Ð²Ñ€ÐµÐ¼Ñ (Рим)",
          "ar": "توقيت ÙˆØ³Ø· Ø£ÙˆØ±ÙˆØ¨Ø§ (روما)",
          "pt": "Hora da Europa Central (Roma)"
      }
  },
  "Europe/Amsterdam": {
      "utc_offset": "+01:00",
      "name": {
          "zh": "中欧时间(阿姆斯特丹)",
          "en": "Central European Time (Amsterdam)",
          "ja": "中央ヨーロッパ時間(アムステルダム)",
          "ko": "중앙유럽 ì‹œê°„ (암스테르담)",
          "es": "Hora Central Europea (Ámsterdam)",
          "fr": "Heure d'Europe Centrale (Amsterdam)",
          "de": "Mitteleuropäische Zeit (Amsterdam)",
          "ru": "Центральноевропейское Ð²Ñ€ÐµÐ¼Ñ (Амстердам)",
          "ar": "توقيت ÙˆØ³Ø· Ø£ÙˆØ±ÙˆØ¨Ø§ (أمستردام)",
          "pt": "Hora da Europa Central (Amsterdã)"
      }
  },
  "Europe/Stockholm": {
      "utc_offset": "+01:00",
      "name": {
          "zh": "中欧时间(斯德哥尔摩)",
          "en": "Central European Time (Stockholm)",
          "ja": "中央ヨーロッパ時間(ストックホルム)",
          "ko": "중앙유럽 ì‹œê°„ (스톡홀름)",
          "es": "Hora Central Europea (Estocolmo)",
          "fr": "Heure d'Europe Centrale (Stockholm)",
          "de": "Mitteleuropäische Zeit (Stockholm)",
          "ru": "Центральноевропейское Ð²Ñ€ÐµÐ¼Ñ (Стокгольм)",
          "ar": "توقيت ÙˆØ³Ø· Ø£ÙˆØ±ÙˆØ¨Ø§ (ستوكهولم)",
          "pt": "Hora da Europa Central (Estocolmo)"
      }
  },
  "Europe/Athens": {
      "utc_offset": "+02:00",
      "name": {
          "zh": "东欧时间(雅典)",
          "en": "Eastern European Time (Athens)",
          "ja": "東ヨーロッパ時間(アテネ)",
          "ko": "동유럽 ì‹œê°„ (아테네)",
          "es": "Hora de Europa Oriental (Atenas)",
          "fr": "Heure d'Europe de l'Est (Athènes)",
          "de": "Osteuropäische Zeit (Athen)",
          "ru": "Восточноевропейское Ð²Ñ€ÐµÐ¼Ñ (Афины)",
          "ar": "توقيت Ø´Ø±Ù‚ Ø£ÙˆØ±ÙˆØ¨Ø§ (أثينا)",
          "pt": "Hora da Europa Oriental (Atenas)"
      }
  },
  "Europe/Istanbul": {
      "utc_offset": "+03:00",
      "name": {
          "zh": "土耳其时间(伊斯坦布尔)",
          "en": "Turkey Time (Istanbul)",
          "ja": "トルコ時間(イスタンブール)",
          "ko": "터키 ì‹œê°„ (이스탄불)",
          "es": "Hora de Turquía (Estambul)",
          "fr": "Heure de Turquie (Istanbul)",
          "de": "Türkische Zeit (Istanbul)",
          "ru": "Турецкое Ð²Ñ€ÐµÐ¼Ñ (Стамбул)",
          "ar": "توقيت ØªØ±ÙƒÙŠØ§ (إسطنبول)",
          "pt": "Hora da Turquia (Istambul)"
      }
  },
  "Asia/Dubai": {
      "utc_offset": "+04:00",
      "name": {
          "zh": "海湾标准时间(迪拜)",
          "en": "Gulf Standard Time (Dubai)",
          "ja": "湾岸標準時(ドバイ)",
          "ko": "걸프 í‘œì¤€ì‹œ (두바이)",
          "es": "Hora Estándar del Golfo (Dubái)",
          "fr": "Heure Standard du Golfe (Dubaï)",
          "de": "Golf-Standardzeit (Dubai)",
          "ru": "Стандартное Ð²Ñ€ÐµÐ¼Ñ ÐŸÐµÑ€ÑÐ¸Ð´ÑÐºÐ¾Ð³Ð¾ Ð·Ð°Ð»Ð¸Ð²Ð° (Дубай)",
          "ar": "التوقيت Ø§Ù„قياسي Ø§Ù„خليجي (دبي)",
          "pt": "Hora Padrão do Golfo (Dubai)"
      }
  },
  "Asia/Karachi": {
      "utc_offset": "+05:00",
      "name": {
          "zh": "巴基斯坦标准时间(卡拉奇)",
          "en": "Pakistan Standard Time (Karachi)",
          "ja": "パキスタン標準時(カラチ)",
          "ko": "파키스탄 í‘œì¤€ì‹œ (카라치)",
          "es": "Hora Estándar de Pakistán (Karachi)",
          "fr": "Heure Standard du Pakistan (Karachi)",
          "de": "Pakistanische Standardzeit (Karatschi)",
          "ru": "Пакистанское ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ (Карачи)",
          "ar": "التوقيت Ø§Ù„قياسي Ø§Ù„باكستاني (كراتشي)",
          "pt": "Hora Padrão do Paquistão (Carachi)"
      }
  },
  "Asia/Kolkata": {
      "utc_offset": "+05:30",
      "name": {
          "zh": "印度标准时间(加尔各答)",
          "en": "India Standard Time (Kolkata)",
          "ja": "インド標準時(コルカタ)",
          "ko": "인도 í‘œì¤€ì‹œ (콜카타)",
          "es": "Hora Estándar de India (Calcuta)",
          "fr": "Heure Standard de l'Inde (Calcutta)",
          "de": "Indische Standardzeit (Kalkutta)",
          "ru": "Индийское ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ (Калькутта)",
          "ar": "التوقيت Ø§Ù„قياسي Ø§Ù„هندي (كولكاتا)",
          "pt": "Hora Padrão da Ãndia (Calcutá)"
      }
  },
  "Asia/Dhaka": {
      "utc_offset": "+06:00",
      "name": {
          "zh": "孟加拉标准时间(达卡)",
          "en": "Bangladesh Standard Time (Dhaka)",
          "ja": "バングラデシュ標準時(ダッカ)",
          "ko": "방글라데시 í‘œì¤€ì‹œ (다카)",
          "es": "Hora Estándar de Bangladés (Daca)",
          "fr": "Heure Standard du Bangladesh (Dacca)",
          "de": "Bangladeschische Standardzeit (Dhaka)",
          "ru": "Бангладешское ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ (Дакка)",
          "ar": "التوقيت Ø§Ù„قياسي Ù„بنغلاديش (دكا)",
          "pt": "Hora Padrão de Bangladesh (Daca)"
      }
  },
  "Asia/Bangkok": {
      "utc_offset": "+07:00",
      "name": {
          "zh": "中南半岛时间(曼谷)",
          "en": "Indochina Time (Bangkok)",
          "ja": "インドシナ時間(バンコク)",
          "ko": "인도차이나 ì‹œê°„ (방콕)",
          "es": "Hora de Indochina (Bangkok)",
          "fr": "Heure d'Indochine (Bangkok)",
          "de": "Indochina-Zeit (Bangkok)",
          "ru": "Индокитайское Ð²Ñ€ÐµÐ¼Ñ (Бангкок)",
          "ar": "توقيت Ø§Ù„هند Ø§Ù„صينية (بانكوك)",
          "pt": "Hora da Indochina (Bangcoc)"
      }
  },
  "Asia/Jakarta": {
      "utc_offset": "+07:00",
      "name": {
          "zh": "印度尼西亚西部时间(雅加达)",
          "en": "Western Indonesia Time (Jakarta)",
          "ja": "西インドネシア時間(ジャカルタ)",
          "ko": "서인도네시아 ì‹œê°„ (자카르타)",
          "es": "Hora de Indonesia Occidental (Yakarta)",
          "fr": "Heure de l'Indonésie Occidentale (Jakarta)",
          "de": "Westindonesische Zeit (Jakarta)",
          "ru": "Западноиндонезийское Ð²Ñ€ÐµÐ¼Ñ (Джакарта)",
          "ar": "توقيت ØºØ±Ø¨ Ø¥Ù†Ø¯ÙˆÙ†ÙŠØ³ÙŠØ§ (جاكرتا)",
          "pt": "Hora da Indonésia Ocidental (Jacarta)"
      }
  },
  "Asia/Shanghai": {
      "utc_offset": "+08:00",
      "name": {
          "zh": "中国标准时间(北京)",
          "en": "China Standard Time (Beijing)",
          "ja": "中国標準時(北京)",
          "ko": "중국 í‘œì¤€ì‹œ (베이징)",
          "es": "Hora Estándar de China (Pekín)",
          "fr": "Heure Standard de Chine (Pékin)",
          "de": "Chinesische Standardzeit (Peking)",
          "ru": "Китайское ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ (Пекин)",
          "ar": "التوقيت Ø§Ù„قياسي Ø§Ù„صيني (بكين)",
          "pt": "Hora Padrão da China (Pequim)"
      }
  },
  "Asia/Taipei": {
      "utc_offset": "+08:00",
      "name": {
          "zh": "台湾标准时间(台北)",
          "en": "Taiwan Standard Time (Taipei)",
          "ja": "台湾標準時(台北)",
          "ko": "대만 í‘œì¤€ì‹œ (타이베이)",
          "es": "Hora Estándar de Taiwán (Taipéi)",
          "fr": "Heure Standard de Taïwan (Taipei)",
          "de": "Taiwanesische Standardzeit (Taipeh)",
          "ru": "Тайваньское ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ (Тайбэй)",
          "ar": "التوقيت Ø§Ù„قياسي Ù„تايوان (تايبيه)",
          "pt": "Hora Padrão de Taiwan (Taipei)"
      }
  },
  "Asia/Hong_Kong": {
      "utc_offset": "+08:00",
      "name": {
          "zh": "香港时间",
          "en": "Hong Kong Time",
          "ja": "香港時間",
          "ko": "홍콩 ì‹œê°„",
          "es": "Hora de Hong Kong",
          "fr": "Heure de Hong Kong",
          "de": "Hongkong-Zeit",
          "ru": "Гонконгское Ð²Ñ€ÐµÐ¼Ñ",
          "ar": "توقيت Ù‡ÙˆÙ†Øº ÙƒÙˆÙ†Øº",
          "pt": "Hora de Hong Kong"
      }
  },
  "Asia/Singapore": {
      "utc_offset": "+08:00",
      "name": {
          "zh": "新加坡标准时间",
          "en": "Singapore Standard Time",
          "ja": "シンガポール標準時",
          "ko": "싱가포르 í‘œì¤€ì‹œ",
          "es": "Hora Estándar de Singapur",
          "fr": "Heure Standard de Singapour",
          "de": "Singapur-Standardzeit",
          "ru": "Сингапурское ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ",
          "ar": "التوقيت Ø§Ù„قياسي Ù„سنغافورة",
          "pt": "Hora Padrão de Cingapura"
      }
  },
  "Asia/Seoul": {
      "utc_offset": "+09:00",
      "name": {
          "zh": "韩国标准时间(首尔)",
          "en": "Korea Standard Time (Seoul)",
          "ja": "韓国標準時(ソウル)",
          "ko": "한국 í‘œì¤€ì‹œ (서울)",
          "es": "Hora Estándar de Corea (Seúl)",
          "fr": "Heure Standard de Corée (Séoul)",
          "de": "Koreanische Standardzeit (Seoul)",
          "ru": "Корейское ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ (Сеул)",
          "ar": "التوقيت Ø§Ù„قياسي Ø§Ù„كوري (سيول)",
          "pt": "Hora Padrão da Coreia (Seul)"
      }
  },
  "Asia/Tokyo": {
      "utc_offset": "+09:00",
      "name": {
          "zh": "日本标准时间(东京)",
          "en": "Japan Standard Time (Tokyo)",
          "ja": "日本標準時(東京)",
          "ko": "일본 í‘œì¤€ì‹œ (도쿄)",
          "es": "Hora Estándar de Japón (Tokio)",
          "fr": "Heure Standard du Japon (Tokyo)",
          "de": "Japanische Standardzeit (Tokio)",
          "ru": "Японское ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ (Токио)",
          "ar": "التوقيت Ø§Ù„قياسي Ø§Ù„ياباني (طوكيو)",
          "pt": "Hora Padrão do Japão (Tóquio)"
      }
  },
  "Australia/Perth": {
      "utc_offset": "+08:00",
      "name": {
          "zh": "澳大利亚西部时间(珀斯)",
          "en": "Australian Western Standard Time (Perth)",
          "ja": "オーストラリア西部標準時(パース)",
          "ko": "호주 ì„œë¶€ í‘œì¤€ì‹œ (퍼스)",
          "es": "Hora Estándar Occidental de Australia (Perth)",
          "fr": "Heure Standard de l'Ouest Australien (Perth)",
          "de": "Westaustralische Standardzeit (Perth)",
          "ru": "Западноавстралийское ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ (Перт)",
          "ar": "التوقيت Ø§Ù„قياسي Ù„غرب Ø£Ø³ØªØ±Ø§Ù„يا (بيرث)",
          "pt": "Hora Padrão da Austrália Ocidental (Perth)"
      }
  },
  "Australia/Sydney": {
      "utc_offset": "+10:00",
      "name": {
          "zh": "澳大利亚东部时间(悉尼)",
          "en": "Australian Eastern Standard Time (Sydney)",
          "ja": "オーストラリア東部標準時(シドニー)",
          "ko": "호주 ë™ë¶€ í‘œì¤€ì‹œ (시드니)",
          "es": "Hora Estándar Oriental de Australia (Sídney)",
          "fr": "Heure Standard de l'Est Australien (Sydney)",
          "de": "Ostaustralische Standardzeit (Sydney)",
          "ru": "Восточноавстралийское ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ (Сидней)",
          "ar": "التوقيت Ø§Ù„قياسي Ù„شرق Ø£Ø³ØªØ±Ø§Ù„يا (سيدني)",
          "pt": "Hora Padrão da Austrália Oriental (Sydney)"
      }
  },
  "Pacific/Guam": {
      "utc_offset": "+10:00",
      "name": {
          "zh": "关岛时间",
          "en": "Guam Time",
          "ja": "グアム時間",
          "ko": "괌 ì‹œê°„",
          "es": "Hora de Guam",
          "fr": "Heure de Guam",
          "de": "Guam-Zeit",
          "ru": "Время Ð“уама",
          "ar": "توقيت ØºÙˆØ§Ù…",
          "pt": "Hora de Guam"
      }
  },
  "Pacific/Noumea": {
      "utc_offset": "+11:00",
      "name": {
          "zh": "新喀里多尼亚时间(努美阿)",
          "en": "New Caledonia Time (Noumea)",
          "ja": "ニューカレドニア時間(ヌメア)",
          "ko": "뉴칼레도니아 ì‹œê°„ (누메아)",
          "es": "Hora de Nueva Caledonia (Numea)",
          "fr": "Heure de Nouvelle-Calédonie (Nouméa)",
          "de": "Neukaledonische Zeit (Noumea)",
          "ru": "Новокаледонское Ð²Ñ€ÐµÐ¼Ñ (Нумеа)",
          "ar": "توقيت ÙƒØ§Ù„يدونيا Ø§Ù„جديدة (نوميا)",
          "pt": "Hora da Nova Caledônia (Numeá)"
      }
  },
  "Pacific/Auckland": {
      "utc_offset": "+12:00",
      "name": {
          "zh": "新西兰标准时间(奥克兰)",
          "en": "New Zealand Standard Time (Auckland)",
          "ja": "ニュージーランド標準時(オークランド)",
          "ko": "뉴질랜드 í‘œì¤€ì‹œ (오클랜드)",
          "es": "Hora Estándar de Nueva Zelanda (Auckland)",
          "fr": "Heure Standard de Nouvelle-Zélande (Auckland)",
          "de": "Neuseeländische Standardzeit (Auckland)",
          "ru": "Новозеландское ÑÑ‚андартное Ð²Ñ€ÐµÐ¼Ñ (Окленд)",
          "ar": "التوقيت Ø§Ù„قياسي Ù„نيوزيلندا (أوكلاند)",
          "pt": "Hora Padrão da Nova Zelândia (Auckland)"
      }
  },
  "Pacific/Fiji": {
      "utc_offset": "+12:00",
      "name": {
          "zh": "斐济时间",
          "en": "Fiji Time",
          "ja": "フィジー時間",
          "ko": "피지 ì‹œê°„",
          "es": "Hora de Fiyi",
          "fr": "Heure de Fidji",
          "de": "Fidschi-Zeit",
          "ru": "Время Ð¤Ð¸Ð´Ð¶Ð¸",
          "ar": "توقيت ÙÙŠØ¬ÙŠ",
          "pt": "Hora de Fiji"
      }
  },
  "Pacific/Tongatapu": {
      "utc_offset": "+13:00",
      "name": {
          "zh": "汤加时间(汤加塔普)",
          "en": "Tonga Time (Tongatapu)",
          "ja": "トンガ時間(トンガタプ)",
          "ko": "통가 ì‹œê°„ (통가타푸)",
          "es": "Hora de Tonga (Tongatapu)",
          "fr": "Heure de Tonga (Tongatapu)",
          "de": "Tonga-Zeit (Tongatapu)",
          "ru": "Время Ð¢Ð¾Ð½Ð³Ð° (Тонгатапу)",
          "ar": "توقيت ØªÙˆÙ†ØºØ§ (تونغاتابو)",
          "pt": "Hora de Tonga (Tongatapu)"
      }
  }
}
const timezoneRegionNames = {
zh: {
  Africa: '非洲',
  America: '美洲',
  Antarctica: '南极洲',
  Arctic: '北极',
  Asia: '亚洲',
  Atlantic: '大西洋',
  Australia: '澳大利亚',
  Europe: '欧洲',
  Indian: '印度洋',
  Pacific: '太平洋'
},
en: {
  Africa: 'Africa',
  America: 'America',
  Antarctica: 'Antarctica',
  Arctic: 'Arctic',
  Asia: 'Asia',
  Atlantic: 'Atlantic',
  Australia: 'Australia',
  Europe: 'Europe',
  Indian: 'Indian',
  Pacific: 'Pacific'
},
es: {
  Africa: 'África',
  America: 'América',
  Antarctica: 'Antártida',
  Arctic: 'Ártico',
  Asia: 'Asia',
  Atlantic: 'Atlántico',
  Australia: 'Australia',
  Europe: 'Europa',
  Indian: 'Índico',
  Pacific: 'Pacífico'
},
fr: {
  Africa: 'Afrique',
  America: 'Amérique',
  Antarctica: 'Antarctique',
  Arctic: 'Arctique',
  Asia: 'Asie',
  Atlantic: 'Atlantique',
  Australia: 'Australie',
  Europe: 'Europe',
  Indian: 'Indien',
  Pacific: 'Pacifique'
},
de: {
  Africa: 'Afrika',
  America: 'Amerika',
  Antarctica: 'Antarktis',
  Arctic: 'Arktis',
  Asia: 'Asien',
  Atlantic: 'Atlantik',
  Australia: 'Australien',
  Europe: 'Europa',
  Indian: 'Indischer Ozean',
  Pacific: 'Pazifik'
},
ru: {
  Africa: 'Африка',
  America: 'Америка',
  Antarctica: 'Антарктида',
  Arctic: 'Арктика',
  Asia: 'Азия',
  Atlantic: 'Атлантика',
  Australia: 'Австралия',
  Europe: 'Европа',
  Indian: 'Индийский Ð¾ÐºÐµÐ°Ð½',
  Pacific: 'Тихий Ð¾ÐºÐµÐ°Ð½'
},
ar: {
  Africa: 'أفريقيا',
  America: 'الأمريكتان',
  Antarctica: 'أنتاركتيكا',
  Arctic: 'القطب Ø§Ù„شمالي',
  Asia: 'آسيا',
  Atlantic: 'الأطلسي',
  Australia: 'أستراليا',
  Europe: 'أوروبا',
  Indian: 'المحيط Ø§Ù„هندي',
  Pacific: 'المحيط Ø§Ù„هادئ'
},
pt: {
  Africa: 'África',
  America: 'América',
  Antarctica: 'Antártida',
  Arctic: 'Ártico',
  Asia: 'Ásia',
  Atlantic: 'Atlântico',
  Australia: 'Austrália',
  Europe: 'Europa',
  Indian: 'Índico',
  Pacific: 'Pacífico'
},
ko: {
  Africa: '아프리카',
  America: '아메리카',
  Antarctica: '남극',
  Arctic: '북극',
  Asia: '아시아',
  Atlantic: '대서양',
  Australia: '오스트레일리아',
  Europe: '유럽',
  Indian: '인도양',
  Pacific: '태평양'
}
}
export { timezones, timezoneRegionNames }
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/config/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,2241 @@
<template>
  <div id="configMain">
    <div class="config-container">
      <div class="config-tabs">
        <el-tabs v-model="activeTab" type="card" @tab-click="handleClick">
          <!-- åŸºç¡€é…ç½® -->
          <el-tab-pane :label="$t('config.basicConfiguration')" name="base">
            <div class="config-grid">
              <!-- æ˜¾ç¤ºè®¾ç½®å¡ç‰‡ -->
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-monitor config-card__header-icon"></i>
                  {{ $t('config.displaySettings') }}
                </div>
                <div class="config-card__body">
                  <el-form ref="displayForm" :model="base" :rules="baseRules" label-width="140px">
                    <!-- è‡ªåŠ¨è°ƒèŠ‚äº®åº¦ -->
                    <!-- <div class="config-form-item">
                      <el-form-item :label="$t('config.autoAdjustScreenBrightness')">
                        <el-switch v-model="base.brightnessAuto" :active-value="1" :inactive-value="0"
                          active-color="#1890ff" inactive-color="#ff4949">
                        </el-switch>
                      </el-form-item>
                    </div> -->
                    <!-- å±å¹•亮度 -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.screenBrightness')">
                        <div class="compact-controls">
                          <el-slider v-model="base.backlight" :max="100" :min="1" style="width:60%;"></el-slider>
                          <span class="value-display">{{ base.backlight }}%</span>
                        </div>
                      </el-form-item>
                    </div>
                    <!-- è‡ªåŠ¨æ¯å± -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.autoTurnOffScreenTime')" prop="screenOff">
                        <el-select v-model="base.screenOff" placeholder="请选择">
                          <!-- <el-option v-for="(item, index) in idleData" :key="index" :label="item" :value="index">
                          </el-option> -->
                          <el-option :label="$t('config.never')" :value="0"></el-option>
                          <el-option :label="$t('config.min1')" :value="1"></el-option>
                          <el-option :label="$t('config.min2')" :value="2"></el-option>
                          <el-option :label="$t('config.min3')" :value="3"></el-option>
                          <el-option :label="$t('config.min4')" :value="4"></el-option>
                          <el-option :label="$t('config.min5')" :value="5"></el-option>
                        </el-select>
                        <!-- <el-input v-model="base.screenOff" type="number" :min="0"
                          :placeholder="$t('config.msg_please_enter')"></el-input><span>
                          {{ $t('config.min') }}</span> -->
                      </el-form-item>
                    </div>
                    <!-- è‡ªåŠ¨å±ä¿ -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.autoScreenSaverTime')" prop="screensaver">
                        <el-select v-model="base.screensaver" placeholder="请选择">
                          <!-- <el-option v-for="(item, index) in idleData" :key="index" :label="item" :value="index">
                          </el-option> -->
                          <el-option :label="$t('config.never')" :value="0"></el-option>
                          <el-option :label="$t('config.min1')" :value="1"></el-option>
                          <el-option :label="$t('config.min2')" :value="2"></el-option>
                          <el-option :label="$t('config.min3')" :value="3"></el-option>
                          <el-option :label="$t('config.min4')" :value="4"></el-option>
                          <el-option :label="$t('config.min5')" :value="5"></el-option>
                        </el-select>
                      </el-form-item>
                    </div>
                    <!-- ç™½è‰²è¡¥å…‰ç¯ -->
                    <div class="config-form-item"
                      v-if="sys.model == 'vf105' || sys.model == 'vf107' || sys.model == 'vf114' || sys.model == 'vf124' || sys.model == 'vf202'">
                      <el-form-item :label="$t('config.brightness')">
                        <div class="compact-controls">
                          <el-slider v-model="base.brightness" :max="100" :min="0" style="width:60%;"></el-slider>
                          <span class="value-display">{{ base.brightness }}%</span>
                        </div>
                      </el-form-item>
                    </div>
                    <!-- çº¢å¤–补光灯 -->
                    <!-- <div class="config-form-item">
                      <el-form-item :label="$t('config.nirBrightness')">
                        <div class="compact-controls">
                          <el-slider v-model="base.nirBrightness" :max="100" :min="0" style="width:60%;"></el-slider>
                          <span class="value-display">{{ base.nirBrightness }}%</span>
                        </div>
                      </el-form-item>
                    </div> -->
                  </el-form>
                </div>
              </el-card>
              <!-- ä¿¡æ¯æ˜¾ç¤ºå¡ç‰‡ -->
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-info config-card__header-icon"></i>
                  {{ $t('config.informationDisplay') }}
                </div>
                <div class="config-card__body">
                  <el-form ref="infoForm" :model="base" label-width="140px">
                    <!-- æ˜¾ç¤ºSN -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.displayDeviceSn')">
                        <el-switch v-model="base.showSn" :active-value="1" :inactive-value="0" active-color="#1890ff"
                          inactive-color="#ff4949">
                        </el-switch>
                      </el-form-item>
                    </div>
                    <!-- æ˜¾ç¤ºIP -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.displayIp')">
                        <el-switch v-model="base.showIp" :active-value="1" :inactive-value="0" active-color="#1890ff"
                          inactive-color="#ff4949">
                        </el-switch>
                      </el-form-item>
                    </div>
                    <!-- æ˜¾ç¤ºå°ç¨‹åºç  -->
                    <!-- <div class="config-form-item">
                      <el-form-item :label="$t('config.displayCode')">
                        <el-switch v-model="base.showProgramCode" :active-value="1" :inactive-value="0"
                          active-color="#1890ff" inactive-color="#ff4949">
                        </el-switch>
                      </el-form-item>
                    </div> -->
                    <!-- äº‘证功能菜单 -->
                    <!-- <div class="config-form-item">
                      <el-form-item :label="$t('config.displayIdentityCard')">
                        <el-switch v-model="base.showIdentityCard" :active-value="1" :inactive-value="0"
                          active-color="#1890ff" inactive-color="#ff4949">
                        </el-switch>
                      </el-form-item>
                    </div> -->
                  </el-form>
                </div>
              </el-card>
              <!-- éŸ³é¢‘设置卡片 -->
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-mic config-card__header-icon"></i>
                  {{ $t('config.audioSettings') }}
                </div>
                <div class="config-card__body">
                  <el-form ref="audioForm" :model="base" label-width="140px">
                    <!-- éŸ³é‡ -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.volume')" prop="volume">
                        <div class="compact-controls">
                          <el-slider v-model="base.volume" :max="10" :min="0" style="flex: 1"></el-slider>
                          <span class="value-display">{{ base.volume }}</span>
                        </div>
                      </el-form-item>
                    </div>
                  </el-form>
                </div>
              </el-card>
              <!-- è¯­è¨€ä¸Žä¸»é¢˜å¡ç‰‡ -->
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-s-operation config-card__header-icon"></i>
                  {{ $t('config.languageAndThemes') }}
                </div>
                <div class="config-card__body">
                  <el-form ref="systemForm" :model="base" label-width="140px">
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.language')" prop="language">
                        <el-radio-group v-model="base.language" class="language-radio-group"
                          :style="{ 'margin-bottom': version == 0 ? '0px' : '12px' }">
                          <el-radio v-if="version == 0" label="CN">{{ $t('config.cn') }}</el-radio>
                          <div v-else>
                            <el-radio label="EN">{{ $t('config.en') }}</el-radio>
                            <el-radio label="ES">{{ $t('config.es') }}</el-radio>
                            <el-radio label="FR">{{ $t('config.fr') }}</el-radio>
                            <el-radio label="DE">{{ $t('config.de') }}</el-radio>
                            <el-radio label="RU">{{ $t('config.ru') }}</el-radio>
                            <el-radio label="AR">{{ $t('config.ar') }}</el-radio>
                            <el-radio label="PT">{{ $t('config.pt') }}</el-radio>
                            <el-radio label="KO">{{ $t('config.ko') }}</el-radio>
                          </div>
                        </el-radio-group>
                      </el-form-item>
                    </div>
                    <!-- å·¥ä½œä¸»é¢˜ -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.themeMode')">
                        <el-radio-group v-model="base.appMode">
                          <el-radio :label="0">{{ $t('config.standardMode') }}</el-radio>
                          <el-radio :label="1">{{ $t('config.simpleMode') }}</el-radio>
                        </el-radio-group>
                      </el-form-item>
                    </div>
                    <!-- æ˜¯å¦ç¬¬ä¸€æ¬¡ç™»å½•后台 -->
                    <!-- <div class="config-form-item">
                      <el-form-item :label="$t('config.firstLogin')">
                        <el-radio-group v-model="base.firstLogin">
                          <el-radio :label="0">{{ $t('config.no') }}</el-radio>
                          <el-radio :label="1">{{ $t('config.yes') }}</el-radio>
                        </el-radio-group>
                      </el-form-item>
                    </div> -->
                  </el-form>
                </div>
              </el-card>
            </div>
          </el-tab-pane>
          <!-- ç½‘络配置 -->
          <el-tab-pane :label="$t('config.networkConfiguration')" name="net">
            <div class="config-grid">
              <!-- ç½‘络类型卡片 -->
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-connection config-card__header-icon"></i>
                  {{ $t('config.networkType') }}
                </div>
                <div class="config-card__body">
                  <el-form ref="networkForm" :model="net" :rules="netRules" label-width="140px">
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.networkType')">
                        <el-radio-group v-model="net.type">
                          <el-radio :label="1">{{ $t('config.ethernet') }}</el-radio>
                          <el-radio :label="2"
                            v-model-permission="['vf203', 'vf105', 'vf107', 'vf114', 'vf124', 'vf205']">WiFi</el-radio>
                          <el-radio :label="4" v-model-permission="['vf203']">4G</el-radio>
                        </el-radio-group>
                      </el-form-item>
                    </div>
                    <template v-if="net.type === 2">
                      <div class="config-form-item">
                        <el-form-item :label="$t('config.wifiName')">
                          <el-input v-model="net.ssid" type="text"
                            :placeholder="$t('config.msg_please_enter')"></el-input>
                        </el-form-item>
                      </div>
                      <div class="config-form-item">
                        <el-form-item :label="$t('config.wifiPassword')">
                          <el-input v-model="net.psk" type="text"
                            :placeholder="$t('config.msg_please_enter')"></el-input>
                        </el-form-item>
                      </div>
                    </template>
                  </el-form>
                </div>
              </el-card>
              <el-card class="config-card card-zhanwei"></el-card>
              <!-- IP配置卡片 -->
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-s-platform config-card__header-icon"></i>
                  {{ $t('config.ipConfiguration') }}
                </div>
                <div class="config-card__body">
                  <el-form ref="ipForm" :model="net" :rules="netRules" label-width="140px">
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.dhcpModeSelection')">
                        <el-radio-group v-model="net.dhcp">
                          <el-radio :label="1">{{ $t('config.customNetworkConfiguration') }}</el-radio>
                          <el-radio :label="2">{{ $t('config.dhcpMode') }}</el-radio>
                        </el-radio-group>
                      </el-form-item>
                    </div>
                    <template v-if="net.dhcp === 1">
                      <div class="config-form-item">
                        <el-form-item :label="$t('config.ipAddress')" prop="ip">
                          <el-input v-model="net.ip" placeholder="192.168.10.99"></el-input>
                        </el-form-item>
                      </div>
                      <div class="config-form-item">
                        <el-form-item :label="$t('config.gateway')" prop="gateway">
                          <el-input v-model="net.gateway" placeholder="192.168.1.1"></el-input>
                        </el-form-item>
                      </div>
                      <div class="config-form-item">
                        <el-form-item :label="$t('config.subnetMask')" prop="mask">
                          <el-input v-model="net.mask" placeholder="255.255.255.0"></el-input>
                        </el-form-item>
                      </div>
                      <div class="config-form-item">
                        <el-form-item :label="$t('config.dnsServer')" prop="dns">
                          <el-input v-model="net.dns" placeholder="8.8.8.8"></el-input>
                        </el-form-item>
                      </div>
                    </template>
                  </el-form>
                </div>
              </el-card>
              <!-- å…¶ä»–配置 -->
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-s-platform config-card__header-icon"></i>
                  {{ $t('config.otherConfiguration') }}
                </div>
                <div class="config-card__body">
                  <el-form ref="ipForm" label-width="140px">
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.mac')">
                        <el-input v-model="net.mac" placeholder="D6:18:6C:CE:B4:60" :disabled="true"></el-input>
                      </el-form-item>
                    </div>
                  </el-form>
                </div>
              </el-card>
              <el-card class="config-card card-zhanwei"></el-card>
            </div>
          </el-tab-pane>
          <!-- mqtt配置 -->
          <el-tab-pane :label="$t('config.mqttRelatedConfiguration')" name="mqtt" v-if="!isWeCom">
            <div class="config-grid">
              <!-- mqtt信息配置 -->
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-connection config-card__header-icon"></i>
                  {{ $t('config.mqttConnectionInformation') }}({{ ($t('config.enterpriseWechat')) }})
                </div>
                <div class="config-card__body">
                  <el-form ref="mqttForm" :model="mqtt" :rules="mqttRules" label-width="140px">
                    <!-- æœåŠ¡å™¨åœ°å€ -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.serverAddress')" prop="addr">
                        <el-input v-model="mqtt.addr" :placeholder="$t('config.msg_please_enter')"></el-input>
                      </el-form-item>
                    </div>
                    <!-- ç”¨æˆ·å -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.userName')">
                        <el-input v-model="mqtt.username" type="text"
                          :placeholder="$t('config.msg_please_enter')"></el-input>
                      </el-form-item>
                    </div>
                    <!-- ç”¨æˆ·å¯†ç  -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.userPassword')">
                        <el-input v-model="mqtt.password" type="text"
                          :placeholder="$t('config.msg_please_enter')"></el-input>
                      </el-form-item>
                    </div>
                    <!-- qos -->
                    <div class="config-form-item">
                      <el-form-item label="Qos">
                        <el-radio-group v-model="mqtt.qos">
                          <el-radio :label="0">Qos0</el-radio>
                          <el-radio :label="1">Qos1</el-radio>
                          <el-radio :label="2">Qos2</el-radio>
                        </el-radio-group>
                      </el-form-item>
                    </div>
                    <!-- å®¢æˆ·ç«¯Id -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.clientID')">
                        <el-input v-model="mqtt.clientId" type="text" :placeholder="$t('config.msg_please_enter')"
                          disabled></el-input>
                      </el-form-item>
                    </div>
                    <!-- æ˜¯å¦åŠ éšæœºæ•° -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.clientIdSuffix')">
                        <el-radio-group v-model="mqtt.clientIdSuffix">
                          <el-radio :label="0">{{ $t('config.no') }}</el-radio>
                          <el-radio :label="1">{{ $t('config.yes') }}</el-radio>
                        </el-radio-group>
                      </el-form-item>
                    </div>
                    <!-- æ¸…除会话 -->
                    <!-- <div class="config-form-item">
                      <el-form-item :label="$t('config.cleanSession')">
                        <el-radio-group v-model="mqtt.cleanSession">
                          <el-radio :label="0">{{ $t('config.no') }}</el-radio>
                          <el-radio :label="1">{{ $t('config.yes') }}</el-radio>
                        </el-radio-group>
                      </el-form-item>
                    </div> -->
                  </el-form>
                </div>
              </el-card>
              <!-- ä¼šè¯é…ç½® -->
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-s-operation config-card__header-icon"></i>
                  {{ $t('config.sessionConfiguration') }}
                </div>
                <div class="config-card__body">
                  <el-form ref="onlineCheckingForm" :model="mqtt" :rules="mqttRules" label-width="140px">
                    <!-- ä¸»é¢˜å‰ç¼€ -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.topicPrefix')">
                        <el-input v-model="mqtt.prefix" type="text"
                          :placeholder="$t('config.msg_please_enter')"></el-input>
                      </el-form-item>
                    </div>
                    <!-- é—嘱 -->
                    <!-- <div class="config-form-item">
                      <el-form-item :label="$t('config.willTopic')">
                        <el-input v-model="mqtt.willTopic" type="text"
                          :placeholder="$t('config.msg_please_enter')"></el-input>
                      </el-form-item>
                    </div> -->
                  </el-form>
                </div>
              </el-card>
              <!-- <el-card class="config-card card-zhanwei"></el-card> -->
              <!-- åœ¨çº¿éªŒè¯å¡ç‰‡ -->
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-link config-card__header-icon"></i>
                  {{ $t('config.onlineChecking') }}
                </div>
                <div class="config-card__body">
                  <el-form ref="onlineCheckingForm" :model="mqtt" :rules="mqttRules" label-width="140px">
                    <!-- åœ¨çº¿éªŒè¯ -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.onlineChecking')">
                        <el-switch v-model="mqtt.onlinecheck" active-color="#1890ff" inactive-color="#ff4949"
                          :active-value="1" :inactive-value="0">
                        </el-switch>
                      </el-form-item>
                    </div>
                    <!-- åœ¨çº¿éªŒè¯è¶…æ—¶æ—¶é—´ -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.onlineCheckingTimeout')" prop="timeout">
                        <el-input v-model="mqtt.timeout" :placeholder="$t('config.msg_please_enter')"></el-input><span>
                          {{ $t('config.second') }}</span>
                      </el-form-item>
                    </div>
                  </el-form>
                </div>
              </el-card>
            </div>
          </el-tab-pane>
          <!-- äººè„¸é…ç½® -->
          <el-tab-pane :label="$t('config.faceRelatedConfiguration')" name="face">
            <div class="config-grid">
              <!-- mqtt信息配置 -->
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-info config-card__header-icon"></i>
                  {{ $t('config.functionalInformation') }}
                </div>
                <div class="config-card__body">
                  <el-form ref="faceForm" :model="face" label-width="140px">
                    <!-- äººè„¸è¯†åˆ«ç›¸ä¼¼åº¦ -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.faceSimilarityThreshold')">
                        <div class="compact-controls">
                          <el-slider v-model="face.similarity" :max="100" :min="0" style="width:60%;"></el-slider>
                          <span class="value-display">{{ face.similarity }}%</span>
                        </div>
                      </el-form-item>
                    </div>
                    <!-- æ´»ä½“检测 -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.livenessDetectionFunction')">
                        <el-switch v-model="face.livenessOff" :active-value="1" :inactive-value="0"
                          active-color="#1890ff" inactive-color="#ff4949">
                        </el-switch>
                      </el-form-item>
                    </div>
                    <!-- æ´»ä½“检测阈值 -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.livenessDetectionThreshold')">
                        <div class="compact-controls">
                          <el-slider v-model="face.livenessVal" :max="100" :min="0" style="width:60%;"></el-slider>
                          <span class="value-display">{{ face.livenessVal }}</span>
                        </div>
                      </el-form-item>
                    </div>
                    <!-- çº¢å¤–框开关 -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.infraredImageDisplay')">
                        <el-switch v-model="face.showNir" :active-value="1" :inactive-value="0" active-color="#1890ff"
                          inactive-color="#ff4949">
                        </el-switch>
                      </el-form-item>
                    </div>
                    <!-- å£ç½©æ£€æµ‹å¼€å…³ -->
                    <!-- <div class="config-form-item">
                      <el-form-item :label="$t('config.maskRecognition')">
                        <el-switch v-model="face.detectMask" :active-value="1" :inactive-value="0"
                          active-color="#1890ff" inactive-color="#ff4949">
                        </el-switch>
                      </el-form-item>
                    </div> -->
                    <!-- é‡æ£€å¼€å…³ -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.recognitionSwitch')">
                        <el-switch v-model="face.recheck" :active-value="1" :inactive-value="0" active-color="#1890ff"
                          inactive-color="#ff4949">
                        </el-switch>
                      </el-form-item>
                    </div>
                  </el-form>
                </div>
              </el-card>
              <el-card class="config-card card-zhanwei"></el-card>
              <!-- æç¤ºè¯­ -->
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-s-grid config-card__header-icon"></i>
                  {{ $t('config.prompt') }}
                </div>
                <div class="config-card__body">
                  <el-form ref="faceMsgForm" :model="face" :rules="faceRules" label-width="140px">
                    <!-- æœªæ³¨å†Œäººè„¸æç¤º -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.strangerVoice')">
                        <el-radio-group v-model="face.stranger">
                          <el-radio :label="0">{{ $t('config.noVoice') }}</el-radio>
                          <el-radio :label="1">{{ $t('config.broadcastPleaseRegisterFirst') }}</el-radio>
                          <el-radio :label="2">{{ $t('config.broadcastHelloStranger') }}</el-radio>
                        </el-radio-group>
                      </el-form-item>
                    </div>
                    <!-- è¯†åˆ«æˆåŠŸæ’­æ”¾è¯­éŸ³ -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.voiceMode')">
                        <el-radio-group v-model="face.voiceMode">
                          <el-radio :label="0">{{ $t('config.noVoice') }}</el-radio>
                          <el-radio :label="1">{{ $t('config.broadcastName') }}</el-radio>
                          <el-radio :label="2">{{ $t('config.broadcastGreeting') }}</el-radio>
                        </el-radio-group>
                      </el-form-item>
                    </div>
                    <!-- è‡ªå®šä¹‰æ¬¢è¿Žè¯­ -->
                    <div class="config-form-item" v-if="face.voiceMode == 2">
                      <el-form-item :label="$t('config.voiceModeDate')">
                        <el-input v-model="face.voiceModeDate" :placeholder="$t('config.msg_please_enter')"></el-input>
                      </el-form-item>
                    </div>
                  </el-form>
                </div>
              </el-card>
            </div>
          </el-tab-pane>
          <!-- ç³»ç»Ÿé…ç½® -->
          <el-tab-pane :label="$t('config.systemRelatedConfiguration')" name="sys">
            <div class="config-grid">
              <!-- åŠŸèƒ½å¼€å…³é…ç½® -->
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-video-play config-card__header-icon"></i>
                  {{ $t('config.functionSwitch') }}
                </div>
                <div class="config-card__body">
                  <el-form ref="sysForm" :model="sys" label-width="140px">
                    <!-- åˆ·å¡æ ¸éªŒ -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.cardSwipingSwitch')">
                        <el-switch v-model="sys.nfc" :active-value="1" :inactive-value="0" active-color="#1890ff"
                          inactive-color="#ff4949">
                        </el-switch>
                      </el-form-item>
                    </div>
                    <!-- å¯†ç å¼€é—¨ -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.passwordSwitch')">
                        <el-switch v-model="sys.pwd" :active-value="1" :inactive-value="0" active-color="#1890ff"
                          inactive-color="#ff4949">
                        </el-switch>
                      </el-form-item>
                    </div>
                    <!-- é™Œç”Ÿäººä¿å­˜å›¾ç‰‡ -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.strangerImage')">
                        <el-radio-group v-model="sys.strangerImage">
                          <el-radio :label="0">{{ $t('config.notsave') }}</el-radio>
                          <el-radio :label="1">{{ $t('config.save') }}</el-radio>
                        </el-radio-group>
                      </el-form-item>
                    </div>
                    <!-- äº‘证开关 -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.cloudCertificateSwitch')">
                        <el-switch v-model="sys.nfcIdentityCardEnable" :active-value="3" :inactive-value="1"
                          active-color="#1890ff" inactive-color="#ff4949">
                        </el-switch>
                      </el-form-item>
                    </div>
                  </el-form>
                </div>
              </el-card>
              <!-- å¿ƒè·³è®¾ç½® -->
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-setting config-card__header-icon"></i>
                  {{ $t('config.heartbeatConfig') }}
                </div>
                <div class="config-card__body">
                  <el-form ref="sysForm" :model="sys" label-width="140px">
                    <!-- å¿ƒè·³å¼€å…³ -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.heartbeatSwitch')">
                        <el-switch v-model="sys.heart_en" :active-value="1" :inactive-value="0" active-color="#1890ff"
                          inactive-color="#ff4949">
                        </el-switch>
                      </el-form-item>
                    </div>
                    <!-- å¿ƒè·³é—´éš” -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.heartRateInterval')" prop="heart_time">
                        <el-input v-model="sys.heart_time" :placeholder="$t('config.msg_please_enter')" type="number"
                          :min="1"></el-input><span>
                          {{ $t('config.second') }}</span>
                      </el-form-item>
                    </div>
                    <!-- å¿ƒè·³ä¸»é¢˜ -->
                    <!-- <div class="config-form-item">
                      <el-form-item :label="$t('config.heartbeatTopic')" prop="heart_topic">
                        <el-input v-model="sys.heart_topic" :placeholder="$t('config.msg_please_enter')"></el-input>
                      </el-form-item>
                    </div> -->
                    <!-- å¿ƒè·³å†…容 -->
                    <!-- <div class="config-form-item">
                      <el-form-item :label="$t('config.heartbeatContent')" prop="heart_payload">
                        <el-input v-model="sys.heart_payload" :placeholder="$t('config.msg_please_enter')"></el-input>
                      </el-form-item>
                    </div> -->
                  </el-form>
                </div>
              </el-card>
              <!-- æ‰«ç è®¾ç½® -->
              <el-card class="config-card card-theme-display" v-model-permission="['vf105', 'vf114']">
                <div class="config-card__header">
                  <i class="el-icon-full-screen config-card__header-icon"></i>
                  {{ $t('config.scanSettings') }}
                </div>
                <div class="config-card__body">
                  <el-form ref="sysForm" :model="sys" label-width="140px">
                    <!-- æ‰«ç å¼€å…³ -->
                    <!-- <div class="config-form-item">
                      <el-form-item :label="$t('config.scanSwitch')">
                        <el-switch v-model="sys.scan" :active-value="1" :inactive-value="0" active-color="#1890ff"
                          inactive-color="#ff4949">
                        </el-switch>
                      </el-form-item>
                    </div> -->
                    <!-- æ‰«ç é—´éš” -->
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.scanInterval')" prop="scanInterval">
                        <el-input v-model="sys.scanInterval"
                          :placeholder="$t('config.msg_please_enter')"></el-input><span>
                          {{ $t('config.second') }}</span>
                      </el-form-item>
                    </div>
                  </el-form>
                </div>
              </el-card>
              <!-- åŸºæœ¬ä¿¡æ¯ -->
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-info config-card__header-icon"></i>
                  {{ $t('config.basicInformation') }}
                </div>
                <div class="config-card__body">
                  <el-form ref="accessForm" :model="sys" label-width="140px">
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.deviceMac')">
                        <el-input v-model="sys.mac" :placeholder="$t('config.msg_please_enter')" disabled></el-input>
                      </el-form-item>
                    </div>
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.uuid')">
                        <el-input v-model="sys.uuid" :placeholder="$t('config.msg_please_enter')" disabled></el-input>
                      </el-form-item>
                    </div>
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.sn')">
                        <el-input v-model="sys.sn" :placeholder="$t('config.msg_please_enter')" disabled></el-input>
                      </el-form-item>
                    </div>
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.model')">
                        <el-input v-model="sys.model" :placeholder="$t('config.msg_please_enter')" disabled></el-input>
                      </el-form-item>
                    </div>
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.version')">
                        <el-input v-model="sys.version" :placeholder="$t('config.msg_please_enter')"
                          disabled></el-input>
                      </el-form-item>
                    </div>
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.appVersion')">
                        <el-input v-model="sys.appVersion" :placeholder="$t('config.msg_please_enter')"
                          disabled></el-input>
                      </el-form-item>
                    </div>
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.releaseTime')">
                        <el-input v-model="sys.releaseTime" :placeholder="$t('config.msg_please_enter')"
                          disabled></el-input>
                      </el-form-item>
                    </div>
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.totaldisk')">
                        <el-input v-model="sys.totaldisk" :placeholder="$t('config.msg_please_enter')"
                          disabled></el-input>
                      </el-form-item>
                    </div>
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.freedisk')">
                        <el-input v-model="sys.freedisk" :placeholder="$t('config.msg_please_enter')"
                          disabled></el-input>
                      </el-form-item>
                    </div>
                  </el-form>
                </div>
              </el-card>
            </div>
          </el-tab-pane>
          <!-- é€šè¡Œé…ç½® -->
          <el-tab-pane :label="$t('config.passageConfiguration')" name="access">
            <div class="config-grid">
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-connection config-card__header-icon"></i>
                  {{ $t('config.functionConfiguration') }}
                </div>
                <div class="config-card__body">
                  <el-form ref="accessForm" :model="access" :rules="accessRules" label-width="140px">
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.numberOfPassageRecords')">
                        <el-input v-model="access.offlineAccessNum"
                          :placeholder="$t('config.msg_please_enter')"></el-input>
                      </el-form-item>
                    </div>
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.durationOfRelayOpening')">
                        <el-input v-model="access.relayTime" :placeholder="$t('config.msg_please_enter')"></el-input>
                        <span> {{ $t('config.second') }}</span>
                      </el-form-item>
                    </div>
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.fireAlarmSwitch')">
                        <el-switch v-model="access.fire" :active-value="1" :inactive-value="0" active-color="#1890ff"
                          inactive-color="#ff4949">
                        </el-switch>
                      </el-form-item>
                    </div>
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.fireAlarmStatus')">
                        <el-radio-group v-model="access.fireStatus">
                          <el-radio :label="0">{{ $t('config.normal') }}</el-radio>
                          <el-radio :label="1">{{ $t('config.warning') }}</el-radio>
                        </el-radio-group>
                      </el-form-item>
                    </div>
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.tamperSwitch')">
                        <el-switch v-model="access.tamper" :active-value="1" :inactive-value="0" active-color="#1890ff"
                          inactive-color="#ff4949">
                        </el-switch>
                      </el-form-item>
                    </div>
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.uploadToCloudSwitch')">
                        <el-switch v-model="access.uploadToCloud" :active-value="1" :inactive-value="0"
                          active-color="#1890ff" inactive-color="#ff4949">
                        </el-switch>
                      </el-form-item>
                    </div>
                  </el-form>
                </div>
              </el-card>
            </div>
          </el-tab-pane>
          <!-- æ—¶é’Ÿé…ç½® -->
          <el-tab-pane :label="$t('config.clockConfiguration')" name="ntp">
            <div class="config-grid">
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-connection config-card__header-icon"></i>
                  {{ $t('config.clockConfiguration') }}({{ $t('config.restartAfterSetting') }})
                </div>
                <div class="config-card__body">
                  <el-form ref="ntpForm" :model="ntp" :rules="ntpRules" label-width="140px">
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.timeSynchronizationServerIP')" prop="server">
                        <el-input v-model="ntp.server" :placeholder="$t('config.msg_please_enter')"></el-input>
                      </el-form-item>
                    </div>
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.timeZone')" prop="gmt">
                        <div class="timezone-selects">
                          <el-select v-model="selectedTimezoneRegion" :placeholder="$t('config.msg_please_enter')"
                            @change="handleRegionChange">
                            <el-option v-for="region in timezoneRegions" :key="region.value" :label="region.label"
                              :value="region.value"></el-option>
                          </el-select>
                          <el-select v-model="selectedTimezoneCity" :placeholder="$t('config.msg_please_enter')"
                            @change="handleCityChange">
                            <el-option v-for="city in timezoneCities" :key="city.value" :label="city.label"
                              :value="city.value"></el-option>
                          </el-select>
                        </div>
                      </el-form-item>
                    </div>
                  </el-form>
                </div>
              </el-card>
            </div>
          </el-tab-pane>
          <!-- äº‘证激活 -->
          <el-tab-pane :label="$t('config.cloudCertificateActivation')" name="cloud">
            <div class="config-grid">
              <el-card class="config-card card-theme-display">
                <div class="config-card__header cloud-card-header">
                  <i class="el-icon-cloudy config-card__header-icon cloud-icon"></i>
                  {{ $t('config.cloudCertificateActivation') }}
                  <!-- <div class="cloud-status" :class="cloudStatusClass">
                    {{ cloudStatusText }}
                  </div> -->
                </div>
                <div class="config-card__body">
                  <el-form ref="cloudForm" :model="cloudForm" :rules="cloudRules" label-position="top">
                    <div class="activation-container">
                      <!-- æ¿€æ´»å¯†é’¥è¾“入区域 -->
                      <div class="activation-input-section">
                        <el-form-item :label="$t('config.activationKey')" prop="activationKey"
                          class="activation-form-item">
                          <el-input v-model="cloudForm.activationKey" type="textarea"
                            :placeholder="$t('config.msg_please_enter')" class="activation-input" clearable>
                            <template #prefix>
                              <i class="el-icon-key"></i>
                            </template>
                          </el-input>
                          <!-- è¾“入提示 -->
                          <div class="activation-tips">
                            <div class="tip-item">
                              <i class="el-icon-info"></i>
                              <span>{{ $t('config.cloudTips1') }}</span>
                            </div>
                            <div class="tip-item">
                              <i class="el-icon-warning-outline"></i>
                              <span>{{ $t('config.cloudTips2') }}</span>
                            </div>
                          </div>
                        </el-form-item>
                      </div>
                      <!-- æ¿€æ´»æŒ‰é’®åŒºåŸŸ -->
                      <div class="activation-action-section">
                        <el-button type="primary" class="activation-btn" :loading="activating"
                          :disabled="!cloudForm.activationKey" @click="handleActivate">
                          <i class="el-icon-check"></i>
                          {{ activating ? $t('config.activationInProgress') : $t('config.confirmActivation') }}
                        </el-button>
                      </div>
                      <!-- æ¿€æ´»çŠ¶æ€æ˜¾ç¤º -->
                      <!-- <div v-if="activationResult" class="activation-result" :class="activationResultClass">
                        <i :class="resultIcon"></i>
                        <span>{{ activationResult.message }}</span>
                        <span v-if="activationResult.details" class="result-details">{{ activationResult.details
                        }}</span>
                      </div> -->
                    </div>
                  </el-form>
                </div>
              </el-card>
            </div>
          </el-tab-pane>
          <!-- å¯†ç é…ç½® -->
          <el-tab-pane :label="$t('config.passwordModification')" name="pwd">
            <div class="config-grid">
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-connection config-card__header-icon"></i>
                  {{ $t('config.password') }}
                </div>
                <div class="config-card__body">
                  <el-form ref="pwdForm" :model="pwd" :rules="pwdRules" label-width="140px">
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.oldPassword')" prop="oldPwd">
                        <el-input style="border:1px solid #eee; width:370px;border-radius: 10px;" v-model="pwd.oldPwd"
                          autocomplete="off"></el-input>
                      </el-form-item>
                    </div>
                    <div class="config-form-item" style="clear: both;">
                      <el-form-item :label="$t('config.newPassword')" prop="newPwd">
                        <el-input style="border:1px solid #eee; width:370px;border-radius: 10px;" v-model="pwd.newPwd"
                          autocomplete="off"></el-input>
                      </el-form-item>
                    </div>
                    <div class="config-form-item">
                      <el-form-item :label="$t('config.confirmPassword')" prop="quePwd">
                        <el-input style="border:1px solid #eee; width:370px;border-radius: 10px;" v-model="pwd.quePwd"
                          autocomplete="off"></el-input>
                      </el-form-item>
                    </div>
                  </el-form>
                  <!-- å¯†ç è§„则提示语 -->
                  <div class="password-rules-tip">
                    <div class="rules-title">{{ $t('config.passwordRule') }}:</div>
                    <ul class="rules-list">
                      <li>{{ $t('config.passwordLength') }}</li>
                      <li>{{ $t('config.cannotBeTheSame') }}</li>
                      <li>{{ $t('config.cannotOrder') }}</li>
                      <li>{{ $t('config.cannotWeakPassword') }}:</li>
                      <li class="weak-passwords">"password", "admin", "qwerty", "iloveyou"<br>
                        "letmein", "welcome", "sunshine", "monkey"</li>
                    </ul>
                  </div>
                </div>
              </el-card>
            </div>
          </el-tab-pane>
          <!-- èµ„源配置 -->
          <el-tab-pane :label="$t('config.resourceConfiguration')" name="resource">
            <div class="config-grid">
              <el-card class="config-card card-theme-display">
                <div class="config-card__header">
                  <i class="el-icon-picture-outline config-card__header-icon"></i>
                  {{ $t('config.backgroundImage') }}
                </div>
                <div class="config-card__body">
                  <div class="background-upload-section">
                    <div class="upload-area">
                      <el-upload class="background-upload" action="#" :show-file-list="false" accept="image/png"
                        :before-upload="handleBackgroundSelect">
                        <el-button type="primary" icon="el-icon-folder-opened">
                          {{ $t('config.selectImage') }}
                        </el-button>
                      </el-upload>
                      <div v-if="background.name" class="file-info">
                        <i class="el-icon-document"></i>
                        <span>{{ background.name }}</span>
                      </div>
                    </div>
                    <div class="upload-actions">
                      <el-button type="primary" icon="el-icon-upload" :disabled="!background.base64"
                        :loading="loading.background" @click="uploadBackground">
                        {{ loading.background ? $t('config.uploading') : $t('config.uploadBackground') }}
                      </el-button>
                    </div>
                    <div class="upload-tips">
                      <p v-if="model == 'vf202'">{{ $t('config.backgroundUploadTip').replace('{n}', '480*854') }}</p>
                      <p v-if="model == 'vf105' || model == 'vf107' || model == 'vf205'">{{
                        $t('config.backgroundUploadTip').replace('{n}', '800*1280') }}</p>
                      <p v-if="model == 'vf114' || model == 'vf124'">{{ $t('config.backgroundUploadTip').replace('{n}',
                        '720*1280') }}</p>
                      <p v-if="model == 'vf203'">{{ $t('config.backgroundUploadTip').replace('{n}', '600*1024') }}</p>
                    </div>
                  </div>
                </div>
              </el-card>
            </div>
          </el-tab-pane>
        </el-tabs>
      </div>
      <el-button v-if="activeTab != 'cloud' && activeTab != 'resource'" class="config-save-btn" type="primary"
        icon="el-icon-check" size="large" @click="saveAll">{{
          $t('config.saveConfig') }}</el-button>
    </div>
  </div>
</template>
<script>
import { throttle } from '@/utils/index.js'
import { timezones, timezoneRegionNames } from '@/utils/timezones.js'
export default {
  data() {
    // å¯†ç éªŒè¯è§„则
    const validatePassword = (rule, value, callback) => {
      if (!value || value.trim() === '') {
        callback(new Error(this.$t('config.msg_inputPassword')));
      } else {
        callback();
      }
    };
    // ç¡®è®¤å¯†ç éªŒè¯è§„则(必须与新密码一致)
    const validateConfirmPassword = (rule, value, callback) => {
      if (value !== this.pwd.newPwd) {
        callback(new Error(this.$t('config.msg_password_mismatch')));
      } else {
        callback();
      }
    };
    return {
      activeTab: 'base',
      isWeCom: false,
      base: {
        screenOff: 0,
        screensaver: 0,
        brightness: 70,
        // brightnessAuto: 1,
        volume: 50,
        showIp: 1,
        showSn: 1,
        // showProgramCode: 1,
        // showIdentityCard: 1,
        language: 'CN',
        appMode: 1,
        // nirBrightness: 80,
        backlight: 70,
        // firstLogin: 0
      },
      idleData: [this.$t('config.never'), this.$t('config.min1'), this.$t('config.min2'), this.$t('config.min3'), this.$t('config.min4'), this.$t('config.min5')],
      baseRules: {
        screenOff: [{
          message: this.$t("common.integerFormat"),
          trigger: ["blur"],
          pattern: /^[0-9]+$/
        }],
        screensaver: [{
          message: this.$t("common.integerFormat"),
          trigger: ["blur"],
          pattern: /^[0-9]+$/
        }]
      },
      net: {
        type: 1,
        ssid: '',
        psk: '',
        dhcp: 2,
        ip: '',
        gateway: '',
        mask: '',
        dns: '',
        mac: ''
      },
      netRules: {
        ip: [
          {
            message: this.$t("common.incorrectFormat"),
            trigger: ["blur"],
            pattern: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
          }
        ],
        gateway: [
          {
            message: this.$t("common.incorrectFormat"),
            trigger: ["blur"],
            pattern: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
          }
        ],
        mask: [
          {
            message: this.$t("common.incorrectFormat"),
            trigger: ["blur"],
            pattern: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
          }
        ],
        dns: [
          {
            message: this.$t("common.incorrectFormat"),
            trigger: ["blur"],
            pattern: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
          }
        ]
      },
      mqtt: {
        addr: '',
        username: '',
        password: '',
        prefix: '',
        qos: 0,
        onlinecheck: '',
        timeout: '',
        // cleanSession: 0,
        clientId: '',
        clientIdSuffix: '',
        // willTopic: '',
      },
      mqttRules: {
        addr: [
          {
            validator: (rule, value, callback) => {
              const supportedProtocols = ['tcp://', 'ssl://', 'mqtt://', 'mqtts://'];
              const ipOrDomainWithPort = /^(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})(:\d{1,5})?$/
              let hostPort = value;
              let hasSupportedProtocol = false;
              for (const protocol of supportedProtocols) {
                if (value.startsWith(protocol)) {
                  hasSupportedProtocol = true;
                  hostPort = value.slice(protocol.length); // åŽ»æŽ‰åè®®å‰ç¼€
                  break;
                }
              }
              if (!hasSupportedProtocol && value.includes('://')) callback(new Error(this.$t("common.incorrectFormat")));
              if (!hostPort) {
                callback();
                return;
              }
              if (!ipOrDomainWithPort.test(hostPort)) callback(new Error(this.$t("common.incorrectFormat")));
              const portMatch = hostPort.match(/:(\d{1,5})$/);
              if (portMatch) {
                const port = parseInt(portMatch[1], 10);
                if (port < 1 || port > 65535) callback(new Error(this.$t("common.incorrectFormat")));
              }
              callback();
            },
            trigger: ["blur"]
          }],
        timeout: [
          {
            message: this.$t("common.incorrectFormat"),
            trigger: ["blur"],
            pattern: /^[1-9]\d*$/
          }
        ],
      },
      face: {
        similarity: 60,
        livenessOff: 1,
        livenessVal: 0,
        showNir: 0,
        stranger: 0,
        voiceMode: 0,
        voiceModeDate: "",
        recheck: 0
      },
      faceRules: {
        // voiceModeDate: [
        //   {
        //     message: this.$t("config.msg_charactersAndNumbers"),
        //     trigger: ["blur"],
        //     pattern: /^[\u4e00-\u9fa5]*([1-9]\d*)?[\u4e00-\u9fa5]*$/
        //   }
        // ],
      },
      sys: {
        nfc: 1,
        pwd: 1,
        strangerImage: 1,
        heart_en: 0,
        heart_time: 30,
        // heart_topic: '',
        // heart_payload: '',
        mac: '',
        uuid: '',
        sn: '',
        model: '',
        version: '',
        appVersion: '',
        releaseTime: '',
        nfcIdentityCardEnable: 1,
        // scan: '',
        scanInterval: '',
        totaldisk: '',
        freedisk: '',
      },
      sysRules: {
        heart_time: [{
          message: this.$t("common.positiveIntegerFormat"),
          trigger: ["blur"],
          pattern: /^[1-9]\d*$/
        }],
        scanInterval: [
          {
            message: this.$t("common.incorrectFormat"),
            trigger: ["blur"],
            pattern: /^[1-9]\d*$/
          }
        ],
      },
      access: {
        offlineAccessNum: 2000,
        relayTime: 2000,
        fire: 0,
        fireStatus: 0,
        tamper: 0,
        uploadToCloud: 0,
      },
      accessRules: {},
      ntp: {
        server: '182.92.12.11',
        gmt: 'Asia/Shanghai'
      },
      selectedTimezoneRegion: '',
      selectedTimezoneCity: '',
      ntpRules: {
        ip: [
          {
            message: this.$t("common.incorrectFormat"),
            trigger: ["blur"],
            pattern: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
          }
        ],
        gmt: [
          {
            required: true,
            message: this.$t("config.msg_please_enter"),
            trigger: ["change"]
          }
        ],
      },
      pwd: {
        orignPwd: '',
        oldPwd: '',
        newPwd: '',
        quePwd: ''
      },
      pwdRules: {
        oldPwd: [
          {
            required: true,
            validator: validatePassword,
            trigger: ["blur"]
          }
        ],
        newPwd: [
          { required: true, validator: validatePassword, trigger: ["blur"] },
        ],
        quePwd: [
          { required: true, validator: validatePassword, trigger: ["blur"] },
          { validator: validateConfirmPassword, trigger: ["blur"] }
        ]
      },
      passwordStrength: 0, // å¯†ç å¼ºåº¦ç­‰çº§ 0-4
      isWeakPassword: false,
      cloudForm: {
        activationKey: ''
      },
      cloudRules: {
        activationKey: [
          { required: true, message: '请输入激活密钥', trigger: 'blur' },
          {
            message: this.$t("common.incorrectFormat"),
            trigger: ["blur"],
            pattern: /^___VBAR_ID_ACTIVE_V/
          }
        ]
      },
      activating: false,
      activationResult: null,
      cloudStatus: 'inactive', // 'active' æˆ– 'inactive'
      background: {
        base64: '',
        name: ''
      },
      loading: {
        background: false
      },
      reboot: {
        language: ''
      },
      originalBase: null, // ä¿å­˜ä»ŽæœåŠ¡å™¨èŽ·å–çš„åŽŸå§‹base配置
      version: 0, // ç‰ˆæœ¬ï¼š1-国际版本,0-国内版本
      model: ''
    }
  },
  created() {
    // ä»ŽsessionStorage获取version,默认为0(国内版本)
    let publicConfig = sessionStorage.getItem('publicConfig')
    let { version, isWeCom, model } = publicConfig ? JSON.parse(publicConfig) : {}
    if (version == null || version == undefined || version == "CN") {
      this.version = 0
    } else {
      this.version = 1
    }
    this.isWeCom = isWeCom
    this.model = model
    this.getConfig()
  },
  watch: {
    'ntp.gmt': function () {
      this.initTimezone()
    }
  },
  computed: {
    activeConfig() {
      let config = this[this.activeTab] || {};
      if (this.activeTab == 'base') {
        if (this.reboot.language == config.language) {
          config = { ...config }
          delete config.language
        }
      }
      if (this.activeTab == 'face') {
        config.similarity = config.similarity / 100
        config.livenessVal = config.livenessVal / 10
      }
      if (this.activeTab == 'pwd') {
        config = {
          password: config.newPwd
        }
      }
      if (this.activeTab == 'ntp') {
        const { gmt, ...rest } = config
        config = {
          ...rest,
          timeZone: gmt
        }
      }
      if (this.activeTab == 'sys') {
        let { nfc, pwd, strangerImage, heart_en, heart_time, nfcIdentityCardEnable, scanInterval } = config
        config = { nfc, pwd, strangerImage, heart_en, heart_time, nfcIdentityCardEnable }
        if (scanInterval) {
          config.scanInterval = scanInterval
        }
      }
      if (this.activeTab == 'mqtt') {
        config = { ...config }
        delete config.clientId
      }
      if (this.activeTab == 'net') {
        delete config.mac
      }
      return config
    },
    cloudStatusClass() {
      return this.cloudStatus === 'active' ? 'active' : 'inactive'
    },
    cloudStatusText() {
      return this.cloudStatus === 'active' ? '已激活' : '未激活'
    },
    activationResultClass() {
      return this.activationResult?.type || 'success'
    },
    resultIcon() {
      return this.activationResult?.type === 'error' ? 'el-icon-close' : 'el-icon-check'
    },
    timezoneRegions() {
      const regions = new Set()
      Object.keys(timezones).forEach(key => {
        const [region] = key.split('/')
        regions.add(region)
      })
      const locale = this.$i18n.locale || 'en'
      return Array.from(regions).map(r => ({ label: this.getRegionLabel(r, locale), value: r }))
    },
    timezoneCities() {
      return this.getCitiesByRegion(this.selectedTimezoneRegion || 'Asia')
    },
  },
  methods: {
    initTimezone() {
      const current = this.ntp.gmt && typeof this.ntp.gmt === 'string' ? this.ntp.gmt : 'Asia/Shanghai'
      const parts = current.split('/')
      const region = parts[0]
      const city = parts.slice(1).join('/')
      this.selectedTimezoneRegion = region || 'Asia'
      const cities = this.getCitiesByRegion(this.selectedTimezoneRegion)
      const defaultCity = city && cities.find(c => c.value === city) ? city : (cities[0] && cities[0].value)
      this.selectedTimezoneCity = defaultCity || ''
      if (this.selectedTimezoneRegion && this.selectedTimezoneCity) {
        this.ntp.gmt = `${this.selectedTimezoneRegion}/${this.selectedTimezoneCity}`
      }
    },
    getCitiesByRegion(region) {
      const locale = this.$i18n.locale || 'en'
      return Object.keys(timezones)
        .filter(key => key.startsWith(region + '/'))
        .map(key => {
          const city = key.split('/')[1]
          const tz = timezones[key]
          const label = (tz.name && (tz.name[locale] || tz.name.en)) || city
          return { value: city, label }
        })
    },
    getRegionLabel(region) {
      const locale = this.$i18n.locale || 'en'
      const map = timezoneRegionNames[locale] || timezoneRegionNames.en || {}
      return map[region] || region
    },
    handleRegionChange(region) {
      const cities = this.getCitiesByRegion(region)
      if (cities.length) {
        this.selectedTimezoneCity = cities[0].value
        this.ntp.gmt = `${region}/${this.selectedTimezoneCity}`
      } else {
        this.selectedTimezoneCity = ''
        this.ntp.gmt = ''
      }
    },
    handleCityChange(city) {
      if (this.selectedTimezoneRegion && city) {
        this.ntp.gmt = `${this.selectedTimezoneRegion}/${city}`
      }
    },
    handleActivate() {
      this.$refs['cloudForm'].validate(async (valid) => {
        if (valid) {
          let data = {
            code: this.cloudForm.activationKey
          }
          try {
            const res = await this.$http.post('/eidActive', { data })
            if (res.code == 200) {
              this.$message.success(this.$t('config.activationSuccessful'))
              this.cloudForm.activationKey = ''
            } else {
              this.$message.error(res.message)
            }
          } catch (error) {
            console.log(error)
          }
        }
      })
    },
    handleClick() {
      if (this.activeTab !== 'pwd') {
        this.getConfig()
        this.pwd = {
          orignPwd: '',
          oldPwd: '',
          newPwd: '',
          quePwd: ''
        }
      }
      if (this.activeTab !== 'cloud' && this.cloudForm.activationKey) {
        this.cloudForm.activationKey = ''
      }
      if (this.$refs.displayForm) {
        this.$refs.displayForm.clearValidate();
      }
      if (this.$refs.infoForm) {
        this.$refs.infoForm.clearValidate();
      }
      if (this.$refs.audioForm) {
        this.$refs.audioForm.clearValidate();
      }
      if (this.$refs.systemForm) {
        this.$refs.systemForm.clearValidate();
      }
      if (this.$refs.networkForm) {
        this.$refs.networkForm.clearValidate();
      }
      if (this.$refs.ipForm) {
        this.$refs.ipForm.clearValidate();
      }
      if (this.$refs.mqttForm) {
        this.$refs.mqttForm.clearValidate();
      }
      if (this.$refs.onlineCheckingForm) {
        this.$refs.onlineCheckingForm.clearValidate();
      }
      if (this.$refs.faceForm) {
        this.$refs.faceForm.clearValidate();
      }
      if (this.$refs.faceMsgForm) {
        this.$refs.faceMsgForm.clearValidate();
      }
      if (this.$refs.sysForm) {
        this.$refs.sysForm.clearValidate();
      }
      if (this.$refs.accessForm) {
        this.$refs.accessForm.clearValidate();
      }
      if (this.$refs.ntpForm) {
        this.$refs.ntpForm.clearValidate();
      }
      if (this.$refs.pwdForm) {
        this.$refs.pwdForm.clearValidate();
      }
      if (this.$refs.cloudForm) {
        this.$refs.cloudForm.clearValidate();
      }
    },
    async getConfig() {
      const res = await this.$http.post(
        "/getConfig",
        {
          data: ""
        }
      );
      console.log(res)
      if (res.code == 200) {
        let { base, mqtt, sys, face, ntp, access, net } = res.data || {}
        if (base) {
          this.base = this.mergeWithBase(this.base, base);
          // ä¿å­˜åŽŸå§‹base配置,用于后续比较
          this.originalBase = { ...base };
          this.pwd.orignPwd = base.password
          this.reboot.language = base.language
        }
        if (mqtt) {
          this.mqtt = this.mergeWithBase(this.mqtt, mqtt);
        }
        if (sys) {
          sys.totaldisk = sys.totaldisk + ' M'
          sys.freedisk = sys.freedisk + ' M'
          this.sys = this.mergeWithBase(this.sys, sys);
        }
        if (face) {
          face.similarity = Math.round(face.similarity * 100)
          face.livenessVal = face.livenessVal * 10
          this.face = this.mergeWithBase(this.face, face);
        }
        if (ntp) {
          const mergedNtp = this.mergeWithBase(this.ntp, ntp)
          if (ntp.timeZone) {
            mergedNtp.gmt = ntp.timeZone
          }
          this.ntp = mergedNtp
          this.initTimezone()
        } else {
          this.initTimezone()
        }
        if (access) this.access = this.mergeWithBase(this.access, access);
        if (net) this.net = this.mergeWithBase(this.net, net);
      } else {
        this.$message.error(res.message)
      }
    },
    saveAll: throttle(async function () {
      try {
        // åˆ†åˆ«éªŒè¯æ¯ä¸ªè¡¨å•
        if (this.activeTab == 'base') {
          await this.$refs.displayForm.validate();
        }
        if (this.activeTab == 'pwd') {
          await this.$refs.pwdForm.validate();
          console.log(this.pwd.orignPwd, this.pwd.oldPwd)
          if (this.pwd.orignPwd != this.pwd.oldPwd) {
            this.$message.error(this.$t('config.msg_oldPasswordError'));
            return
          }
        }
        if (this.activeTab == 'face') {
          await this.$refs.faceMsgForm.validate();
        }
        await this.submitData();
      } catch (error) {
        console.log(error)
      }
    }, 2000),
    async submitData() {
      const res = await this.$http.post(
        "/setConfig",
        {
          data: {
            [this.activeTab == 'pwd' ? 'base' : this.activeTab]: this.activeConfig
          }
        }
      );
      if (res.code == 200) {
        this.$message.success(this.$t('config.msg_saveSuccess'))
        if (this.activeTab == 'pwd') {
          this.pwd = {
            orignPwd: '',
            oldPwd: '',
            newPwd: '',
            quePwd: ''
          }
        }
        this.getConfig()
      } else {
        this.$message.error(res.message)
      }
    },
    // åªåˆå¹¶ç›®æ ‡å¯¹è±¡ä¸­å­˜åœ¨çš„属性
    mergeWithBase(target, source) {
      if (!source || typeof source !== 'object') {
        return { ...target };
      }
      const result = { ...target };
      const targetKeys = Object.keys(target);
      for (let i = 0; i < targetKeys.length; i++) {
        const key = targetKeys[i];
        if (Object.prototype.hasOwnProperty.call(source, key)) {
          result[key] = source[key];
        }
      }
      return result;
    },
    // å¤„理背景图片选择
    async handleBackgroundSelect(file) {
      if (file.type !== 'image/png') {
        this.$message.error(this.$t('config.backgroundImageOnlyPNG'));
        return false;
      }
      if (file.size > 5 * 1024 * 1024) {
        this.$message.error(this.$t('config.backgroundSizeLimit'));
        return false;
      }
      const targetResolution = this.getBackgroundResolutionByModel();
      if (targetResolution) {
        try {
          const { width, height } = await this.getImageSize(file);
          if (width !== targetResolution.width || height !== targetResolution.height) {
            this.$message.error(this.$t('config.backgroundResolutionMismatch').replace('{n}', `${targetResolution.width}*${targetResolution.height}`));
            return false;
          }
        } catch (error) {
          this.$message.error(this.$t('config.backgroundParseFailed'));
          return false;
        }
      }
      try {
        const base64 = await this.readFileAsBase64(file);
        this.background = {
          base64,
          name: file.name
        };
        this.$message.success(this.$t('config.backgroundImageSelected'));
      } catch (error) {
        this.$message.error(this.$t('config.backgroundParseFailed'));
      }
      return false;
    },
    getBackgroundResolutionByModel() {
      const resolutionMap = {
        vf202: { width: 480, height: 854 },
        vf105: { width: 800, height: 1280 },
        vf107: { width: 800, height: 1280 },
        vf205: { width: 800, height: 1280 },
        vf114: { width: 720, height: 1280 },
        vf124: { width: 720, height: 1280 },
        vf203: { width: 600, height: 1024 }
      };
      return resolutionMap[this.model] || null;
    },
    getImageSize(file) {
      return new Promise((resolve, reject) => {
        const image = new Image();
        const imageUrl = URL.createObjectURL(file);
        image.onload = () => {
          URL.revokeObjectURL(imageUrl);
          resolve({ width: image.width, height: image.height });
        };
        image.onerror = () => {
          URL.revokeObjectURL(imageUrl);
          reject(new Error('image read failed'));
        };
        image.src = imageUrl;
      });
    },
    // è¯»å–文件为Base64
    readFileAsBase64(file) {
      return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = () => {
          const result = reader.result || '';
          const base64 = typeof result === 'string' ? result.split(',').pop() : '';
          if (!base64) {
            reject(new Error('empty result'));
            return;
          }
          resolve(base64);
        };
        reader.onerror = () => reject(new Error('read failed'));
        reader.readAsDataURL(file);
      });
    },
    // ä¸Šä¼ èƒŒæ™¯å›¾ç‰‡
    async uploadBackground() {
      if (!this.background.base64) {
        this.$message.warning(this.$t('config.backgroundRequired'));
        return;
      }
      this.loading.background = true;
      try {
        const res = await this.$http.post("/control", { data: { command: 13, extra: { wallpaperBase64: this.background.base64 } } });
        if (res.code == 200) {
          this.$message.success(this.$t('config.backgroundSuccess'));
          this.background = {
            base64: '',
            name: ''
          };
        } else {
          this.$message.error(res.message || this.$t('config.backgroundFailed'));
        }
      } catch (error) {
        this.$message.error(this.$t('config.backgroundFailed'));
      } finally {
        this.loading.background = false;
      }
    },
  }
}
</script>
<style lang="less" scoped>
.config-container {
  padding: 20px;
  background-color: #f5f7fa;
  min-height: 100vh;
  max-width: 100vw;
  /* é™åˆ¶æœ€å¤§å®½åº¦ä¸ºè§†å£å®½åº¦ */
  overflow-x: hidden;
  /* éšè—æ¨ªå‘溢出 */
}
.config-grid {
  column-count: 2;
  /* ä¸¤åˆ— */
  column-gap: 20px;
  /* åˆ—间距 */
  margin-bottom: 80px;
  box-sizing: border-box;
  width: 100%;
}
.config-card {
  break-inside: avoid;
  /* é˜²æ­¢å¡ç‰‡è·¨åˆ—分割 */
  display: inline-block;
  /* å…³é”®å±žæ€§ */
  width: 100%;
  /* å æ»¡åˆ—宽 */
  margin-bottom: 20px;
  /* å¡ç‰‡é—´è· */
  border-radius: 12px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
  background: white;
  transition: transform 0.3s ease;
  vertical-align: top;
  /* é¡¶éƒ¨å¯¹é½ */
}
.config-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
}
.config-card__header {
  background: linear-gradient(90deg, #ecf5ff, #f0f9ff);
  border-bottom: 1px solid #ebeef5;
  padding: 16px 20px;
  border-top-left-radius: 12px;
  border-top-right-radius: 12px;
  font-weight: 600;
  color: #409EFF;
  display: flex;
  align-items: center;
}
.config-card__header-icon {
  margin-right: 8px;
  font-size: 18px;
}
.config-card__body {
  padding: 20px;
  display: flex;
  flex-direction: column;
}
/* ç¡®ä¿æ‰€æœ‰å¡ç‰‡å†…容自然高度 */
.card-content {
  display: flex;
  flex-direction: column;
  height: auto !important;
}
.config-form-item {
  display: flex;
  align-items: center;
  margin-bottom: 8px;
  padding-bottom: 8px;
  border-bottom: 1px dashed #ebeef5;
}
/* ç¡®ä¿el-form-item的label对齐 */
.config-card__body ::v-deep .el-form-item {
  display: flex;
  align-items: center;
  width: 100%;
  margin-bottom: 0;
}
.config-card__body ::v-deep .el-form-item__label {
  // width: 140px;
  text-align: right;
  padding-right: 15px;
  color: #606266;
  font-weight: 500;
  flex-shrink: 0;
}
.config-card__body ::v-deep .el-form-item__content {
  flex: 1;
  margin-left: 0 !important;
}
/* è¯­è¨€é€‰é¡¹æ¯è¡Œ3个布局 */
.language-radio-group {
  display: flex;
  flex-wrap: wrap;
  width: 100%;
}
.language-radio-group ::v-deep .el-radio {
  width: calc(33.333% - 10px);
  margin-right: 15px;
  margin-left: 0;
}
.language-radio-group ::v-deep .el-radio:nth-child(3n) {
  margin-right: 0;
}
.config-form-item:last-child {
  border-bottom: none;
  margin-bottom: 0;
  padding-bottom: 0;
}
.config-form-control {
  flex: 1;
}
.config-save-btn {
  position: fixed;
  bottom: 30px;
  right: 30px;
  z-index: 100;
  box-shadow: 0 4px 12px rgba(32, 160, 255, 0.4);
}
.config-tabs {
  background: transparent;
}
.config-tabs ::v-deep .el-tabs__header {
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  margin-bottom: 20px;
  border: none;
}
.config-tabs ::v-deep .el-tabs__item {
  font-weight: 500;
  height: 50px;
  line-height: 50px;
}
.compact-controls {
  display: flex;
  align-items: center;
  gap: 15px;
  flex-wrap: wrap;
}
.value-display {
  min-width: 40px;
  text-align: center;
  color: #409EFF;
  font-weight: 500;
}
.divider {
  height: 1px;
  background: linear-gradient(90deg, transparent, #dcdfe6, transparent);
  margin: 20px 0;
}
/* å“åº”式设计 */
@media (max-width: 1200px) {
  .config-grid {
    grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
  }
}
@media (max-width: 768px) {
  .config-container {
    padding: 10px;
  }
  .config-grid {
    grid-template-columns: 1fr;
    gap: 15px;
  }
  .config-form-item {
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
  }
  .config-form-control {
    width: 100%;
  }
  .compact-controls {
    width: 100%;
    justify-content: space-between;
  }
  /* å°å±å¹•下语言选项每行2个 */
  .language-radio-group ::v-deep .el-radio {
    width: calc(50% - 8px);
  }
  .language-radio-group ::v-deep .el-radio:nth-child(3n) {
    margin-right: 15px;
  }
  .language-radio-group ::v-deep .el-radio:nth-child(2n) {
    margin-right: 0;
  }
}
@media (max-width: 480px) {
  .config-card__body {
    padding: 15px;
  }
  .config-form-item {
    margin-bottom: 15px;
    padding-bottom: 15px;
  }
  /* è¶…小屏幕下语言选项每行1个 */
  .language-radio-group ::v-deep .el-radio {
    width: 100%;
    margin-right: 0;
  }
  .language-radio-group ::v-deep .el-radio:nth-child(2n) {
    margin-right: 0;
  }
}
/* å¡ç‰‡é¢œè‰²ä¸»é¢˜ */
.card-theme-display {
  --card-header-bg: linear-gradient(90deg, #ecf5ff, #f0f9ff);
  --card-header-color: #409EFF;
}
.card-theme-network {
  --card-header-bg: linear-gradient(90deg, #f0f9eb, #f9fbe7);
  --card-header-color: #67C23A;
}
.card-theme-audio {
  --card-header-bg: linear-gradient(90deg, #fef0f0, #fef6f6);
  --card-header-color: #F56C6C;
}
.card-theme-system {
  --card-header-bg: linear-gradient(90deg, #f4f4f5, #f9f9fa);
  --card-header-color: #909399;
}
.card-theme-security {
  --card-header-bg: linear-gradient(90deg, #fdf6ec, #fefcef);
  --card-header-color: #E6A23C;
}
.card-theme-display .config-card__header {
  background: var(--card-header-bg);
  color: var(--card-header-color);
}
.card-theme-network .config-card__header {
  background: var(--card-header-bg);
  color: var(--card-header-color);
}
.card-theme-audio .config-card__header {
  background: var(--card-header-bg);
  color: var(--card-header-color);
}
.card-theme-system .config-card__header {
  background: var(--card-header-bg);
  color: var(--card-header-color);
}
.card-theme-security .config-card__header {
  background: var(--card-header-bg);
  color: var(--card-header-color);
}
.card-zhanwei {
  height: 0;
  border: none;
}
/deep/.el-form-item__label {
  text-align: center;
  display: flex;
  justify-content: center;
  align-items: center;
}
/deep/.el-form-item__content>.el-input {
  width: 60%;
}
.password-strength-container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 2px;
  width: 400px;
}
.password-strength-bar {
  width: 90%;
  height: 6px;
  background-color: #f0f0f0;
  border-radius: 3px;
  overflow: hidden;
  margin-bottom: 4px;
}
.password-strength-progress {
  height: 100%;
  transition: all 0.3s ease;
  width: 0%;
}
/* ä¿®æ­£é€‰æ‹©å™¨ - ç›´æŽ¥åº”用于进度条元素 */
.password-strength-progress.strength-weak {
  width: 25%;
  background-color: #ff4d4f;
}
.password-strength-progress.strength-medium {
  width: 50%;
  background-color: #faad14;
}
.password-strength-progress.strength-good {
  width: 75%;
  background-color: #1890ff;
}
.password-strength-progress.strength-strong {
  width: 100%;
  background-color: #52c41a;
}
.password-strength-text {
  line-height: 20px;
}
/* ä¿®æ­£é€‰æ‹©å™¨ - ç›´æŽ¥åº”用于文本元素 */
.password-strength-text.strength-weak {
  color: #ff4d4f;
}
.password-strength-text.strength-medium {
  color: #faad14;
}
.password-strength-text.strength-good {
  color: #1890ff;
}
.password-strength-text.strength-strong {
  color: #52c41a;
}
.password-weak-warning {
  font-size: 12px;
  color: #faad14;
  margin-top: 4px;
  display: flex;
  align-items: center;
}
.password-weak-warning i {
  margin-right: 4px;
}
.password-rules-tip {
  margin-top: 20px;
  padding: 12px 16px;
  background: #f8f9fa;
  border: 1px solid #e9ecef;
  border-radius: 8px;
  font-size: 12px;
  line-height: 1.5;
  color: #495057;
}
.rules-title {
  font-weight: 600;
  color: #343a40;
  margin-bottom: 8px;
  font-size: 13px;
}
.rules-list {
  margin: 0;
  padding-left: 16px;
  list-style: none;
}
.rules-list li {
  margin-bottom: 4px;
  position: relative;
}
.rules-list li:before {
  content: "·";
  position: absolute;
  left: -12px;
  color: #6c757d;
}
.weak-passwords {
  color: #dc3545;
  font-style: italic;
  margin-top: 4px;
  line-height: 1.4;
  background: rgba(220, 53, 69, 0.05);
  padding: 6px 8px;
  border-radius: 4px;
  border-left: 3px solid #dc3545;
}
/* äº‘证激活页面样式 */
.cloud-card-header {
  display: flex;
  align-items: center;
  position: relative;
}
.cloud-icon {
  color: #409EFF;
  font-size: 18px;
}
.cloud-status {
  margin-left: auto;
  padding: 4px 12px;
  border-radius: 12px;
  font-size: 12px;
  font-weight: 500;
}
.cloud-status.active {
  background: #f0f9ff;
  color: #1890ff;
  border: 1px solid #91d5ff;
}
.cloud-status.inactive {
  background: #fff2f0;
  color: #ff4d4f;
  border: 1px solid #ffccc7;
}
.activation-container {
  max-width: 600px;
  margin: 0 auto;
}
.activation-form-item {
  margin-bottom: 24px;
}
.activation-form-item .el-form-item__label {
  font-weight: 600;
  color: #303133;
  font-size: 14px;
  margin-bottom: 8px;
  padding: 0;
}
.activation-input {
  width: 100% !important;
}
/deep/.activation-input .el-textarea__inner {
  height: 100px !important;
  border-radius: 8px;
  border: 2px solid #e6e8eb;
  font-size: 14px;
  transition: all 0.3s ease;
  padding-left: 10px;
}
.activation-input .el-textarea__inner:focus {
  border-color: #409EFF;
  box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
}
.activation-input .el-input__prefix {
  display: flex;
  align-items: center;
  left: 12px;
  color: #909399;
}
.activation-tips {
  margin-top: 12px;
  padding: 16px;
  background: #f8f9fa;
  border-radius: 8px;
  border-left: 4px solid #409EFF;
}
.tip-item {
  display: flex;
  align-items: flex-start;
  margin-bottom: 8px;
  font-size: 13px;
  color: #606266;
  line-height: 1.4;
}
.tip-item:last-child {
  margin-bottom: 0;
}
.tip-item i {
  margin-right: 8px;
  margin-top: 2px;
  color: #409EFF;
  font-size: 14px;
}
.activation-action-section {
  text-align: center;
  padding: 24px 0;
  border-top: 1px solid #f0f0f0;
  border-bottom: 1px solid #f0f0f0;
  margin: 20px 0;
}
.background-upload-section {
  padding: 20px 0;
}
.upload-area {
  margin-bottom: 20px;
}
.background-upload {
  display: inline-block;
}
.file-info {
  margin-top: 12px;
  padding: 8px 12px;
  background: #f5f7fa;
  border-radius: 6px;
  display: flex;
  align-items: center;
  gap: 8px;
  color: #606266;
  font-size: 13px;
}
.file-info i {
  color: #409EFF;
}
.upload-actions {
  margin-bottom: 16px;
}
.upload-tips {
  padding: 12px;
  background: #f0f9ff;
  border-radius: 6px;
  border-left: 3px solid #409EFF;
}
.upload-tips p {
  margin: 0;
  color: #606266;
  font-size: 13px;
  line-height: 1.5;
}
.activation-btn {
  height: 44px;
  padding: 0 32px;
  font-size: 14px;
  font-weight: 600;
  border-radius: 8px;
  background: linear-gradient(135deg, #409EFF, #66b1ff);
  border: none;
  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3);
  transition: all 0.3s ease;
}
.activation-btn:hover:not(.is-disabled) {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(64, 158, 255, 0.4);
}
.activation-btn:active:not(.is-disabled) {
  transform: translateY(0);
}
.action-helpers {
  margin-top: 16px;
  display: flex;
  justify-content: center;
  gap: 16px;
}
.helper-btn {
  color: #606266;
  font-size: 13px;
  transition: color 0.3s ease;
}
.helper-btn:hover {
  color: #409EFF;
}
.activation-result {
  padding: 16px;
  border-radius: 8px;
  margin-top: 20px;
  display: flex;
  align-items: center;
  flex-direction: column;
  text-align: center;
  animation: fadeIn 0.5s ease;
}
.activation-result.success {
  background: #f6ffed;
  border: 1px solid #b7eb8f;
  color: #52c41a;
}
.activation-result.error {
  background: #fff2f0;
  border: 1px solid #ffccc7;
  color: #ff4d4f;
}
.activation-result i {
  font-size: 24px;
  margin-bottom: 8px;
}
.result-details {
  margin-top: 8px;
  font-size: 12px;
  color: #8c8c8c;
  line-height: 1.4;
}
.timezone-selects {
  display: flex;
  gap: 12px;
}
.timezone-selects .el-select {
  flex: 1;
}
@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
/* å“åº”式设计 */
@media (max-width: 768px) {
  .activation-container {
    max-width: 100%;
  }
  .action-helpers {
    flex-direction: column;
    gap: 8px;
  }
  .activation-btn {
    width: 100%;
    max-width: 280px;
  }
}
</style>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/control/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,982 @@
<template>
  <div id="device-control-panel">
    <!-- æ ‡ç­¾é¡µ -->
    <el-tabs v-model="activeTab" type="border-card" class="main-tabs" @tab-click="handleClick">
      <!-- è¿œç¨‹æŽ§åˆ¶æ ‡ç­¾ -->
      <el-tab-pane :label="$t('control.remoteControl')" name="remote">
        <div class="remote-control">
          <el-row :gutter="20">
            <!-- é‡å¯è®¾å¤‡ -->
            <el-col :span="8">
              <div class="control-card restart-card" @click="handleRestart" :class="{ 'loading': loading.restart }">
                <div class="card-content">
                  <div class="card-icon">
                    <i class="el-icon-refresh"></i>
                  </div>
                  <h3>{{ $t('control.restart') }}</h3>
                  <p class="card-description">{{ $t('control.restartTips') }}</p>
                  <div class="card-hover-effect">
                    <span>{{ $t('control.clickToRestart') }}</span>
                  </div>
                  <div v-if="loading.restart" class="loading-overlay">
                    <i class="fas fa-spinner"></i>
                    <div class="loading-text">{{ $t('control.restarting') }}</div>
                  </div>
                </div>
              </div>
            </el-col>
            <!-- è¿œç¨‹å¼€é—¨ -->
            <el-col :span="8">
              <div class="control-card open-card" @click="handleRemoteOpen" :class="{ 'loading': loading.remoteOpen }">
                <div class="card-content">
                  <div class="card-icon">
                    <i class="el-icon-unlock"></i>
                  </div>
                  <h3>{{ $t('control.remoteOpen') }}</h3>
                  <p class="card-description">{{ $t('control.remoteTips') }}</p>
                  <div class="card-hover-effect">
                    <span>{{ $t('control.clickToOpen') }}</span>
                  </div>
                  <div v-if="loading.remoteOpen" class="loading-overlay">
                    <i class="fas fa-spinner"></i>
                    <div class="loading-text">{{ $t('control.opening') }}</div>
                  </div>
                </div>
              </div>
            </el-col>
            <!-- è®¾å¤‡é‡ç½® -->
            <el-col :span="8">
              <div class="control-card reset-card" @click="handleReset" :class="{ 'loading': loading.reset }">
                <div class="card-content">
                  <div class="card-icon">
                    <i class="el-icon-delete"></i>
                  </div>
                  <h3>{{ $t('control.reset') }}</h3>
                  <p class="card-description">{{ $t('control.resetTips') }}</p>
                  <div class="card-hover-effect">
                    <span>{{ $t('control.clickToReset') }}</span>
                  </div>
                  <div v-if="loading.reset" class="loading-overlay">
                    <i class="fas fa-spinner"></i>
                    <div class="loading-text">{{ $t('control.reseting') }}</div>
                  </div>
                </div>
              </div>
            </el-col>
          </el-row>
        </div>
      </el-tab-pane>
      <!-- è®¾å¤‡å‡çº§æ ‡ç­¾ -->
      <el-tab-pane :label="$t('control.firmwareUpgrade')" name="upgrade">
        <div class="firmware-upgrade">
          <el-row :gutter="30">
            <el-col :span="12">
              <el-card class="upgrade-card" shadow="never">
                <h3><i class="fas fa-cloud-download-alt"></i>{{ $t('control.urlUpgrade') }}</h3>
                <el-form ref="upgradeForm" :model="upgradeForm" :rules="upgradeRules" label-position="top"
                  class="upgrade-form">
                  <el-form-item :label="$t('control.firmwareUrl')" prop="url">
                    <el-input v-model="upgradeForm.url" placeholder="https://example.com/firmware.dpk" clearable>
                      <i slot="prefix" class="el-input__icon el-icon-link"></i>
                    </el-input>
                  </el-form-item>
                  <el-form-item :label="$t('control.md5Checksum')" prop="md5">
                    <el-input v-model="upgradeForm.md5" :placeholder="$t('common.placeholder')" clearable>
                      <i slot="prefix" class="el-input__icon el-icon-key"></i>
                    </el-input>
                  </el-form-item>
                  <el-form-item>
                    <el-button type="primary" @click="submitUpgrade" :loading="loading.upgrade" style="width: 100%">
                      {{ $t('control.startUpgrade') }}
                    </el-button>
                  </el-form-item>
                </el-form>
              </el-card>
            </el-col>
            <el-col :span="12">
              <el-card class="upgrade-card" shadow="never">
                <h3><i class="fas fa-info-circle"></i> {{ $t('control.fileUpgrade') }}</h3>
                <div class="upload-area" @click="triggerFileUpload">
                  <div class="upload-icon">
                    <i class="el-icon-upload"></i>
                  </div>
                  <div class="upload-text">{{ $t('control.uploadFile') }}</div>
                  <div class="upload-hint">{{ $t('control.formatFile') }}</div>
                  <input type="file" ref="fileInput" style="display: none" accept=".zip,.dpk"
                    @change="handleFileUpload">
                </div>
                <el-form>
                  <el-form-item :label="$t('control.md5Checksum')">
                    <el-input v-model="fileMd5" :placeholder="$t('common.placeholder')" clearable>
                      <i slot="prefix" class="el-input__icon el-icon-key"></i>
                    </el-input>
                  </el-form-item>
                </el-form>
                <div v-if="uploadedFile" class="file-info">
                  <div class="file-info-item">
                    <div class="file-label">{{ $t('control.fileName') }}:</div>
                    <div class="file-value">{{ uploadedFile.name }}</div>
                  </div>
                  <div class="file-info-item">
                    <div class="file-label">{{ $t('control.size') }}:</div>
                    <div class="file-value">{{ formatFileSize(uploadedFile.size) }}</div>
                  </div>
                  <!-- <div class="file-info-item">
                    <div class="file-label">类型:</div>
                    <div class="file-value">{{ uploadedFile.type || '未知类型' }}</div>
                  </div>
                  <div class="file-info-item">
                    <div class="file-label">MD5:</div>
                    <div class="file-value">{{ fileMd5 || '计算中...' }}</div>
                  </div>
                  <div class="file-info-item">
                    <div class="file-label">URL:</div>
                    <div class="file-value">{{ fileUrl || '待生成...' }}</div>
                  </div> -->
                </div>
                <div v-if="uploadedFile" class="upload-actions">
                  <el-button @click="clearUpload">{{ $t('control.clearFile') }}</el-button>
                  <el-button type="primary" @click="submitFileUpgrade" :loading="loading.fileUpgrade">
                    {{ loading.fileUpgrade ? $t('control.uploading') : $t('control.uploadAndUpgrade') }}
                  </el-button>
                </div>
              </el-card>
            </el-col>
          </el-row>
        </div>
      </el-tab-pane>
    </el-tabs>
    <!-- é‡ç½®ç¡®è®¤å¯¹è¯æ¡† -->
    <el-dialog :title="$t('control.resetConfirm')" :visible.sync="resetDialogVisible" width="30%"
      :close-on-click-modal="false" custom-class="reset-dialog">
      <div class="reset-warning">
        <el-alert :title="$t('control.resetWarningTitle')" type="warning" :description="$t('control.resetWarningDesc')"
          show-icon>
        </el-alert>
        <el-form ref="resetForm" :model="resetForm" :rules="resetRules" style="margin-top: 20px">
          <el-form-item :label="$t('control.resetPassword')" prop="password">
            <el-input v-model="resetForm.password" type="password" show-password
              :placeholder="$t('control.passwordPlaceholder')">
            </el-input>
          </el-form-item>
        </el-form>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="resetDialogVisible = false">{{ $t('basicSetting.cancelButton') }}</el-button>
        <el-button type="danger" @click="confirmReset" :loading="loading.reset">{{ $t('control.confirmResetBtn')
          }}</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
import { resetObjectValues, throttle } from '@/utils/index.js'
export default {
  name: 'Control',
  data () {
    return {
      activeTab: 'remote',
      // è¡¨å•数据
      upgradeForm: {
        type: 0,
        url: '',
        md5: ''
      },
      fileMd5: '',
      resetForm: {
        password: ''
      },
      // å¯¹è¯æ¡†çŠ¶æ€
      resetDialogVisible: false,
      // åŠ è½½çŠ¶æ€
      loading: {
        restart: false,
        remoteOpen: false,
        upgrade: false,
        reset: false,
        fileUpgrade: false,
      },
      // è¡¨å•验证规则
      upgradeRules: {
        url: [
          { required: true, message: this.$t('control.urlRequired'), trigger: 'blur' },
          { type: 'url', message: this.$t('control.urlInvalid'), trigger: 'blur' }
        ],
        md5: [
          { required: true, message: this.$t('control.md5Required'), trigger: 'blur' },
          { pattern: /^[a-fA-F0-9]{32}$/, message: this.$t('control.md5Invalid'), trigger: 'blur' }
        ],
        password: [
          { required: true, message: this.$t('control.passwordRequired'), trigger: 'blur' }
        ]
      },
      resetRules: {
        password: [
          { required: true, message: this.$t('control.passwordRequired'), trigger: 'blur' }
        ]
      },
      uploadedFile: ''
    }
  },
  methods: {
    // å¤„理重启
    handleRestart () {
      this.$confirm(
        this.$t('control.restartConfirm'),
        this.$t('control.restart'),
        {
          confirmButtonText: this.$t('common.confirm'),
          cancelButtonText: this.$t('common.cancel'),
          type: 'warning'
        }
      ).then(async () => {
        this.loading.restart = true;
        const res = await this.$http.post("/control", { data: { command: 0 } });
        if (res.code == 200) {
          this.loading.restart = false;
          this.$message.success(this.$t('control.restartSuccess'));
        } else {
          this.loading.restart = false;
          this.$message.success(this.$t('control.restartFailed'));
        }
      }).catch(() => {
        // å–消操作
        this.loading.restart = false;
      });
    },
    // å¤„理远程开门
    handleRemoteOpen () {
      this.$confirm(
        this.$t('control.openConfirm'),
        this.$t('control.remoteOpen'),
        {
          confirmButtonText: this.$t('common.confirm'),
          cancelButtonText: this.$t('common.cancel'),
          type: 'warning'
        }
      ).then(async () => {
        this.loading.remoteOpen = true;
        const res = await this.$http.post("/control", { data: { command: 1 } });
        if (res.code == 200) {
          this.loading.remoteOpen = false;
          this.$message.success(this.$t('control.remoteOpenSuccess'));
        } else {
          this.loading.remoteOpen = false;
          this.$message.success(this.$t('control.remoteOpenFailed'));
        }
      }).catch(() => {
        // å–消操作
        this.loading.remoteOpen = false
      });
    },
    // å¤„理设备重置
    handleReset () {
      let that = this
      this.$confirm(
        this.$t('control.resetConfirm') + this.$t('control.resetWillOut'),
        this.$t('control.reset'),
        {
          confirmButtonText: this.$t('common.confirm'),
          cancelButtonText: this.$t('common.cancel'),
          type: 'warning'
        }
      ).then(async () => {
        this.loading.reset = true;
        const res = await this.$http.post("/control", { data: { command: 4 } });
        if (res.code == 200) {
          this.loading.reset = false;
          this.$message.success(this.$t('control.resetSuccess'));
          setTimeout(() => {
            that.$router.push('/login')
            sessionStorage.removeItem("token")
            sessionStorage.removeItem("publicConfig")
          }, 1000)
        } else {
          this.loading.reset = false;
          this.$message.success(this.$t('control.resetFailed'));
        }
      }).catch(() => {
        // å–消操作
        this.loading.reset = false;
      });
    },
    handleClick () {
      if (this.$refs.upgradeForm) {
        this.$refs.upgradeForm.clearValidate();
      }
    },
    // æäº¤å‡çº§
    submitUpgrade: throttle(function () {
      let that = this
      this.$refs.upgradeForm.validate((valid) => {
        if (valid) {
          this.$confirm(
            this.$t('control.upgradeConfirm'),
            this.$t('control.firmwareUpgrade'),
            {
              confirmButtonText: this.$t('common.confirm'),
              cancelButtonText: this.$t('common.cancel'),
              type: 'primary'
            }
          ).then(async () => {
            that.loading.upgrade = true;
            const res = await that.$http.post("/upgradeFirmware", { data: that.upgradeForm });
            if (res.code == 200) {
              that.loading.upgrade = false;
              that.$message.success(that.$t('control.upgradeSuccess'));
              resetObjectValues(that.upgradeForm)
            } else {
              that.$message.error(res.message)
            }
          }).catch(() => {
            // å–消操作
            that.loading.upgrade = false;
          });
        }
      });
    }, 2000),
    confirmReset () { },
    triggerFileUpload () {
      this.$refs.fileInput.click();
    },
    handleFileUpload (event) {
      const file = event.target.files[0];
      if (!file) return;
      // æ£€æŸ¥æ–‡ä»¶ç±»åž‹
      const validTypes = ['.zip', '.dpk'];
      const fileExtension = '.' + file.name.split('.').pop().toLowerCase();
      if (!validTypes.includes(fileExtension)) {
        this.$message.error(this.$t('control.formatFile'));
        return;
      }
      // æ£€æŸ¥æ–‡ä»¶å¤§å° (500MB)
      if (file.size > 20 * 1024 * 1024) {
        this.$message.error(this.$t('control.formatFile'));
        return;
      }
      this.uploadedFile = file;
    },
    formatFileSize (bytes) {
      if (bytes === 0) return '0 Bytes';
      const k = 1024;
      const sizes = ['Bytes', 'KB', 'MB', 'GB'];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    },
    clearUpload () {
      this.uploadedFile = null;
      this.fileMd5 = '';
      this.fileUrl = '';
      this.$refs.fileInput.value = '';
    },
    async submitFileUpgrade () {
      if (!this.uploadedFile) {
        this.$message.warning('请先选择要上传的文件');
        return;
      }
      if (!this.fileMd5) {
        this.$message.warning(this.$t('control.md5Required'));
        return;
      }
      this.loading.fileUpgrade = true;
      let formData = new FormData()
      // æ·»åŠ æ–‡ä»¶
      formData.append('file', this.uploadedFile)
      formData.append('md5', this.fileMd5)
      const res = await this.$http.post("/upload", formData);
      if (res.code == 200) {
        this.loading.fileUpgrade = false;
        this.$message.success(this.$t('control.upgradeSuccess'));
        this.uploadedFile = null
        this.fileMd5 = ''
      } else {
        this.$message.error(res.message)
        this.loading.fileUpgrade = false;
      }
    }
  }
}
</script>
<style scoped>
#device-control-panel {
  padding: 20px;
  max-width: 100vw;
}
.device-status {
  display: flex;
  align-items: center;
  background: rgba(16, 16, 48, 0.7);
  padding: 10px 20px;
  border-radius: 30px;
  border: 1px solid rgba(24, 144, 255, 0.3);
  box-shadow: 0 0 15px rgba(24, 144, 255, 0.2);
}
.status-indicator {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  margin-right: 10px;
  background: var(--themeColor);
  box-shadow: 0 0 10px var(--themeColor);
  animation: pulse 2s infinite;
}
@keyframes pulse {
  0% {
    box-shadow: 0 0 0 0 rgba(24, 144, 255, 0.7);
  }
  70% {
    box-shadow: 0 0 0 10px rgba(64, 224, 208, 0);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(64, 224, 208, 0);
  }
}
.main-tabs {
  background: rgba(16, 16, 48, 0.7);
  border-radius: 15px;
  border: 1px solid rgba(24, 144, 255, 0.2);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
  overflow: hidden;
}
.main-tabs::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 4px;
  background: linear-gradient(90deg, var(--themeColor), #ff8c00, #ff0080);
}
.el-tabs__header {
  background: rgba(32, 32, 64, 0.5);
  margin: 0;
  border-bottom: 1px solid rgba(24, 144, 255, 0.2);
}
.el-tabs__item {
  color: #a0a0ff;
  font-weight: 500;
  padding: 0 25px;
  height: 50px;
  line-height: 50px;
}
.el-tabs__item.is-active {
  color: var(--themeColor);
  background: rgba(24, 144, 255, 0.1);
}
.el-tabs__active-bar {
  background-color: var(--themeColor);
  height: 3px;
  box-shadow: 0 0 10px var(--themeColor);
}
.el-tabs__content {
  padding: 30px;
}
/* è¿œç¨‹æŽ§åˆ¶å¡ç‰‡æ ·å¼ */
.remote-control {
  padding: 10px 0;
}
.control-card {
  height: 220px;
  border-radius: 16px;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  position: relative;
  overflow: hidden;
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2);
  border: 1px solid transparent;
  background: rgba(32, 32, 64, 0.5);
}
.control-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 4px;
  background: linear-gradient(90deg, var(--themeColor), #ff8c00, #ff0080);
}
.control-card:hover {
  transform: translateY(-8px) scale(1.02);
  box-shadow: 0 15px 35px rgba(0, 0, 0, 0.4);
  border-color: rgba(24, 144, 255, 0.5);
}
.control-card:active {
  transform: translateY(-2px) scale(0.98);
}
.control-card.loading {
  cursor: not-allowed;
  opacity: 0.7;
}
/* å¡ç‰‡èƒŒæ™¯æ¸å˜ */
.restart-card {
  background: linear-gradient(135deg, rgba(255, 214, 102, 0.1) 0%, rgba(255, 169, 64, 0.1) 100%);
}
.open-card {
  background: linear-gradient(135deg, rgba(149, 222, 100, 0.1) 0%, rgba(82, 196, 26, 0.1) 100%);
}
.reset-card {
  background: linear-gradient(135deg, rgba(255, 156, 110, 0.1) 0%, rgba(255, 77, 79, 0.1) 100%);
}
.card-content {
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  position: relative;
  z-index: 2;
  padding: 20px;
}
.card-icon {
  width: 70px;
  height: 70px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 20px;
  background: rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(10px);
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
  border: 1px solid rgba(255, 255, 255, 0.2);
}
.restart-card .card-icon {
  background: linear-gradient(135deg, rgba(255, 214, 102, 0.3) 0%, rgba(255, 169, 64, 0.3) 100%);
  border-color: rgba(255, 214, 102, 0.5);
}
.open-card .card-icon {
  background: linear-gradient(135deg, rgba(149, 222, 100, 0.3) 0%, rgba(82, 196, 26, 0.3) 100%);
  border-color: rgba(149, 222, 100, 0.5);
}
.reset-card .card-icon {
  background: linear-gradient(135deg, rgba(255, 156, 110, 0.3) 0%, rgba(255, 77, 79, 0.3) 100%);
  border-color: rgba(255, 156, 110, 0.5);
}
.card-icon i {
  font-size: 32px;
  color: white;
  text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
}
.control-card h3 {
  color: white;
  font-size: 20px;
  font-weight: 600;
  margin: 0 0 10px;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.card-description {
  color: rgba(255, 255, 255, 0.8);
  font-size: 14px;
  text-align: center;
  line-height: 1.2;
}
.card-hover-effect {
  position: absolute;
  bottom: 5px;
  opacity: 0;
  transform: translateY(20px);
  transition: all 0.3s ease;
}
.control-card:hover .card-hover-effect {
  opacity: 1;
  transform: translateY(0);
}
.card-hover-effect span {
  color: rgba(255, 255, 255, 0.9);
  font-size: 12px;
  font-weight: 500;
  background: rgba(0, 0, 0, 0.3);
  padding: 6px 15px;
  border-radius: 20px;
  backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.2);
}
.loading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.7);
  backdrop-filter: blur(4px);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  z-index: 3;
  border-radius: 16px;
}
.loading-overlay i {
  font-size: 40px;
  color: var(--themeColor);
  margin-bottom: 10px;
  animation: spin 1s linear infinite;
  text-shadow: 0 0 10px var(--themeColor);
}
.loading-text {
  color: #a0a0ff;
  font-size: 14px;
}
@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
/* è®¾å¤‡å‡çº§æ ·å¼ */
.firmware-upgrade {
  padding: 10px 0;
}
.upgrade-card {
  background: rgba(32, 32, 64, 0.5);
  border-radius: 12px;
  border: 1px solid rgba(24, 144, 255, 0.2);
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2);
}
/* .upgrade-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 4px;
  background: linear-gradient(90deg, var(--themeColor), #ff8c00, #ff0080);
  border-radius: 12px 12px 0 0;
} */
.upgrade-card h3 {
  margin: 0 0 25px;
  color: var(--themeColor);
  font-weight: 500;
  font-size: 16px;
  display: flex;
  align-items: center;
}
.upgrade-card h3 i {
  margin-right: 10px;
  font-size: 24px;
}
.upgrade-form {
  margin-top: 10px;
}
.el-form-item__label {
  color: #a0a0ff;
  font-weight: 500;
}
.el-input__inner {
  background: rgba(16, 16, 48, 0.7);
  border: 1px solid rgba(24, 144, 255, 0.3);
  color: #e0e0ff;
  border-radius: 8px;
}
.el-input__inner:focus {
  border-color: var(--themeColor);
  box-shadow: 0 0 0 1px rgba(24, 144, 255, 0.2);
}
.el-input__prefix i {
  color: var(--themeColor);
}
.el-button {
  border-radius: 8px;
  font-weight: 500;
  transition: all 0.3s ease;
}
.el-button--primary {
  background: var(--themeColor);
  border: none;
  box-shadow: 0 4px 15px rgba(24, 144, 255, 0.3);
}
.el-button--primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(24, 144, 255, 0.4);
}
/* é‡ç½®å¯¹è¯æ¡†æ ·å¼ */
.reset-dialog .el-dialog {
  background: rgba(16, 16, 48, 0.9);
  border-radius: 15px;
  border: 1px solid rgba(24, 144, 255, 0.3);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(10px);
}
.reset-dialog .el-dialog__header {
  background: rgba(32, 32, 64, 0.7);
  border-bottom: 1px solid rgba(24, 144, 255, 0.2);
  padding: 15px 20px;
  border-radius: 15px 15px 0 0;
}
.reset-dialog .el-dialog__title {
  color: #ff8c00;
  font-weight: 600;
}
.reset-dialog .el-dialog__body {
  padding: 20px;
}
.reset-warning {
  margin-bottom: 20px;
}
.el-alert {
  background: rgba(255, 154, 110, 0.1);
  border: 1px solid rgba(255, 154, 110, 0.3);
  color: #ff8c00;
}
.el-alert__title {
  color: #ff8c00;
  font-weight: 600;
}
.reset-dialog .el-dialog__footer {
  background: rgba(32, 32, 64, 0.7);
  border-top: 1px solid rgba(24, 144, 255, 0.2);
  padding: 15px 20px;
  border-radius: 0 0 15px 15px;
}
.el-button--danger {
  background: linear-gradient(135deg, #ff6b6b, #ff4757);
  border: none;
  box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3);
}
.el-button--danger:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(255, 107, 107, 0.4);
}
/* æ–‡ä»¶ä¸Šä¼ æ ·å¼ */
.upload-card {
  background: rgba(32, 32, 64, 0.5);
  border-radius: 12px;
  border: 1px solid rgba(64, 224, 208, 0.2);
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2);
  padding: 25px;
}
.upload-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 4px;
  background: linear-gradient(90deg, #ff8c00, #ff0080, var(--themeColor));
  border-radius: 12px 12px 0 0;
}
.upload-area {
  border: 2px dashed rgba(24, 144, 255, 0.4);
  border-radius: 12px;
  padding: 40px 20px;
  text-align: center;
  transition: all 0.3s ease;
  background: rgba(16, 16, 48, 0.3);
  cursor: pointer;
}
.upload-area:hover {
  border-color: var(--themeColor);
  background: rgba(16, 16, 48, 0.5);
}
.upload-icon {
  font-size: 48px;
  color: var(--themeColor);
  margin-bottom: 15px;
  text-shadow: 0 0 10px rgba(24, 144, 255, 0.5);
}
.upload-text {
  color: #a0a0ff;
  margin-bottom: 10px;
}
.upload-hint {
  color: #8080cc;
  font-size: 12px;
  margin-top: 10px;
}
.file-info {
  margin-top: 20px;
  padding: 15px;
  background: rgba(16, 16, 48, 0.7);
  border-radius: 8px;
  border: 1px solid rgba(64, 224, 208, 0.2);
}
.file-info-item {
  display: flex;
  margin-bottom: 8px;
  font-size: 14px;
}
.file-info-item:last-child {
  margin-bottom: 0;
}
.file-label {
  color: #a0a0ff;
  width: 80px;
  flex-shrink: 0;
}
.file-value {
  color: #e0e0ff;
  word-break: break-all;
}
.upload-actions {
  margin-top: 20px;
  display: flex;
  gap: 10px;
}
.upload-actions .el-button {
  flex: 1;
}
/* å“åº”式设计 */
@media (max-width: 768px) {
  #device-control-panel {
    padding: 15px;
  }
  .el-tabs__content {
    padding: 20px 15px;
  }
  .control-card {
    height: 180px;
    margin-bottom: 20px;
  }
  .card-icon {
    width: 60px;
    height: 60px;
  }
  .card-icon i {
    font-size: 28px;
  }
  .control-card h3 {
    font-size: 18px;
  }
}
@media (max-width: 480px) {
  .control-card {
    height: 160px;
  }
  .card-icon {
    width: 50px;
    height: 50px;
    margin-bottom: 15px;
  }
  .card-icon i {
    font-size: 24px;
  }
  .control-card h3 {
    font-size: 16px;
  }
  .card-description {
    font-size: 12px;
  }
}
</style>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/home/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,535 @@
<template>
  <div class="index">
    <el-container direction="vertical">
      <!-- å·¦ä¾§å¯¼èˆªæ  -->
      <el-aside class="modern-sidebar asideL">
        <div class="sidebar-header">
          <div class="system-logo">
            <i class="el-icon-monitor"></i>
            <span class="system-name">{{ $t('aside.systemname') }}</span>
          </div>
        </div>
        <el-menu v-if="num == 1" router unique-opened :default-active="activeIndex" class="modern-menu">
          <el-menu-item class="modern-menu-item" :index="item.path" v-for="item in filteredMenu" :key="item.name"
            @click="changeItem(item)">
            <i :class="getMenuIcon(item.path)" class="menu-icon"></i>
            <span class="menu-title">{{ item.name }}</span>
            <div class="menu-indicator"></div>
          </el-menu-item>
        </el-menu>
        <el-menu v-if="num == 2" router unique-opened :default-active="activeIndex2" class="modern-menu">
          <el-menu-item class="modern-menu-item" :index="item.path" v-for="item in personMenuList" :key="item.name"
            @click="changeItem(item)">
            <i :class="getMenuIcon(item.path)" class="menu-icon"></i>
            <span class="menu-title">{{ item.name }}</span>
            <div class="menu-indicator"></div>
          </el-menu-item>
        </el-menu>
      </el-aside>
      <el-container class="asideR">
        <!-- å¤´éƒ¨å¯¼èˆªæ  -->
        <el-header class="modern-header rightTop">
          <div class="header-left">
            <div class="page-title">
              <span>{{ getCurrentPageTitle() }}</span>
            </div>
          </div>
          <div class="header-right">
            <!-- <div class="lang-box">
              <el-select v-model="language" size="mini" @change="changeLang">
                <el-option :label="$t('common.chinese')" value="zh"></el-option>
                <el-option :label="$t('common.english')" value="en"></el-option>
                <el-option :label="$t('common.spanish')" value="es"></el-option>
                <el-option :label="$t('common.french')" value="fr"></el-option>
                <el-option :label="$t('common.german')" value="de"></el-option>
                <el-option :label="$t('common.russian')" value="ru"></el-option>
                <el-option :label="$t('common.arabic')" value="ar"></el-option>
                <el-option :label="$t('common.portuguese')" value="pt"></el-option>
                <el-option :label="$t('common.korean')" value="ko"></el-option>
              </el-select>
            </div> -->
            <div class="header-actions">
              <el-button class="logout-btn" type="text" @click="logout">
                <i class="el-icon-switch-button"></i>
                {{ $t('aside.quit') }}
              </el-button>
            </div>
          </div>
        </el-header>
        <!-- ä¸»ä½“部分 -->
        <el-main class="modern-main pagemain">
          <router-view></router-view>
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>
<script>
export default {
  name: "Home",
  computed: {
    // è®¡ç®—属性过滤菜单
    filteredMenu() {
      let publicConfig = sessionStorage.getItem('publicConfig')
      let { model } = publicConfig ? JSON.parse(publicConfig) : {}
      return this.systemMenuList.filter(item => {
        // æ£€æŸ¥åž‹å·
        if (item.allowedModels && !item.allowedModels.includes(model)) {
          return false
        }
        return true
      })
    }
  },
  data () {
    return {
      num: '',
      systemMenuList: [
        // {
        //   path: "/monitor",
        //   name: this.$t('aside.deviceMonitoring')
        // },
        {
          path: "/config",
          name: this.$t('aside.basicSetting')
        },
        {
          path: "/control",
          name: this.$t('aside.deviceControl')
        },
        {
          path: "/person",
          name: this.$t('aside.workerSetting')
        },
        {
          path: "/record",
          name: this.$t('aside.recordManagement')
        },
        {
          path: "/security",
          name: this.$t('aside.securityManagement'),
          allowedModels: ['vf105', 'vf114']
        }
      ],
      activeIndex: "/",
      activeIndex2: "/",
      indexx: '/',
      language: "zh"
    };
  },
  // æ£€æµ‹è·¯ç”±å˜åŒ–
  watch: {
    $route () {
      this.setCurrentRoute();
    },
  },
  created () {
    this.setCurrentRoute();
    this.num = 1
  },
  mounted () {
    document.getElementsByTagName('body')[0].style.setProperty('--themeColor', "#1890ff");
  },
  methods: {
    // èŽ·å–èœå•å›¾æ ‡
    getMenuIcon (path) {
      const iconMap = {
        // '/monitor': 'el-icon-video-camera',
        '/config': 'el-icon-setting',
        '/person': 'el-icon-user-solid',
        '/control': 'el-icon-key',
        '/record': 'el-icon-tickets',
        '/security': 'el-icon-connection'
      }
      return iconMap[path] || 'el-icon-menu'
    },
    // èŽ·å–å½“å‰é¡µé¢æ ‡é¢˜
    getCurrentPageTitle () {
      const titleMap = {
        // '/monitor': this.$t('aside.deviceMonitoring'),
        '/config': this.$t('aside.basicSetting'),
        '/person': this.$t('aside.workerSetting'),
        '/control': this.$t('aside.deviceControl'),
        '/record': this.$t('aside.recordManagement'),
        '/security': this.$t('aside.securityManagement'),
      }
      return titleMap[this.$route.path] || this.$t('aside.basicSetting')
    },
    changeItem (item) {
      this.indexx = item.path
      this.activeIndex = item.path
      console.log(this.indexx);
    },
    setCurrentRoute () {
      this.activeIndex = this.$route.path; // é€šè¿‡ä»–就可以监听到当前路由状态并激活当前菜单
      this.activeIndex2 = this.$route.path;
      this.indexx = this.$route.path;
    },
    // é€€å‡ºç™»å½•
    logout () {
      const that = this
      that.$confirm(this.$t('aside.tips_msg'), this.$t('aside.tips'), {
        type: "warning"
      }).then(async () => {
        that.toDoLogout()
      }).catch(() => {
        return false
      })
    },
    toDoLogout () {
      this.$router.push('/login')
      sessionStorage.removeItem("token")
      sessionStorage.removeItem("publicConfig")
    },
    changeLang () {
      this.$i18n.locale = this.language
      sessionStorage.setItem('language', this.language)
      this.systemMenuList = [
        // {
        //   path: "/monitor",
        //   name: this.$t('aside.deviceMonitoring')
        // },
        {
          path: "/config",
          name: this.$t('aside.basicSetting')
        },
        {
          path: "/control",
          name: this.$t('aside.deviceControl')
        },
        {
          path: "/person",
          name: this.$t('aside.workerSetting')
        },
        {
          path: "/record",
          name: this.$t('aside.recordManagement')
        },
        {
          path: "/security",
          name: this.$t('aside.securityManagement'),
          allowedModels: ['vf105', 'vf114']
        }
      ]
    }
  },
};
</script>
<style lang="less" scoped>
@import '../../assets/styles/theme.css';
html,
#app,
body {
  height: 100%;
}
.index {
  height: 100vh;
  background: var(--bg-secondary);
}
.el-container {
  height: 100%;
}
/* ä¾§è¾¹æ æ ·å¼ */
.asideL {
  width: 260px !important;
  height: 100%;
  position: fixed;
  left: 0;
  background: linear-gradient(180deg, var(--bg-sidebar) 0%, #0c1426 100%);
  box-shadow: var(--shadow-lg);
  z-index: 1000;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.sidebar-header {
  padding: var(--spacing-lg);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  margin-bottom: var(--spacing-md);
}
.system-logo {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  color: var(--text-white);
  font-size: 18px;
  font-weight: 600;
  i {
    font-size: 24px;
    color: var(--primary-color);
  }
}
.system-name {
  font-size: 16px;
  letter-spacing: 0.5px;
}
/* èœå•样式 */
.modern-menu {
  background: transparent !important;
  border: none !important;
  padding: 0 var(--spacing-md);
}
.modern-menu-item {
  height: 48px !important;
  line-height: 48px !important;
  margin-bottom: var(--spacing-xs) !important;
  border-radius: var(--radius-md) !important;
  color: rgba(255, 255, 255, 0.7) !important;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
  position: relative !important;
  overflow: hidden !important;
  &:hover {
    color: var(--text-white) !important;
    background: rgba(255, 255, 255, 0.1) !important;
    transform: translateX(4px);
  }
  &.is-active {
    color: var(--text-white) !important;
    background: var(--primary-color) !important;
    box-shadow: var(--shadow-md);
    .menu-indicator {
      opacity: 1;
      transform: scaleY(1);
    }
  }
}
.menu-icon {
  font-size: 18px;
  margin-right: var(--spacing-md);
  width: 20px;
  text-align: center;
}
.menu-title {
  font-size: 14px;
  font-weight: 500;
  flex: 1;
}
.menu-indicator {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translate(0, -50%) !important;
  width: 3px;
  height: 20px;
  background: var(--text-white);
  border-radius: 2px;
  opacity: 0;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
/* ä¸»å†…容区域 */
.asideR {
  margin-left: 260px;
  height: 100%;
  display: flex;
  flex-direction: column;
}
/* å¤´éƒ¨æ ·å¼ */
.rightTop {
  height: 64px !important;
  padding: 0 var(--spacing-lg);
  background: var(--bg-header);
  border-bottom: 1px solid var(--border-light);
  box-shadow: var(--shadow-sm);
  display: flex;
  align-items: center;
  justify-content: space-between;
  z-index: 999;
}
.header-left {
  flex: 1;
}
.page-title {
  font-size: 20px;
  font-weight: 600;
  color: var(--text-primary);
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  &::before {
    content: '';
    width: 4px;
    height: 20px;
    background: var(--primary-color);
    border-radius: 2px;
  }
}
.header-right {
  display: flex;
  align-items: center;
}
.header-actions {
  display: flex;
  align-items: center;
  gap: var(--spacing-lg);
}
.lang-selector {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  padding: var(--spacing-sm) var(--spacing-md);
  border-radius: var(--radius-md);
  color: var(--text-secondary);
  cursor: pointer;
  transition: all 0.3s ease;
  &:hover {
    background: var(--bg-tertiary);
    color: var(--text-primary);
  }
}
.user-info {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  padding: var(--spacing-sm) var(--spacing-md);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: all 0.3s ease;
  &:hover {
    background: var(--bg-tertiary);
  }
}
.user-avatar {
  border: 2px solid var(--border-light);
}
.user-name {
  font-size: 14px;
  color: var(--text-primary);
  font-weight: 500;
}
.logout-btn {
  color: var(--error-color) !important;
  font-weight: 500;
  padding: var(--spacing-sm) var(--spacing-md) !important;
  border-radius: var(--radius-md) !important;
  transition: all 0.3s ease !important;
  &:hover {
    background: rgba(255, 77, 79, 0.1) !important;
    color: var(--error-color) !important;
  }
}
/* ä¸»å†…容区域 */
.modern-main {
  flex: 1;
  padding: var(--spacing-xs) !important;
  background: var(--bg-secondary);
  overflow: auto;
}
.pagemain {
  height: calc(100vh - 64px) !important;
}
/* å“åº”式设计 */
@media (max-width: 768px) {
  .asideL {
    width: 240px !important;
    transform: translateX(-100%);
    &.mobile-open {
      transform: translateX(0);
    }
  }
  .asideR {
    margin-left: 0;
  }
  .header-actions {
    gap: var(--spacing-md);
  }
  .user-name {
    display: none;
  }
}
/* åŠ¨ç”»æ•ˆæžœ */
.fade-in-up {
  animation: fadeInUp 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(30px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
/* Element UI ç»„件样式覆盖 */
/deep/ .el-menu {
  border-right: none !important;
}
/deep/ .el-menu-item {
  border-left: none !important;
}
/deep/ .el-dropdown-menu {
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  border: 1px solid var(--border-light);
}
/deep/ .el-dropdown-menu__item {
  padding: var(--spacing-sm) var(--spacing-md);
  transition: all 0.3s ease;
  &:hover {
    background: var(--bg-tertiary);
    color: var(--primary-color);
  }
}
/deep/ .el-tooltip__popper {
  background: var(--bg-dark);
  color: var(--text-white);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}
</style>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/login/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,348 @@
<template>
  <div id="login">
    <!-- è£…饰元素 -->
    <div class="decoration decoration-1"></div>
    <div class="decoration decoration-2"></div>
    <!-- <div class="lang-box">{{ $t('login.lang') }}
      <el-select v-model="language" size="mini" @change="changeLang">
        <el-option :label="$t('common.chinese')" value="zh"></el-option>
        <el-option :label="$t('common.english')" value="en"></el-option>
        <el-option :label="$t('common.spanish')" value="es"></el-option>
        <el-option :label="$t('common.french')" value="fr"></el-option>
        <el-option :label="$t('common.german')" value="de"></el-option>
        <el-option :label="$t('common.russian')" value="ru"></el-option>
        <el-option :label="$t('common.arabic')" value="ar"></el-option>
        <el-option :label="$t('common.portuguese')" value="pt"></el-option>
        <el-option :label="$t('common.korean')" value="ko"></el-option>
      </el-select>
    </div> -->
    <div class="loginMain">
      <div class="loginForm">
        <div class="formTop">{{ $t('login.systemname') }}</div>
        <div class="formMain">
          <el-form :model="loginForm" status-icon :rules="rulesForm" ref="loginForm">
            <!-- å¯†ç   -->
            <el-form-item prop="userPassword" :label="$t('login.pwd')" :label-width="language == 'zh' ? '40px' : '70px'">
              <el-input type="password" v-model="loginForm.userPassword" :placeholder="$t('login.pwd_label')"
                prefix-icon="iconfont icon-mima"></el-input>
            </el-form-item>
            <el-form-item>
              <el-button type="primary" @click="submitForm" class="loginFormBut">
                {{ $t('login.login') }}
              </el-button>
            </el-form-item>
          </el-form>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  data () {
    // å¯†ç 
    var userPassword = (rule, value, callback) => {
      if (value === "") {
        callback(new Error(this.$t('login.pwd_label')));
      } else {
        callback();
      }
    };
    return {
      language: 'zh',
      loginForm: {
        userPassword: "",
      },
      // input验证
      rulesForm: {
        userPassword: [{ validator: userPassword, trigger: "blur" }],
      },
    };
  },
  created () {
    this.getPublicConfig()
  },
  mounted () {
    window.addEventListener('keydown', this.keyDown, false);
  },
  methods: {
    async getPublicConfig() {
      try {
        const res = await this.$http.post('/getPublicConfig', {})
        if (res.code == 200) {
          sessionStorage.setItem("publicConfig", JSON.stringify(res.data))
          let { language} = res.data
          this.language = language
          this.$i18n.locale = language
        } else {
          this.$message.error(res.message)
        }
      } catch (even) {
        console.log(even)
      }
    },
    keyDown (e) {
      //如果是回车则执行登录方法
      if (e.keyCode == 13) {
        e.preventDefault()
        this.submitForm();
      }
    },
    submitForm () {
      this.$refs.loginForm.validate(async (valid) => {
        if (valid) {
          const res = await this.$http.post("/login", this.loginForm);
          if (res.code == 200) {
            this.$message({
              message: this.$t('login.success_msg'),
              type: 'success'
            });
            this.$router.push({ path: "/config" }),
            sessionStorage.setItem("token", res.data.accessToken)
          } else {
            this.$message.error(this.$t('login.error_name'));
            return false
          }
        } else {
          this.$message.error(this.$t('login.error_res'));
          return false
        }
      });
    }
  },
  destroyed () {
    window.removeEventListener('keydown', this.keyDown, false);
  }
};
</script>
<style lang="less" scoped>
#login {
  width: 100%;
  height: 100%;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  background-image: url(../../assets/bg.png);
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
  font-family: 'Helvetica Neue', Arial, sans-serif;
  .lang-box {
    position: absolute;
    top: 30px;
    right: 30px;
    font-size: 14px;
    color: white;
    z-index: 10;
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .loginMain {
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 2;
    width: 100%;
    .loginForm {
      width: 430px;
      background-color: rgba(255, 255, 255, 0.95);
      border-radius: 16px;
      box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2);
      overflow: hidden;
      backdrop-filter: blur(10px);
      border: 1px solid rgba(255, 255, 255, 0.2);
      animation: fadeIn 0.8s ease-out;
      .formTop {
        width: 100%;
        font-size: 32px;
        font-weight: 700;
        text-align: center;
        padding: 40px 0 30px;
        color: #333;
        background: linear-gradient(to right, #667eea, #764ba2);
        letter-spacing: 1px;
      }
      .formMain {
        width: 80%;
        margin: 0 auto;
        padding-bottom: 40px;
        .el-form {
          margin-top: 20px;
          .el-form-item {
            margin-bottom: 28px;
            .el-form-item__label {
              font-weight: 600;
              color: #555;
              font-size: 14px;
              padding-bottom: 8px;
              display: block;
            }
            .el-input {
              width: 100%;
            }
            .el-input__inner {
              height: 50px;
              border-radius: 10px;
              border: 2px solid #e1e5e9;
              font-size: 16px;
              padding-left: 45px;
              transition: all 0.3s ease;
              background-color: #f8f9fa;
            }
            .el-input__inner:focus {
              border-color: #667eea;
              box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
              background-color: white;
            }
            .el-input__prefix {
              left: 15px;
              display: flex;
              align-items: center;
            }
            .iconfont {
              font-size: 20px;
              color: #7b8a9b;
            }
            .el-input__inner:focus+.el-input__prefix .iconfont {
              color: #667eea;
            }
            .loginFormBut {
              width: 100%;
              height: 50px;
              border-radius: 10px;
              font-size: 16px;
              font-weight: 600;
              background: linear-gradient(to right, #667eea, #764ba2);
              color: white;
              border: none;
              transition: all 0.3s ease;
              margin-top: 10px;
              letter-spacing: 1px;
            }
            .loginFormBut:hover {
              transform: translateY(-2px);
              box-shadow: 0 7px 14px rgba(102, 126, 234, 0.3);
            }
            .loginFormBut:active {
              transform: translateY(0);
            }
          }
        }
      }
    }
    @keyframes fadeIn {
      from {
        opacity: 0;
        transform: translateY(20px);
      }
      to {
        opacity: 1;
        transform: translateY(0);
      }
    }
  }
}
#login::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.4);
  z-index: 1;
}
/* å“åº”式设计 */
@media (max-width: 768px) {
  .loginForm {
    width: 90%;
    max-width: 400px;
  }
  .formTop {
    font-size: 28px;
    padding: 30px 0 20px;
  }
  .formMain {
    width: 85%;
  }
  .el-input__inner {
    height: 46px;
    font-size: 15px;
  }
  .loginFormBut {
    height: 46px;
  }
}
@media (max-width: 480px) {
  .loginForm {
    width: 95%;
  }
  .formMain {
    width: 90%;
  }
  .lang-box {
    top: 20px;
    right: 20px;
  }
}
/* æ·»åŠ ä¸€äº›è£…é¥°å…ƒç´  */
.decoration {
  position: absolute;
  z-index: 1;
}
.decoration-1 {
  top: 10%;
  left: 10%;
  width: 80px;
  height: 80px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.1);
}
.decoration-2 {
  bottom: 15%;
  right: 12%;
  width: 120px;
  height: 120px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.05);
}
</style>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/monitor/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,715 @@
<template>
  <div class="container">
    <div class="dashboard">
      <div class="card">
        <div class="card-header">
          <h2 class="card-title">设备概览</h2>
          <i class="fas fa-microchip card-icon"></i>
        </div>
        <div class="stats-grid">
          <div class="stat-item">
            <div class="stat-label">设备状态</div>
            <div class="stat-value">{{ deviceStatus.online ? '在线' : '离线' }}</div>
            <div class="stat-label">{{ deviceStatus.online ? '运行正常' : '设备异常' }}</div>
          </div>
          <div class="stat-item">
            <div class="stat-label">注册人数</div>
            <div class="stat-value">{{ deviceStats.recognitions }}</div>
            <div class="stat-label">人次</div>
          </div>
          <div class="stat-item">
            <div class="stat-label">白名单数</div>
            <div class="stat-value">{{ whitelist.face }} / {{ whitelist.password }} / {{ whitelist.card }}</div>
            <div class="stat-label">人脸/密码/卡片</div>
          </div>
          <div class="stat-item">
            <div class="stat-label">通行记录总数</div>
            <div class="stat-value">{{ systemStatus.memory }}</div>
            <div class="stat-label">条数</div>
          </div>
        </div>
      </div>
      <div class="card">
        <div class="card-header">
          <h2 class="card-title">CPU使用率</h2>
          <i class="fas fa-tachometer-alt card-icon"></i>
        </div>
        <div class="chart-container" id="cpuChart"></div>
      </div>
      <div class="card">
        <div class="card-header">
          <h2 class="card-title">内存使用率</h2>
          <i class="fas fa-memory card-icon"></i>
        </div>
        <div class="chart-container" id="memoryChart"></div>
      </div>
    </div>
    <div class="main-content">
      <div class="card">
        <div class="card-header">
          <h2 class="card-title">设备运行日志</h2>
          <i class="fas fa-clipboard-list card-icon"></i>
        </div>
        <div class="logs-container">
          <div v-for="log in logs" :key="log.id" :class="['log-item', log.type]">
            <div class="log-time">{{ log.time }}</div>
            <div class="log-message">{{ log.message }}</div>
          </div>
        </div>
      </div>
      <div class="card">
        <div class="card-header">
          <h2 class="card-title">设备信息</h2>
          <i class="fas fa-server card-icon"></i>
        </div>
        <div class="device-info-container">
          <div class="device-status">
            <div :class="['status-indicator', deviceStatus.online ? 'status-online pulse' : 'status-offline']"></div>
            <div class="device-details">
              <!-- <div class="device-name">{{ deviceInfo.name }}</div> -->
              <div class="device-id">设备SN: {{ deviceInfo.id }} | IP: {{ deviceInfo.ip }}</div>
            </div>
          </div>
          <div class="info-grid">
            <div class="info-item">
              <div class="info-label">设备型号</div>
              <div class="info-value">{{ deviceInfo.model }}</div>
            </div>
            <div class="info-item">
              <div class="info-label">固件版本</div>
              <div class="info-value">{{ deviceInfo.firmware }}</div>
            </div>
            <div class="info-item">
              <div class="info-label">运行时间</div>
              <div class="info-value">{{ deviceInfo.uptime }}</div>
            </div>
            <div class="info-item">
              <div class="info-label">存储空间</div>
              <div class="info-value">{{ deviceInfo.storage }}</div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import * as echarts from "echarts";
export default {
  data () {
    return {
      currentTime: '',
      deviceStatus: {
        online: true
      },
      userCount: 888,
      whitelist: {
        face: 99,
        password: 88,
        card: 66
      },
      deviceStats: {
        recognitions: 1247
      },
      systemStatus: {
        cpu: 45,
        memory: 62
      },
      deviceInfo: {
        name: '人脸识别终端-A01',
        id: 'FRD-2023-A01',
        ip: '192.168.1.101',
        model: 'FRD-X3000',
        firmware: 'v2.5.3',
        uptime: '15天 8小时 32分钟',
        storage: '78% (128GB/164GB)',
        network: '稳定',
        lastMaintenance: '2023-10-28'
      },
      logs: [
        { id: 1, time: '2023-11-15 14:23:45', message: '人脸识别成功 - ç”¨æˆ·: å¼ ä¸‰', type: 'normal' },
        { id: 2, time: '2023-11-15 14:22:30', message: 'CPU使用率超过80%', type: 'warning' },
        { id: 3, time: '2023-11-15 14:21:15', message: '网络连接短暂中断,已恢复', type: 'error' },
        { id: 4, time: '2023-11-15 14:20:05', message: '系统重启完成', type: 'normal' },
        { id: 5, time: '2023-11-15 14:19:50', message: '内存使用率超过阈值', type: 'warning' },
        { id: 6, time: '2023-11-15 14:18:30', message: '识别引擎更新完成', type: 'normal' },
        { id: 7, time: '2023-11-15 14:17:15', message: '人脸识别成功 - ç”¨æˆ·: æŽå››', type: 'normal' },
        { id: 8, time: '2023-11-15 14:16:20', message: '数据库备份完成', type: 'normal' }
      ],
      cpuChart: null,
      memoryChart: null,
      cpuData: [],
      memoryData: [],
      timeData: []
    }
  },
  mounted () {
    this.updateTime();
    setInterval(this.updateTime, 1000);
    this.initCharts();
    this.simulateDataUpdate();
    // æ¨¡æ‹Ÿå®žæ—¶æ•°æ®æ›´æ–°
    setInterval(this.updateCharts, 2000);
    setInterval(this.addRandomLog, 5000);
    setInterval(this.updateDeviceStats, 3000);
  },
  methods: {
    updateTime () {
      const now = new Date();
      this.currentTime = now.toLocaleString('zh-CN', {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
        hour12: false
      });
    },
    initCharts () {
      // åˆå§‹åŒ–CPU图表
      this.cpuChart = echarts.init(document.getElementById('cpuChart'));
      const cpuOption = {
        tooltip: {
          trigger: 'axis',
          formatter: '{b}<br/>CPU: {c}%'
        },
        grid: {
          top: '15%',
          left: '3%',
          right: '4%',
          bottom: '10%',
          containLabel: true
        },
        xAxis: {
          type: 'category',
          boundaryGap: false,
          data: this.timeData,
          axisLine: {
            lineStyle: {
              color: '#a0a0ff'
            }
          }
        },
        yAxis: {
          type: 'value',
          max: 100,
          axisLine: {
            lineStyle: {
              color: '#a0a0ff'
            }
          },
          splitLine: {
            lineStyle: {
              color: 'rgba(160, 160, 255, 0.1)'
            }
          }
        },
        series: [{
          name: 'CPU使用率',
          type: 'line',
          smooth: true,
          symbol: 'circle',
          symbolSize: 8,
          lineStyle: {
            width: 3,
            color: '#1890ff'
          },
          itemStyle: {
            color: '#1890ff',
            borderColor: '#fff',
            borderWidth: 2
          },
          areaStyle: {
            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
              { offset: 0, color: 'rgba(24, 144, 255, 0.5)' },
              { offset: 1, color: 'rgba(24, 144, 255, 0.1)' }
            ])
          },
          data: this.cpuData
        }]
      };
      this.cpuChart.setOption(cpuOption);
      // åˆå§‹åŒ–内存图表
      this.memoryChart = echarts.init(document.getElementById('memoryChart'));
      const memoryOption = {
        tooltip: {
          formatter: '{a} <br/>{b} : {c}%'
        },
        series: [
          {
            name: '内存使用率',
            type: 'gauge',
            radius: '90%',
            center: ['50%', '60%'],
            progress: {
              show: true,
              width: 20,
              itemStyle: {
                color: {
                  type: 'linear',
                  x: 0,
                  y: 0,
                  x2: 0,
                  y2: 1,
                  colorStops: [{
                    offset: 0, color: '#40e0d0'
                  }, {
                    offset: 1, color: '#ff0080'
                  }]
                }
              }
            },
            axisLine: {
              lineStyle: {
                width: 20,
                color: [
                  [0.3, '#40e0d0'],
                  [0.7, '#ff8c00'],
                  [1, '#ff0080']
                ]
              }
            },
            axisTick: {
              distance: -20,
              length: 8,
              lineStyle: {
                color: '#fff',
                width: 2
              }
            },
            splitLine: {
              distance: -20,
              length: 20,
              lineStyle: {
                color: '#fff',
                width: 3
              }
            },
            axisLabel: {
              distance: -20,
              color: '#fff',
              fontSize: 14
            },
            anchor: {
              show: true,
              size: 15,
              showAbove: true,
              itemStyle: {
                borderWidth: 4,
                borderColor: '#1890ff'
              }
            },
            detail: {
              valueAnimation: true,
              formatter: '{value}%',
              color: '#fff',
              fontSize: 24,
              offsetCenter: [0, '30%']
            },
            title: {
              // å•独设置名称的样式
              show: true,
              offsetCenter: [0, '50%'],  // åç§°ä½ç½® [水平偏移, åž‚直偏移]
              color: '#fff',  // åç§°é¢œè‰²æ”¹ä¸ºç™½è‰²
              fontSize: 12,
              fontWeight: 'bold'
            },
            data: [
              {
                value: this.systemStatus.memory,
                name: '已用/总(M):353/780'
              }
            ]
          }
        ]
      };
      this.memoryChart.setOption(memoryOption);
    },
    simulateDataUpdate () {
      // åˆå§‹åŒ–一些模拟数据 - ä½¿ç”¨çœŸå®žæ—¶é—´
      const now = new Date();
      for (let i = 10; i >= 0; i--) {
        const time = new Date(now.getTime() - i * 2000);
        this.timeData.push(this.formatTime(time));
        this.cpuData.push(Math.floor(Math.random() * 30) + 30);
        this.memoryData.push(Math.floor(Math.random() * 20) + 50);
      }
    },
    updateCharts () {
      // æ›´æ–°CPU图表数据 - ä½¿ç”¨å½“前真实时间
      const now = new Date();
      const currentTime = this.formatTime(now);
      this.timeData.push(currentTime);
      this.timeData.shift();
      const newCpuValue = Math.floor(Math.random() * 30) + 30;
      this.cpuData.push(newCpuValue);
      this.cpuData.shift();
      this.systemStatus.cpu = newCpuValue;
      // æ›´æ–°å†…存图表数据
      const newMemoryValue = Math.floor(Math.random() * 20) + 50;
      this.systemStatus.memory = newMemoryValue;
      this.cpuChart.setOption({
        xAxis: {
          data: this.timeData
        },
        series: [{
          data: this.cpuData
        }]
      });
      this.memoryChart.setOption({
        series: [{
          data: [{
            value: newMemoryValue,
            name: '已用/总(M):353/780'
          }]
        }]
      });
    },
    // æ ¼å¼åŒ–时间为 HH:MM:SS æ ¼å¼
    formatTime (date) {
      const hours = date.getHours().toString().padStart(2, '0');
      const minutes = date.getMinutes().toString().padStart(2, '0');
      const seconds = date.getSeconds().toString().padStart(2, '0');
      return `${hours}:${minutes}:${seconds}`;
    },
    addRandomLog () {
      const logTypes = ['normal', 'warning', 'error'];
      const messages = [
        '人脸识别成功 - ç”¨æˆ·: çŽ‹äº”',
        '设备温度异常',
        '网络连接恢复',
        '存储空间不足警告',
        '识别准确率下降',
        '系统备份完成',
        '安全策略已更新',
        '设备固件升级可用',
        '数据库优化完成',
        '人脸库更新完成'
      ];
      const now = new Date();
      const timeStr = now.getFullYear() + '-' +
        (now.getMonth() + 1).toString().padStart(2, '0') + '-' +
        now.getDate().toString().padStart(2, '0') + ' ' +
        now.getHours().toString().padStart(2, '0') + ':' +
        now.getMinutes().toString().padStart(2, '0') + ':' +
        now.getSeconds().toString().padStart(2, '0');
      const randomType = logTypes[Math.floor(Math.random() * logTypes.length)];
      const randomMessage = messages[Math.floor(Math.random() * messages.length)];
      this.logs.unshift({
        id: this.logs.length + 1,
        time: timeStr,
        message: randomMessage,
        type: randomType
      });
      // ä¿æŒæ—¥å¿—数量不超过15条
      if (this.logs.length > 15) {
        this.logs.pop();
      }
    },
    updateDeviceStats () {
      // éšæœºå¢žåŠ è¯†åˆ«æ¬¡æ•°
      if (Math.random() > 0.7) {
        this.deviceStats.recognitions += Math.floor(Math.random() * 3) + 1;
      }
      // éšæœºæ”¹å˜è®¾å¤‡åœ¨çº¿çŠ¶æ€ï¼ˆå°æ¦‚çŽ‡ï¼‰
      if (Math.random() > 0.95) {
        this.deviceStatus.online = !this.deviceStatus.online;
      }
    },
    beforeDestroy () {
      if (this.cpuChart) {
        this.cpuChart.dispose();
      }
      if (this.memoryChart) {
        this.memoryChart.dispose();
      }
    }
  }
};
</script>
<style lang="less">
.container {
  max-width: 1400px;
  margin: 0 auto;
  padding: 20px;
}
.dashboard {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 25px;
  margin-bottom: 30px;
}
.card {
  background: rgba(16, 16, 48, 0.7);
  border-radius: 15px;
  padding: 25px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(64, 224, 208, 0.2);
  transition: all 0.3s ease;
  position: relative;
  overflow: hidden;
}
.card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 4px;
  background: linear-gradient(90deg, var(--themeColor), #ff8c00, #ff0080);
}
.card:hover {
  transform: translateY(-10px);
  box-shadow: 0 15px 35px rgba(0, 0, 0, 0.4);
  border-color: rgba(24, 144, 255, 0.5);
}
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
}
.card-title {
  font-size: 20px;
  font-weight: 600;
  color: var(--themeColor);
}
.card-icon {
  font-size: 24px;
  color: #ff8c00;
}
.stats-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 15px;
}
.stat-item {
  text-align: center;
  padding: 15px;
  background: rgba(32, 32, 64, 0.5);
  border-radius: 10px;
  transition: all 0.3s ease;
}
.stat-item:hover {
  background: rgba(32, 32, 64, 0.8);
  transform: scale(1.05);
}
.stat-value {
  font-size: 24px;
  font-weight: 700;
  margin: 10px 0;
  color: #ff8c00;
}
.stat-label {
  font-size: 14px;
  color: #a0a0ff;
}
.chart-container {
  height: 300px;
  margin-top: 10px;
}
.main-content {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 25px;
}
.logs-container {
  max-height: 400px;
  overflow-y: auto;
  padding-right: 10px;
}
.logs-container::-webkit-scrollbar {
  width: 8px;
}
.logs-container::-webkit-scrollbar-track {
  background: rgba(32, 32, 64, 0.5);
  border-radius: 10px;
}
.logs-container::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, var(--themeColor), #ff8c00);
  border-radius: 10px;
}
.log-item {
  padding: 15px;
  margin-bottom: 15px;
  background: rgba(32, 32, 64, 0.5);
  border-radius: 10px;
  border-left: 4px solid var(--themeColor);;
  transition: all 0.3s ease;
}
.log-item:hover {
  background: rgba(32, 32, 64, 0.8);
  transform: translateX(5px);
}
.log-item.warning {
  border-left-color: #ff8c00;
}
.log-item.error {
  border-left-color: #ff0080;
}
.log-time {
  font-size: 12px;
  color: #a0a0ff;
  margin-bottom: 5px;
}
.log-message {
  font-size: 14px;
  color: #ffffff;
}
.device-info-container {
  display: flex;
  flex-direction: column;
  gap: 15px;
}
.device-status {
  display: flex;
  align-items: center;
  padding: 15px;
  background: rgba(32, 32, 64, 0.5);
  border-radius: 10px;
  transition: all 0.3s ease;
}
.device-status:hover {
  background: rgba(32, 32, 64, 0.8);
  transform: translateX(5px);
}
.status-indicator {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  margin-right: 15px;
}
.status-online {
  background: var(--themeColor);
  box-shadow: 0 0 10px var(--themeColor);
}
.status-offline {
  background: #ff0080;
  box-shadow: 0 0 10px #ff0080;
}
.device-details {
  flex: 1;
}
.device-name {
  font-weight: 600;
  margin-bottom: 5px;
  font-size: 18px;
}
.device-id {
  font-size: 14px;
  color: #a0a0ff;
}
@media (max-width: 992px) {
  .main-content {
    grid-template-columns: 1fr;
  }
}
@media (max-width: 768px) {
  .dashboard {
    grid-template-columns: 1fr;
  }
  header {
    flex-direction: column;
    gap: 15px;
  }
}
.pulse {
  animation: pulse 2s infinite;
}
@keyframes pulse {
  0% {
    box-shadow: 0 0 0 0 rgba(24, 144, 255, 0.7);
  }
  70% {
    box-shadow: 0 0 0 10px rgba(64, 224, 208, 0);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(64, 224, 208, 0);
  }
}
.glow {
  text-shadow: 0 0 10px currentColor;
}
.info-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 15px;
  margin-top: 15px;
}
.info-item {
  padding: 12px;
  background: rgba(32, 32, 64, 0.5);
  border-radius: 8px;
  font-size: 14px;
}
.info-label {
  color: #a0a0ff;
  margin-bottom: 5px;
}
.info-value {
  font-weight: 600;
  color: #ff8c00;
}
</style>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/person/addOrEdit.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,132 @@
<!-- äººå‘˜æ–°å¢žæˆ–编辑 -->
<template>
  <div>
    <el-drawer size="50%" :with-header="false" :visible.sync="visible" :before-close="handleAddClose">
      <div class="config-tabs">
        <el-menu :default-active="activeTab" mode="horizontal" class="quanxian-menu">
          <el-menu-item index="info" @click="activeTab = 'info'">{{ $t('person.user') }}</el-menu-item>
          <el-menu-item index="vourcher" @click="activeTab = 'vourcher'">{{ $t('person.voucher') }}</el-menu-item>
          <el-menu-item index="permission" @click="activeTab = 'permission'">{{ $t('person.permission') }}</el-menu-item>
        </el-menu>
        <!-- åŠŸèƒ½é¡µé¢ -->
        <el-scrollbar>
          <div style="padding: 20px;">
            <component
              :is="currentComponent"
              :key="componentKey"
              :form-data="form"
              :add-or-edit-type="addOrEditType"
              @close-drawer="handleAddClose"
              @operation-success="operationSuccess"
            />
          </div>
        </el-scrollbar>
      </div>
    </el-drawer>
  </div>
</template>
<script>
import Info from '@/views/person/info'
import Vourcher from '@/views/person/vourcher'
import Permission from '@/views/person/permission'
export default {
  components: { Info, Vourcher, Permission },
  data () {
    return {
      form: {
        userId: "",
        name: "",
        permissionIds: "",
        extra: ""
      },
      addOrEditType: 'add',
      visible: false,
      activeTab: 'info',
      componentKey: 0,  // ç»„件重新渲染的key
      deviceModel: '',
    }
  },
  computed: {
    currentComponent () {
      return this.activeTab;
    }
  },
  created () {
    this.initialForm = JSON.parse(JSON.stringify(this.form));
  },
  mounted () {
  },
  methods: {
    open (type, row) {
      console.log(type, row)
      this.visible = true
      this.addOrEditType = type
      this.activeTab = 'info';
      if (row) {
        let extra = row.extra ? JSON.parse(row.extra) : ""
        this.form = { ...row, extra }
      }
      // å¼ºåˆ¶é‡æ–°æ¸²æŸ“子组件
      this.componentKey += 1;
    },
    handleAddClose (showConfirm = true) {
      const closeAction = () => {
        this.visible = false;
        this.form = { ...this.initialForm };
        // å¼ºåˆ¶é‡æ–°æ¸²æŸ“子组件,确保下次打开是干净的
        this.componentKey += 1;
      };
      if (showConfirm) {
        this.$confirm(this.$t('common.closeTips'))
          .then(closeAction)
          .catch(() => { });
      } else {
        closeAction();
      }
    },
    operationSuccess (param) {
      this.form = {...param}
      this.$emit('addOrEditSuccess')
    },
  }
}
</script>
<style lang='less' scoped>
.config-tabs {
  background: transparent;
}
.config-tabs ::v-deep .el-tabs__header {
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  margin-bottom: 20px;
  border: none;
}
.config-tabs ::v-deep .el-tabs__item {
  font-weight: 500;
  height: 50px;
  line-height: 50px;
}
.drawerHeader {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 50px 10px 30px 10px;
  font-size: 20px;
}
.drawerImg {
  height: 20px;
  margin-right: 20px;
}
// .el-drawer__wrapper {
//   z-index: 3000 !important;
// }</style>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/person/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,254 @@
<template>
  <el-main>
    <el-row>
      <el-form class="el-InputForm" :inline="true" :model="searchForm" ref="ruleForm">
        <el-form-item :label="$t('person.userId')" label-width="60px">
          <el-input v-model="searchForm.userId" :placeholder="$t('person.placeholderUserId')" clearable></el-input>
        </el-form-item>
        <el-form-item :label="$t('person.name')" label-width="80px">
          <el-input v-model="searchForm.name" :placeholder="$t('person.placeholderName')" clearable></el-input>
        </el-form-item>
        <div style="position: absolute; right: 25px; bottom: 25px;">
          <el-button type="info" style="margin-left:10px;border:none;" size='medium' icon="el-icon-search"
            @click="search">{{ $t('common.query') }}</el-button>
          <el-button type="warning" style="border:none;" size='medium' icon="el-icon-refresh-right" @click="doReset">{{
            $t('common.reset') }}</el-button>
        </div>
      </el-form>
      <el-col :span="24" style="margin:20px 0">
        <el-button type="primary" style="border:none;" size='mini' icon="el-icon-plus" @click="doAdd">{{
          $t('person.addUser') }}</el-button>
        <el-button type="warning" style="border:none;" size='mini' icon="el-icon-delete" @click="batchDelete">{{
          $t('common.batchDelete') }}</el-button>
        <el-button type="danger" style="border:none;" size='mini' icon="el-icon-delete-solid" @click="doClear">{{
          $t('person.oneClickClear') }}</el-button>
      </el-col>
      <!-- æ–°å¢žäººå‘˜ -->
      <AddOrEdit ref="addOrEdit" @addOrEditSuccess='fetchData'></AddOrEdit>
      <!-- Table -->
      <el-col :span="24" style="padding: 0">
        <Table :table-label="tableHeader" v-loading="isSubmitLoading" :table-data="tableData"
          :table-option="tableOption" :table-selection="tableSelection"
          @onHandleSelectionChange="handleSelectionChange"></Table>
      </el-col>
      <!-- åˆ†é¡µ -->
      <el-col :span="24" style="text-align: center">
        <Pagination ref="page" :total="total" @pageChange="pageChange"></Pagination>
      </el-col>
    </el-row>
  </el-main>
</template>
<script>
import Table from "@/components/table/tableList";
import Pagination from "@/components/table/pagination";
import AddOrEdit from "./addOrEdit.vue"
import { removeEmptyValues, resetObjectValues } from '@/utils/index.js'
export default {
  components: {
    Pagination,
    Table,
    AddOrEdit
  },
  data () {
    return {
      tableHeader: [
        { label: this.$t('person.userId'), list: 'userId', overflowShow: 'hidden' },
        { label: this.$t('person.name'), list: 'name', overflowShow: 'hidden' },
      ],
      tableSelection: {
        key: true,
        type: "selection",
        detaile: false,
      },
      tableOption: {
        label: this.$t('common.operation'),
        width: "300px",
        value: 0,
        options: [
          {
            label: this.$t('common.edit'),
            key: 0,
            type: "text",
            State: true,
            method: (row) => {
              this.$refs.addOrEdit.open('edit', { ...row })
            },
          },
          {
            label: this.$t('common.delete'),
            key: 1,
            type: "text",
            State: true,
            method: (row) => {
              this.handleDelete(row.userId)
            },
          }
        ]
      },
      tableHeight: 450,
      currentPage: 0,
      pageSize: 20,
      total: 0,
      tableData: [],
      searchForm: {
        userId: '',
        name: '',
      },
      selectIdList: [],
      isSubmitLoading: false,
    };
  },
  created () {
    this.fetchData()
  },
  methods: {
    async fetchData () {
      let searchForm = removeEmptyValues({ ...this.searchForm })
      searchForm.page = this.currentPage
      searchForm.size = this.pageSize
      try {
        const res = await this.$http.post("/getUser", { data: searchForm });
        if (res.code == 200) {
          this.tableData = res.data.content
          this.total = res.data.total
        } else {
          this.$message.error(res.message);
        }
      } catch (even) {
        console.log(even)
      }
    },
    doAdd () {
      this.$refs.addOrEdit.open('add')
    },
    doReset () {
      resetObjectValues(this.searchForm)
      this.fetchData()
    },
    handleDelete (userId) {
      let that = this
      this.$confirm(
        this.$t('common.deleteTips'),
        this.$t('common.tips'),
        {
          confirmButtonText: this.$t('common.confirm'),
          cancelButtonText: this.$t('common.cancel'),
          type: 'warning'
        }
      ).then(async () => {
        try {
          const res = await that.$http.post("delUser", { data: [userId] })
          if (res.code == 200) {
            that.$message.success(this.$t('common.deleteSuccess'))
            that.fetchData()
          } else {
            that.$message.error(res.message)
          }
        } catch (even) {
          console.log(even)
        }
      }).catch(() => { })
    },
    batchDelete () {
      let that = this
      if (this.selectIdList.length <= 0) {
        this.$message.warning(this.$t('common.placeholderSelect'));
        return;
      }
      this.$confirm(this.$t('common.deleteTips'),
        this.$t('common.tips'),
        {
          confirmButtonText: this.$t('common.confirm'),
          cancelButtonText: this.$t('common.cancel'),
          type: 'warning'
        })
        .then(async () => {
          that.isSubmitLoading = true;
          const res = await this.$http.post("delUser", { data: this.selectIdList });
          that.isSubmitLoading = false;
          if (res.code == 200) {
            that.$message.success(this.$t('common.deleteSuccess'))
            that.fetchData()
          } else {
            that.$message.error(res.message)
          }
        })
        .catch(() => {
          return false;
        });
    },
    doClear () {
      let that = this
      this.$confirm(this.$t('person.clearTips'),
        this.$t('common.tips'),
        {
          confirmButtonText: this.$t('common.confirm'),
          cancelButtonText: this.$t('common.cancel'),
          type: 'warning'
        })
        .then(async () => {
          that.isSubmitLoading = true;
          try {
            const res = await this.$http.post("/clearUser", {});
            that.isSubmitLoading = false;
            if (res.code == 200) {
              that.$message.success(this.$t('person.clearSuccess'))
              that.fetchData()
            } else {
              that.$message.error(that.$t('person.clearFailed'))
            }
          } catch (error) {
            console.log(error)
          }
        })
        .catch(() => {
          return false;
        });
    },
    handleSelectionChange (vals) {
      this.selectIdList = [];
      vals.map(v => {
        this.selectIdList.push(v.userId);
      });
    },
    pageChange (item) {
      this.pageSize = item.limit;
      this.currentPage = item.page - 1;
      this.fetchData()
    },
    search () {
      this.currentPage = 0
      this.$refs.page.Page(1);
      this.fetchData();
    },
  },
};
</script>
<style lang="less" scoped>
.el-InputForm {
  position: relative;
  background-color: #fff;
  padding: 10px 20px;
  box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.16);
  border-radius: 15px;
}
.card {
  width: 96%;
  height: 85%;
  border-radius: 5px;
  box-shadow: 0px 10px 30px 10px rgba(211, 215, 221, 0.4);
  padding: 20px;
  margin: 20px auto;
  font-size: 14px;
}
</style>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/person/info.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,140 @@
<!-- äººå‘˜æ–°å¢žæˆ–编辑 -->
<template>
  <div>
    <el-form ref="localForm" :model="localForm" label-width="100px" :rules="rules" style="width: 90%;margin: 0 auto;">
      <el-form-item :label="$t('person.userId')" v-if="addOrEditType == 'edit'">
        <el-input v-model="localForm.userId" :disabled=true :placeholder="$t('common.placeholder')"></el-input>
      </el-form-item>
      <el-form-item :label="$t('person.name')" prop="name">
        <el-input v-model="localForm.name" :placeholder="$t('common.placeholder')"></el-input>
      </el-form-item>
      <el-form-item :label="$t('person.idCard')">
        <el-input v-model="localForm.idCard" :placeholder="$t('common.placeholder')"></el-input>
      </el-form-item>
      <el-form-item :label="$t('person.userType')">
        <el-checkbox v-model="localForm.type">{{ $t('person.administrator') }}</el-checkbox>
      </el-form-item>
    </el-form>
    <div class="dialog-footer">
      <el-button type="warning" @click="handleAddClose()">{{ $t('common.cancel') }}</el-button>
      <el-button type="primary" @click="updata()">{{ $t('common.confirm') }}</el-button>
    </div>
  </div>
</template>
<script>
import { generateRandomString, throttle } from '@/utils/index.js'
export default {
  props: {
    formData: {
      type: Object,
      default: () => ({})
    },
    addOrEditType: {
      type: String,
      default: 'add'
    }
  },
  data () {
    return {
      localForm: {
        userId: "",
        name: "",
        type: false,
        idCard: "",
        permissionIds: ""
      },
      localFormCopy: {},
      rules: {
        name: [{ required: true, message: this.$t('common.placeholder'), trigger: ["blur"] },],
        userId: [{ required: true, message: this.$t('common.placeholder'), trigger: ["blur"] },],
      },
    }
  },
  created () {
    if (this.addOrEditType == 'edit') {
      this.getUser()
    }
    this.localFormCopy = { ...this.localForm }
  },
  mounted () {
  },
  methods: {
    async getUser () {
      try {
        let data = {
          userId: this.formData.userId,
          page: 0,
          size: 99
        }
        const res = await this.$http.post('/getUser', { data })
        if (res.code == 200) {
          console.log(res)
          let { userId, name, extra, permissionIds } = res.data.content[0]
          let { type, idCard } = JSON.parse(extra)
          this.localForm.userId = userId
          this.localForm.name = name
          this.localForm.type = type == 1 ? true : false
          this.localForm.idCard = idCard
          this.localForm.permissionIds = permissionIds
        } else {
          this.$message.error(res.message);
        }
      } catch (even) {
        console.log(even)
      }
    },
    handleAddClose () {
      this.$emit('close-drawer', false);
    },
    updata: throttle(function () {
      let data = {
        name: this.localForm.name,
        extra: {
          type: this.localForm.type ? 1 : 0,
          idCard: this.localForm.idCard
        },
      }
      if (this.localForm.permissionIds) {
        data.permissionIds = this.localForm.permissionIds.split(',')
      }
      this.$refs['localForm'].validate(async (valid) => {
        if (valid) {
          if (this.addOrEditType == 'add') {
            data.userId = generateRandomString()
            const res = await this.$http.post(
              "/insertUser",
              { data: [data] }
            );
            if (res.code == 200) {
              this.$message.success(this.$t('common.addSuccess'))
              this.$emit("operation-success", { ...data, permissionIds: data.permissionIds ? data.permissionIds.join(',') : '' })
            } else {
              if (res.data && res.data[0]) {
                this.$message.error(res.data[0].errmsg)
              }
            }
          } else {
            data.userId = this.localForm.userId
            const res = await this.$http.post(
              "/modifyUser",
              { data: [data] }
            );
            if (res.code == 200) {
              this.$message.success(this.$t('common.editSuccess'))
              this.$emit("operation-success", { ...data, permissionIds: data.permissionIds ? data.permissionIds.join(',') : '' })
            } else {
              if (res.data && res.data[0]) {
                this.$message.error(res.data[0].errmsg)
              }
            }
          }
        }
      })
    }, 2000)
  }
}
</script>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/person/permission.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,702 @@
<!-- æƒé™  -->
<template>
  <div>
    <div style="position: absolute; right: 25px; top: 65px;">
      <!-- <el-button type="danger" style="margin-left:10px;border:none;" size='medium' icon="el-icon-delete">{{
          $t('permission.deletePermission') }}</el-button> -->
      <el-button type="primary" style="border:none;" size='medium' icon="el-icon-plus" @click="openTimeDialog">{{
        $t('permission.addPermission') }}</el-button>
    </div>
    <div style="margin-top: 150px;">
      <Table :table-label="tableHeader" :table-data="tableData" :table-option="tableOption"></Table>
    </div>
    <div class="dialog-footer" style="margin-top: 50px;">
      <el-button type="warning" @click="doClose">{{ $t('common.close') }}</el-button>
    </div>
    <!-- æ·»åŠ æƒé™ -->
    <el-dialog :title="$t('permission.timeRange')" append-to-body modal-append-to-body :visible.sync="timeVisible"
      width="800px" :before-close="handleClose" class="time-range-dialog">
      <el-form label-position="right" style="width:80%;margin:0 auto;">
        <el-form-item>
          <div> {{ $t('permission.effectiveType') }}</div>
          <el-radio v-model="radio" :label="0">{{ $t('permission.unlimitedMode') }}</el-radio>
          <el-radio v-model="radio" :label="1">{{ $t('permission.usualMode') }}</el-radio>
          <el-radio v-model="radio" :label="2">{{ $t('permission.dailyMode') }}</el-radio>
          <el-radio v-model="radio" :label="3">{{ $t('permission.weeklyRepetitionMode') }}</el-radio>
        </el-form-item>
        <el-form-item v-if="radio === 1">
          <div>{{ $t('permission.effectiveTime') }}</div>
          <el-date-picker :clearable="false" v-model="dateTimes" value-format="timestamp"
            :default-time="['00:00:00', '23:59:59']" type="datetimerange" :range-separator="$t('common.to')"
            :start-placeholder="$t('common.startDate')" :end-placeholder="$t('common.endDate')">
          </el-date-picker>
        </el-form-item>
        <!-- <el-form-item v-if="radio === 2">
          <div>{{ $t('permission.effectiveTime') }}</div>
          <el-time-picker :default-value="['00:00', '23:59']" :clearable="false" is-range value-format="HH:mm"
            format="HH:mm" v-model="timeRange" :range-separator="$t('common.to')"
            :start-placeholder="$t('common.startTime')" :end-placeholder="$t('common.endTime')"
            :placeholder="$t('placeholder.placeholder_choose_time_priod')">
          </el-time-picker>
          <div style="margin-top:30px">{{ $t('permission.effectiveWeek') }}</div>
          <el-date-picker :clearable="false" v-model="dateTimes" value-format="timestamp"
            :default-time="['00:00:00', '23:59:59']" type="datetimerange" :range-separator="$t('common.to')"
            :start-placeholder="$t('common.startDate')" :end-placeholder="$t('common.endDate')">
          </el-date-picker>
        </el-form-item> -->
        <el-form-item v-if="radio === 2 || radio == 3">
          <div>{{ $t('permission.effectiveTime') }}</div>
          <el-date-picker style="width:400px;" v-model="dateTimes" value-format="timestamp"
            :default-time="['00:00:00', '23:59:59']" :clearable="false" type="datetimerange"
            :range-separator="$t('common.to')" :start-placeholder="$t('common.startDate')"
            :end-placeholder="$t('common.endDate')">
          </el-date-picker>
          <div style="margin-top:30px">{{ $t('permission.effectiveWeek') }}</div>
          <el-row class="the-week-box" v-if="radio == 2">
            <el-col :span="24">
              <div @click="goSetTime(dayInfo)">
                <el-col :span="3">{{ $t('permission.time_range') }}</el-col>
                <el-col :span="20">{{ dayInfo.timeArr ? dayInfo.timeArr : '-' }}</el-col>
                <el-col :span="1"><img src="../../assets/right.png" alt=""></el-col>
              </div>
            </el-col>
          </el-row>
          <el-row class="the-week-box" v-if="radio == 3">
            <el-col :span="24" v-for="theDay in weeks" :key="theDay.id">
              <div @click="goSetTime(theDay)">
                <el-col :span="3">{{ theDay.label }}</el-col>
                <el-col :span="20">{{ theDay.timeArr ? theDay.timeArr : '-' }}</el-col>
                <el-col :span="1"><img src="../../assets/right.png" alt=""></el-col>
              </div>
            </el-col>
          </el-row>
        </el-form-item>
      </el-form>
      <div class="dialog-footer">
        <el-button @click="handleClose" style="color:#1D2129;">{{ $t('common.cancel') }}</el-button>
        <el-button type="primary" @click="doSubmit">{{ $t('common.confirm') }}</el-button>
      </div>
    </el-dialog>
    <!-- è®¾ç½®æ—¶é—´æ®µ -->
    <el-dialog append-to-body modal-append-to-body :title="title" :visible.sync="timeAreaVisible" width="500px"
      :before-close="cancelTimeAdd">
      <el-form label-position="right" label-width="30px">
        <div>{{ $t('permission.timePeriod') }}
          <el-button type="success" :disabled="timeArr.length >= 5" style="float: right;" size="mini"
            icon="el-icon-s-grid" @click="addTime">{{ $t('permission.addTimePeriod') }}</el-button>
        </div>
        <el-form-item v-for="(item, index) in timeArr" :key="index" :label="index + 1 + ''">
          <el-time-picker :data-num="index" :default-value="['00:00', '23:59']" :clearable="false" :key="index"
            @focus="onTimePicker" @change="theTimeChange" size="mini" style="width:75%;" is-range value-format="HH:mm"
            format="HH:mm" v-model="item.theTime" :range-separator="$t('common.to')"
            :start-placeholder="$t('common.startTime')" :end-placeholder="$t('common.endTime')"
            :placeholder="$t('placeholder.placeholder_choose_time_priod')">
          </el-time-picker>
          <el-button style="margin-left:20px" type="text" icon="el-icon-circle-close"
            @click.prevent="removeTime(index)">{{ $t('common.delete') }}</el-button>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button style="color:#1D2129;" @click="cancelTimeAdd">{{ $t('common.cancel') }}</el-button>
        <el-button type="primary" @click="confirmTimeAdd">{{ $t('common.confirm') }}</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import Table from "@/components/table/tableList";
import { parseTime, generateRandomString } from "@/utils/index.js"
export default {
  props: {
    formData: {
      type: Object,
      default: () => ({})
    },
    addOrEditType: {
      type: String,
      default: 'add'
    }
  },
  components: {
    Table
  },
  data () {
    return {
      localForm: {},
      permissionIds: [],
      curPermissionId: '',
      tableHeader: [
        {
          label: this.$t('permission.permissionId'),
          list: 'permissionId',
          overflowShow: 'hidden',
          width: '150px'
        },
        {
          label: this.$t('permission.effectiveType'),
          type: 'html',
          overflowShow: 'hidden',
          width: '100px',
          code: (row) => {
            let type = row.time.type
            switch (type) {
              case 0:
                return this.$t('permission.unlimitedMode')
              case 1:
                return this.$t('permission.usualMode')
              case 2:
                return this.$t('permission.dailyMode')
              case 3:
                return this.$t('permission.weeklyRepetitionMode')
            }
          }
        },
        {
          label: this.$t('permission.effectiveTime'),
          type: 'html',
          overflowShow: 'hidden',
          code: (row) => {
            let type = row.time.type
            if (type == 0) {
              return '-'
            } else {
              let { beginTime, endTime } = row.time.range
              return `${parseTime(beginTime * 1000)} ~ ${parseTime(endTime * 1000)}`
            }
          }
        },
        {
          label: this.$t('permission.effectiveWeek'),
          type: 'html',
          overflowShow: 'hidden',
          code: (row) => {
            let type = row.time.type
            switch (type) {
              case 0:
                return '-'
              case 1: {
                return '-'
              }
              case 2: {
                let dayPeriodTime = row.time.dayPeriodTime
                let times = dayPeriodTime.split('|')
                if (times.length === 0) return '-';
                times = times.map(v => `<div>${v}<div>`)
                return times.join("");
              }
              case 3: {
                let weekPeriodTime = row.time.weekPeriodTime
                const weekDays = [this.$t('common.monday'), this.$t('common.tuseday'), this.$t('common.wednesday'), this.$t('common.thursday'), this.$t('common.friday'), this.$t('common.saterday'), this.$t('common.sunday')];
                let days = [];
                for (let i = 1; i <= 7; i++) {
                  const dayKey = String(i);
                  if (weekPeriodTime[dayKey]) {
                    days.push(`${weekDays[i - 1]}: ${weekPeriodTime[dayKey]}`);
                  }
                }
                if (days.length === 0) return '-';
                days = days.map(v => `<div>${v}<div>`)
                return days.join("");
              }
              default:
                break;
            }
          }
        },
        // {
        //   label: this.$t('permission.extra'),
        //   type: 'html',
        //   overflowShow: 'hidden',
        //   code: (row) => {
        //     if (row.extra) {
        //       return row.extra
        //     } else {
        //       return '-'
        //     }
        //   }
        // },
      ],
      tableOption: {
        label: this.$t('common.operation'),
        width: "100px",
        value: 0,
        options: [
          {
            label: this.$t('common.edit'),
            key: 1,
            type: "text",
            State: true,
            method: (row) => {
              this.operationType = 'edit'
              this.handleEdit({ ...row })
            },
          },
          {
            label: this.$t('common.delete'),
            key: 1,
            type: "text",
            State: true,
            method: (row) => {
              this.handleDelete(row.permissionId)
            },
          }
        ]
      },
      tableData: [],
      timeRange: ['00:00', '23:59'],
      visible: false,
      timeVisible: false,
      timeAreaVisible: false,
      chooseWeeks: [],
      radio: 0,
      title: "",
      currentId: "",
      dateTimes: [],
      times: [],
      nowTimeVal: [],
      nowTimeIndex: 9,
      count: '',
      timeType: 'day',
      dayInfo: {
        timeArr: '',
      },
      initWeeks: [
        { label: this.$t('common.monday'), id: '1', timeArr: '' },
        { label: this.$t('common.tuseday'), id: '2', timeArr: '' },
        { label: this.$t('common.wednesday'), id: '3', timeArr: '' },
        { label: this.$t('common.thursday'), id: '4', timeArr: '' },
        { label: this.$t('common.friday'), id: '5', timeArr: '' },
        { label: this.$t('common.saterday'), id: '6', timeArr: '' },
        { label: this.$t('common.sunday'), id: '7', timeArr: '' }
      ],
      weeks: [],
      timeArr: [],
      operationType: 'add'
    }
  },
  created () {
    this.weeks = [...this.initWeeks]
    this.getUser()
  },
  mounted () { },
  methods: {
    openTimeDialog () {
      this.timeVisible = true
      this.operationType = 'add'
    },
    handleClose () {
      this.weeks = [...this.initWeeks]
      this.chooseWeeks.splice(0)
      this.radio = 0
      this.dateTimes.splice(0)
      this.times.splice(0)
      this.timeVisible = false
    },
    doClose () {
      this.$emit('close-drawer', false);
    },
    async getUser () {
      try {
        let data = {
          userId: this.formData.userId,
          page: 0,
          size: 99
        }
        const res = await this.$http.post('/getUser', { data })
        if (res.code == 200) {
          console.log(res)
          let { userId, name, extra, permissionIds } = res.data.content[0]
          this.localForm.userId = userId
          this.localForm.name = name
          this.localForm.extra = extra ? JSON.parse(extra) : ""
          this.permissionIds = permissionIds ? permissionIds.split(',') : []
          this.getPermission()
        } else {
          this.$message.error(res.message);
        }
      } catch (even) {
        console.log(even)
      }
    },
    getPermission () {
      let that = this
      this.tableData = []
      if (this.permissionIds) {
        this.permissionIds.forEach(async v => {
          let data = {
            page: 0,
            size: 100,
            permissionId: v
          }
          try {
            const res = await that.$http.post('/getPermission', { data })
            if (res.code == 200) {
              that.tableData = that.tableData.concat(res.data.content)
            } else {
              that.$message.error(res.message)
            }
          } catch (even) {
            console.log(even);
          }
        })
      }
    },
    handleEdit (row) {
      console.log(row)
      this.curPermissionId = row.permissionId
      this.radio = row.time.type
      if (row.time.type != 0) {
        let { beginTime, endTime } = row.time.range
        this.dateTimes = [beginTime * 1000, endTime * 1000]
      }
      if (row.time.type == 2) {
        this.dayInfo.timeArr = row.time.dayPeriodTime
      }
      if (row.time.type == 3) {
        let weekPeriodTime = row.time.weekPeriodTime
        this.weeks = this.initWeeks.map(week => ({
          ...week,
          timeArr: weekPeriodTime[week.id] || ''
        }))
      }
      this.timeVisible = true
    },
    handleDelete (permissionId) {
      let that = this
      this.$confirm(
        this.$t('common.deleteTips'),
        this.$t('common.tips'),
        {
          confirmButtonText: this.$t('common.confirm'),
          cancelButtonText: this.$t('common.cancel'),
          type: 'warning'
        }
      ).then(async () => {
        try {
          const res = await that.$http.post("delPermission", {
            data: {
              permissionIds: [permissionId]
            }
          })
          if (res.code == 200) {
            that.$message.success(this.$t('common.deleteSuccess'))
            that.updatePerson('delete', permissionId)
          } else {
            that.$message.error(res.message)
          }
        } catch (even) {
          console.log(even)
        }
      }).catch(() => { })
    },
    handleAddClose () {
      this.$emit('close-drawer', false);
    },
    async doSubmit () {
      if (!this.formData.userId) {
        this.$message.error(this.$t('person.userNotExist'))
        return false
      }
      const that = this
      let theData = {
        time: {
          type: this.radio
        }
      }
      switch (that.radio) {
        case 1:
          if (!that.dateTimes.length) {
            that.$message.warning(this.$t('permission.choose_time_range'));
            return false
          }
          theData.time.range = {
            beginTime: new Date(that.dateTimes[0]).getTime() / 1000,
            endTime: new Date(that.dateTimes[1]).getTime() / 1000
          }
          console.log(theData);
          break;
        case 2:
          if (that.dateTimes.length === 0) {
            that.$message.warning(this.$t('power.choose_time_range'));
            return false
          }
          theData.time.range = {
            beginTime: new Date(that.dateTimes[0]).getTime() / 1000,
            endTime: new Date(that.dateTimes[1]).getTime() / 1000
          }
          theData.time.dayPeriodTime = that.dayInfo.timeArr
          console.log(theData);
          break
        case 3: {
          if (that.dateTimes.length === 0) {
            that.$message.warning(this.$t('power.choose_time_range'));
            return false
          }
          theData.time.range = {
            beginTime: new Date(that.dateTimes[0]).getTime() / 1000,
            endTime: new Date(that.dateTimes[1]).getTime() / 1000
          }
          let obj = {}
          this.weeks.forEach(item => {
            if (item?.timeArr) {
              obj[item.id] = item.timeArr
            }
          })
          theData.time.weekPeriodTime = obj
          console.log(theData);
          break;
        }
      }
      if (this.operationType == 'add') {
        try {
          theData.permissionId = generateRandomString()
          const res = await this.$http.post('/insertPermission', { data: [theData] })
          if (res.code == 200) {
            this.$message.success(this.$t('common.addSuccess'))
            this.timeVisible = false
            this.updatePerson('add', theData.permissionId)
          } else {
            if (res.data && res.data[0]) {
              this.$message.error(res.data[0].errmsg)
            }
          }
        } catch (even) {
          console.log(even);
        }
      } else if (this.operationType == 'edit') {
        try {
          theData.permissionId = this.curPermissionId
          const res = await this.$http.post('/modifyPermission', { data: [theData] })
          if (res.code == 200) {
            this.$message.success(this.$t('common.editSuccess'))
            this.timeVisible = false
            this.getPermission()
          } else {
            if (res.data && res.data[0]) {
              this.$message.error(res.data[0].errmsg)
            }
          }
        } catch (even) {
          console.log(even);
        }
      }
    },
    async updatePerson (type, permissionId) {
      let data = {}
      data.userId = this.localForm.userId
      data.name = this.localForm.name
      data.extra = this.localForm.extra
      if (type == 'add') {
        data.permissionIds = this.permissionIds && this.permissionIds.length ? this.permissionIds.concat([permissionId]) : [permissionId]
      } else if (type == 'delete') {
        data.permissionIds = this.permissionIds && this.permissionIds.length ? this.permissionIds.filter(v => v != permissionId) : []
      }
      const res = await this.$http.post(
        "/modifyUser",
        { data: [data] }
      );
      if (res.code == 200) {
        this.permissionIds = [...data.permissionIds]
        this.getPermission()
        this.$emit("operation-success", {...data, permissionIds: data.permissionIds ? data.permissionIds.join(',') : ''})
      } else {
        this.$message.error(res.message)
      }
    },
    getSecondsFromMidnight (timeStr) {
      const [hours, minutes] = timeStr.split(':').map(Number);
      return hours * 3600 + minutes * 60;
    },
    // è®¾ç½®æ—¶é—´æ®µ
    goSetTime (theDay) {
      this.timeAreaVisible = true
      this.timeArr.splice(0)
      if (theDay.timeArr !== '') {
        let aaa = theDay.timeArr.split('|')
        aaa.forEach(item => {
          this.timeArr.push({
            theTime: [item.split('-')[0], item.split('-')[1]]
          })
        })
      }
      this.currentId = theDay.id
      this.title = theDay.label
    },
    // æ·»åŠ æ—¶æ®µ
    addTime () {
      if (this.timeArr.length > 0) {
        let lastTime = this.timeArr[this.timeArr.length - 1].theTime
        if (lastTime[0] === '00:00' && lastTime[1] === '23:59') {
          this.$message.warning(this.$t('permission.modify_previous_time'))
          return false
        }
      }
      this.timeArr.push({ theTime: ['00:00', '23:59'] })
    },
    // åˆ é™¤æ—¶æ®µ
    removeTime (index) {
      this.timeArr.splice(index, 1)
    },
    onTimePicker (val) {
      if (val.$attrs['data-num'] !== this.nowTimeIndex) {
        setTimeout(() => {
          this.nowTimeIndex = val.$attrs['data-num']
          this.nowTimeVal = val.value
        }, 300)
      }
    },
    // æ—¶é—´æ®µæ ¡éªŒ
    async theTimeChange (val) {
      const [startTime, endTime] = val;
      // æ ¡éªŒå¼€å§‹æ—¶é—´ä¸èƒ½æ™šäºŽæˆ–等于结束时间
      if (this.toChangeNumber(startTime) >= this.toChangeNumber(endTime)) {
        this.timeArr[this.nowTimeIndex].theTime = this.nowTimeVal;
        this.$message.warning(this.$t('permission.cannot_be_earlier'));
        this.resetTimeSelection();
        return;
      }
      // æ£€æŸ¥æ—¶é—´æ®µæ˜¯å¦ä¸Žå…¶ä»–时间段重叠
      const hasOverlap = this.timeArr.some((item, index) =>
        index !== this.nowTimeIndex && this.isCross(item.theTime, val)
      );
      if (hasOverlap) {
        this.timeArr[this.nowTimeIndex].theTime = this.nowTimeVal;
        this.$message.warning(this.$t('permission.times_cannot_overlap'));
      }
      this.$nextTick(() => {
        this.nowTimeIndex = 9
        this.nowTimeVal = []
      })
    },
    // è½¬æ¢ä¸ºæ•°å­—
    toChangeNumber (str) {
      let theList = str.split(':')
      return theList[0] * 60 + (theList[1] - 0)
    },
    // åˆ¤æ–­æ˜¯å¦äº¤å‰
    isCross (data1, data2) {
      const toMinutes = (str) => {
        const [hours, minutes] = str.split(':').map(Number);
        return hours * 60 + minutes;
      };
      const [start1, end1] = data1.map(toMinutes);
      const [start2, end2] = data2.map(toMinutes);
      // ç›´æŽ¥åˆ¤æ–­é‡å æ¡ä»¶ï¼šä¸¤ä¸ªåŒºé—´æœ‰äº¤é›†
      return start1 < end2 && start2 < end1;
    },
    // å–消添加时段
    cancelTimeAdd () {
      this.timeArr.splice(0)
      this.timeAreaVisible = false
    },
    confirmTimeAdd () {
      let that = this
      if (that.timeArr.length > 1) {
        // æ£€æŸ¥æœ€åŽä¸€ç»„是否与前面任何一组存在交叉
        let lastTime = that.timeArr[that.timeArr.length - 1].theTime
        const hasCrossWithAnyPrevious = that.timeArr
          .slice(0, -1)  // æŽ’除最后一组
          .some(item => that.isCross(item.theTime, lastTime));
        if (hasCrossWithAnyPrevious) {
          that.$message.warning(this.$t('permission.times_cannot_overlap'));
          return false;
        }
      }
      if (this.radio == 2) {
        this.dayInfo.timeArr = this.timeArr.map(item => item.theTime.join('-')).join('|')
      } else {
        let timeLabel = []
        if (that.timeArr.length > 0) {
          that.timeArr.forEach(item => {
            timeLabel.push(item.theTime[0] + '-' + item.theTime[1])
          })
        }
        that.weeks.map(item => {
          if (item.id === that.currentId) {
            item.timeArr = timeLabel.join('|')
          }
        })
      }
      that.cancelTimeAdd()
    },
  }
}
</script>
<style lang='less' scoped>
::v-deep .el-dialog__header {
  text-align: center !important;
}
.dialog-footer {
  text-align: center;
}
.tagStyle {
  cursor: pointer;
}
::v-deep .el-tag--plain,
.el-tag--dark {
  width: 60px;
  height: 40px;
  font-size: 16px;
  text-align: center;
  line-height: 40px;
}
.the-week-box {
  position: relative;
  .el-col-24 {
    padding: 0 10px;
    background-color: #eeeeee80;
    height: 50px;
    line-height: 50px;
    margin-top: 10px;
    overflow: hidden;
    border-radius: 6px;
    cursor: pointer;
    &:hover {
      background-color: #eeeeee;
    }
  }
  img {
    margin-top: 15px;
    width: 20px;
    height: 20px;
  }
}
.weeks:hover {
  background: #f5f7fa;
}
::v-deep .el-range-separator {
  width: 8% !important;
}
</style>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/person/vourcher.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1026 @@
<template>
  <div>
    <div class="drawContent">
      <!-- å‡­è¯ç±»åž‹é€‰æ‹© -->
      <el-tabs v-model="activeTab" class="voucher-tabs">
        <!-- å¯†ç å‡­è¯ -->
        <el-tab-pane name="password" :label="$t('voucher.password')">
          <div class="tab-content">
            <el-form ref="passwordForm" :model="form" :rules="rules">
              <el-form-item prop="password">
                <el-input v-model="form.password" type="text" :placeholder="$t('voucher.validPassword')" maxlength="6"
                  show-word-limit clearable>
                </el-input>
              </el-form-item>
            </el-form>
          </div>
        </el-tab-pane>
        <!-- å¡ç‰‡å‡­è¯ -->
        <el-tab-pane name="card" :label="$t('voucher.card')">
          <div class="tab-content">
            <el-form ref="cardForm" :model="form" :rules="rules">
              <el-form-item prop="card">
                <el-input v-model="form.card" :placeholder="$t('voucher.validCard')" clearable></el-input>
              </el-form-item>
            </el-form>
          </div>
        </el-tab-pane>
        <!-- äººè„¸å‡­è¯ -->
        <el-tab-pane name="face" :label="$t('voucher.face')">
          <div class="tab-content face-tab">
            <!-- æ³¨å†Œæ–¹å¼é€‰æ‹© -->
            <div class="register-type-selector">
              <el-radio-group v-model="faceType" @change="handleRegisterTypeChange">
                <el-radio-button :label="0">{{ $t('voucher.photoRegistration') }}</el-radio-button>
                <el-radio-button :label="1">{{ $t('voucher.featureValueRegistration') }}</el-radio-button>
              </el-radio-group>
            </div>
            <!-- ç…§ç‰‡ä¸Šä¼ æ³¨å†Œ -->
            <div v-if="faceType == 0" class="photo-register">
              <div class="upload-area">
                <el-upload class="avatar-uploader" action="#" :show-file-list="false" accept=".jpg,.jpeg" :on-change="handleUploadChange"
                  :auto-upload="false">
                  <img v-if="faceImage" :src="faceImage" class="avatar" />
                  <i v-else class="el-icon-plus avatar-uploader-icon"></i>
                  <!-- æ¸…除按钮 -->
                  <el-button v-if="faceImage" class="clear-btn" type="danger" icon="el-icon-delete" circle
                    @click.stop="clearImage"></el-button>
                </el-upload>
              </div>
            </div>
            <!-- ç‰¹å¾å€¼æ³¨å†Œ -->
            <div v-if="faceType == 1" class="feature-register">
              <div class="feature-input-area">
                <el-input type="textarea" clearable :rows="6" :placeholder="$t('common.placeholder')"
                  v-model="faceFeature" class="feature-textarea">
                </el-input>
              </div>
            </div>
          </div>
        </el-tab-pane>
        <!-- æŒ‡çº¹å‡­è¯ -->
        <el-tab-pane v-if="fingerprintEnabled" name="fingerprint" :label="$t('voucher.finger')">
          <div class="tab-content fingerprint-tab">
            <!-- æ³¨å†Œæ–¹å¼é€‰æ‹© -->
            <div class="register-type-selector">
              <el-radio-group v-model="fingerprintType" @change="handleFingerprintTypeChange">
                <el-radio-button :label="0">{{ $t('voucher.fingerRegistration') }}</el-radio-button>
                <el-radio-button :label="1">{{ $t('voucher.fingerFeatureRegistration') }}</el-radio-button>
              </el-radio-group>
            </div>
            <!-- æŒ‡çº¹å½•å…¥ -->
            <div v-if="fingerprintType == 0" class="fingerprint-capture">
              <div class="capture-area">
                <div class="fingerprint-status">
                  <i :class="fingerprintStatusIcon" class="status-icon"></i>
                  <p class="status-text">{{ fingerprintStatusText }}</p>
                </div>
                <el-button
                  type="primary"
                  :loading="fingerprintLoading"
                  :disabled="fingerprintLoading"
                  @click="enrollFinger">
                  {{ fingerprintLoading ? $t('voucher.fingerInputting') : (hasFingerprint ? $t('voucher.fingerReInput') : $t('voucher.startFingerInput')) }}
                </el-button>
                <div v-if="fingerprintLoading" class="capture-tips">
                  <p>{{ $t('voucher.fingerInput') }}</p>
                  <p class="timeout-text">{{ $t('voucher.fingerRemainingTime') }}: {{ fingerprintTimeout }}S</p>
                </div>
              </div>
            </div>
            <!-- ç‰¹å¾å€¼æ³¨å†Œ -->
            <div v-if="fingerprintType == 1" class="feature-register">
              <div class="feature-input-area">
                <el-input type="textarea" clearable :rows="6" :placeholder="$t('voucher.fingerInputTips')"
                  v-model="fingerprintFeature" class="feature-textarea">
                </el-input>
              </div>
            </div>
          </div>
        </el-tab-pane>
        <!-- PIN码凭证 -->
        <el-tab-pane name="code" :label="$t('voucher.code')" v-if="model == 'vf105' || model == 'vf114'">
          <Table :table-label="tableHeader" :table-data="tableData" :table-option="tableOption"></Table>
        </el-tab-pane>
      </el-tabs>
    </div>
    <!-- æ“ä½œæŒ‰é’® -->
    <div class="dialog-footer">
      <el-button type="warning" @click="handleAddClose">
        {{ $t('common.cancel') }}
      </el-button>
      <el-button v-if="activeTab != 'code'" type="primary" :loading="isLoading" :disabled="isLoading" @click="addConfirm">
        {{ $t('common.confirm') }}
      </el-button>
    </div>
  </div>
</template>
<script>
import Table from "@/components/table/tableList";
import { generateRandomString, throttle } from '@/utils/index.js'
export default {
  props: {
    formData: {
      type: Object,
      default: () => ({})
    },
    addOrEditType: {
      type: String,
      default: 'add'
    },
  },
  components: {
    Table
  },
  data () {
    return {
      visible: false,
      userId: '',
      name: '',
      form: {
        password: '',
        card: '',
        face: '',
        fingerprint: '',
        // code100: '',
        // code101: '',
        // code103: '',
        oldPassword: '',
        oldCard: '',
        oldFace: '',
        oldFingerprint: '',
        // oldCode100: '',
        // oldCode101: '',
        // oldCode103: ''
      },
      keyId: {
        password: '',
        card: '',
        face: '',
        fingerprint: '',
        // code100: '',
        // code101: '',
        // code103: '',
      },
      rules: {
        password: [{ message: this.$t('voucher.validPassword'), trigger: ["blur"], pattern: /^\d{6}$/ }],
        card: [{ message: this.$t('voucher.validCard'), trigger: ["blur"], pattern: /^[0-9a-zA-Z]+$/ },],
      },
      model: '',
      isLoading: false,
      // å‡­è¯æ•°æ®
      activeTab: 'password',
      faceType: 0, // æ³¨å†Œæ–¹å¼:0-照片注册,1-特征值注册
      faceImage: '', // äººè„¸å›¾ç‰‡ base64 æˆ– URL
      faceFeature: '', // äººè„¸ç‰¹å¾å€¼
      originalFaceData: null, // å­˜å‚¨åŽŸå§‹äººè„¸æ•°æ®,用于判断是否修改
      // æŒ‡çº¹ç›¸å…³æ•°æ®
      fingerprintType: 0, // æ³¨å†Œæ–¹å¼:0-指纹录入,1-特征值注册
      fingerprintFeature: '', // æŒ‡çº¹ç‰¹å¾å€¼
      fingerprintLoading: false, // æŒ‡çº¹å½•入加载状态
      fingerprintTimeout: 60, // æŒ‡çº¹å½•入超时时间
      fingerprintTimer: null, // æŒ‡çº¹å½•入定时器
      fingerprintPollTimer: null, // æŒ‡çº¹å½•入轮询定时器
      fingerprintStatusText: '等待录入', // æŒ‡çº¹çŠ¶æ€æ–‡æœ¬
      fingerprintStatusIcon: 'el-icon-fingerprint', // æŒ‡çº¹çŠ¶æ€å›¾æ ‡
      originalFingerprintData: null, // å­˜å‚¨åŽŸå§‹æŒ‡çº¹æ•°æ®,用于判断是否修改
      hasFingerprint: false, // æ˜¯å¦å·²æœ‰æŒ‡çº¹å‡­è¯
      tableHeader: [
        {
          label: this.$t('voucher.credentialId'),
          list: 'keyId',
        },
        {
          label: this.$t('voucher.codeType'),
          list: 'type',
        },
        {
          label: this.$t('voucher.credentialValue'),
          list: 'code',
        },
      ],
      tableOption: {
        label: this.$t('common.operation'),
        width: "100px",
        value: 0,
        options: [
          {
            label: this.$t('common.delete'),
            key: 1,
            type: "text",
            State: true,
            method: (row) => {
              this.handleDeleteCode(row.keyId)
            },
          }
        ]
      },
      tableData: [],
    };
  },
  created () {
    this.initialForm = JSON.parse(JSON.stringify(this.form));
    this.initialKeyId = JSON.parse(JSON.stringify(this.keyId));
    this.userId = this.formData.userId
    let publicConfig = sessionStorage.getItem('publicConfig')
    let { finger, model } = publicConfig ? JSON.parse(publicConfig) : {}
    this.fingerprintEnabled = finger
    this.model = model
    this.getVoucher()
  },
  beforeDestroy() {
    // æ¸…理定时器
    this.interruptFinger()
    this.clearFingerprintTimers()
  },
  methods: {
    // æŸ¥è¯¢å‡­è¯
    async getVoucher () {
      let data = {
        page: 0,
        size: 100,
        userId: this.userId
      }
      try {
        const res = await this.$http.post('/getKey', { data })
        if (res.code == 200) {
          console.log(res)
          let content = res.data.content
          let card = content.filter(v => v.type == 200)[0]
          if (card) {
            this.form.card = card.code
            this.form.oldCard = card.code
            this.keyId.card = card.keyId
          } else {
            this.keyId.card = ''
          }
          let face = content.filter(v => v.type == 300)[0]
          if (face) {
            // ä¿å­˜åŽŸå§‹äººè„¸æ•°æ®
            let faceType = face.extra ? JSON.parse(face.extra).faceType : 0
            this.originalFaceData = {
              keyId: face.keyId,
              code: face.code,
              faceType
            }
            if (face.extra) {
              if (faceType == 0) {
                this.faceImage = "data:image/jpeg;base64," + face.code
              } else if (faceType == 1) {
                this.faceFeature = face.code
              }
              this.faceType = faceType
            }
            this.form.face = face.code
            this.form.oldFace = face.code
            this.keyId.face = face.keyId
          } else {
            this.keyId.face = ''
            this.originalFaceData = null
          }
          let password = content.filter(v => v.type == 400)[0]
          if (password) {
            this.form.password = password.code
            this.form.oldPassword = password.code
            this.keyId.password = password.keyId
          } else {
            this.keyId.password = ''
          }
          // å¤„理指纹凭证
          let fingerprint = content.filter(v => v.type == 500)[0]
          if (fingerprint) {
            let fingerprintType = fingerprint.extra ? JSON.parse(fingerprint.extra).fingerprintType : 0
            this.originalFingerprintData = {
              keyId: fingerprint.keyId,
              code: fingerprint.code,
              fingerprintType
            }
            // å¦‚果已有指纹凭证,默认显示指纹录入页面(fingerprintType = 0)
            // ä½†ä¿ç•™åŽŸå§‹ fingerprintType ç”¨äºŽåˆ¤æ–­æ˜¯å¦ä¿®æ”¹
            this.fingerprintType = 0
            this.hasFingerprint = true
            // å¦‚果原始是特征值注册,保存特征值用于显示
            if (fingerprint.extra && fingerprintType == 1) {
              this.fingerprintFeature = fingerprint.code
            }
            this.form.fingerprint = fingerprint.code
            this.form.oldFingerprint = fingerprint.code
            this.keyId.fingerprint = fingerprint.keyId
            // è®¾ç½®çŠ¶æ€æ–‡æœ¬ä¸º"指纹已录入"
            this.fingerprintStatusText = this.$t('voucher.fingerInputed')
            this.fingerprintStatusIcon = 'el-icon-success'
          } else {
            this.keyId.fingerprint = ''
            this.originalFingerprintData = null
            this.hasFingerprint = false
            // é‡ç½®çŠ¶æ€æ–‡æœ¬
            this.fingerprintStatusText = this.$t('voucher.fingerWaitInput')
            this.fingerprintStatusIcon = 'el-icon-fingerprint'
          }
          this.tableData = content.filter(v => v.type == 100 || v.type == 101 || v.type == 103)
          console.log(this.tableData)
        } else {
          this.$message.error(res.message)
        }
      } catch (even) {
        console.log(even);
      }
    },
    // å¤„理注册方式变化
    handleRegisterTypeChange (value) {
      console.log('切换注册方式:', value);
    },
    // å¤„理指纹注册方式变化
    handleFingerprintTypeChange (value) {
      console.log('切换指纹注册方式:', value);
      // æ¸…理定时器
      this.clearFingerprintTimers()
      // æ ¹æ®æ˜¯å¦å·²æœ‰æŒ‡çº¹å‡­è¯è®¾ç½®çŠ¶æ€æ–‡æœ¬
      if (value === 0) {
        this.fingerprintStatusText = this.hasFingerprint ? this.$t('voucher.fingerInputed') : this.$t('voucher.fingerWaitInput')
        this.fingerprintStatusIcon = this.hasFingerprint ? 'el-icon-success' : 'el-icon-fingerprint'
      } else {
        this.fingerprintStatusText = this.$t('voucher.fingerWaitInput')
        this.fingerprintStatusIcon = 'el-icon-fingerprint'
      }
    },
    // æ¸…除定时器
    clearFingerprintTimers() {
      if (this.fingerprintTimer) {
        clearInterval(this.fingerprintTimer)
        this.fingerprintTimer = null
      }
      if (this.fingerprintPollTimer) {
        clearInterval(this.fingerprintPollTimer)
        this.fingerprintPollTimer = null
      }
    },
    // å¼€å§‹æŒ‡çº¹å½•å…¥
    async enrollFinger() {
      if (!this.fingerprintEnabled) {
        this.$message.warning('当前设备不支持指纹操作')
        return
      }
      if (!this.userId) {
        this.$message.error(this.$t('person.userNotExist'))
        return false
      }
      this.fingerprintLoading = true
      this.fingerprintTimeout = 60
      this.fingerprintStatusText = this.$t('voucher.fingerInputNow')
      this.fingerprintStatusIcon = 'el-icon-loading'
      try {
        // å‘送指纹录入指令
        const startRes = await this.$http.post('/control', {
          data: {
            command: 12,
            extra: {
              userId: this.userId,
              fingerprintAction: 0,
              isRemote: false
            },
          }
        })
        if (startRes.code !== 200) {
          this.$message.error(startRes.message || this.$t('voucher.fingerInputFailed'))
          this.resetFingerprintStatus()
          return
        }
        // å¼€å§‹å€’计时
        this.fingerprintTimer = setInterval(() => {
          this.fingerprintTimeout--
          if (this.fingerprintTimeout <= 0) {
            this.handleFingerprintTimeout()
          }
        }, 1000)
        // å¼€å§‹è½®è¯¢æŸ¥è¯¢æŒ‡çº¹å½•入结果
        this.fingerprintPollTimer = setInterval(async () => {
          await this.checkFingerprintResult()
        }, 500) // æ¯0.5秒查询一次
      } catch (error) {
        console.error('指纹录入失败:', error)
        this.$message.error(this.$t('voucher.fingerReTry'))
        this.resetFingerprintStatus()
      }
    },
    // ä¸­æ–­æŒ‡çº¹å½•å…¥
    async interruptFinger () {
      if (!this.fingerprintLoading) return
      try {
        await this.$http.post('/control', {
          data: {
            command: 12,
            extra: {
              fingerprintAction: 1
            }
          }
        })
      } catch (error) {
        console.error('中断指纹采集失败:', error)
      } finally {
        this.fingerprintLoading = false
        this.clearFingerprintTimers()
      }
    },
    // æŸ¥è¯¢æŒ‡çº¹å½•入结果
    async checkFingerprintResult() {
      try {
        const res = await this.$http.post('/getFingerChar', {
          data: {
            userId: this.userId
          }
        })
        if (res.code === 200 && res.data && typeof res.data.ret == 'string') {
          // å½•入成功,将特征值填充到form.fingerprint和fingerprintFeature
          const featureValue = res.data.ret
          this.form.fingerprint = featureValue
          this.fingerprintFeature = featureValue
          this.fingerprintStatusText = this.$t('voucher.fingerInputSuccess')
          this.fingerprintStatusIcon = 'el-icon-success'
          this.$message.success(this.$t('voucher.fingerFilled'))
          this.resetFingerprintStatus(false)
          // æ ‡è®°å·²æœ‰æŒ‡çº¹å‡­è¯
          this.hasFingerprint = true
          // å½•入成功后,自动切换到特征值注册模式,展示采集到的特征值
          // æ— è®ºæ˜¯é¦–次录入还是重新录入,都要切换到特征值注册页面
          this.fingerprintType = 1
        }else if (res.code === 200 && res.data && res.data.ret == -1) {
          this.$message.error(this.$t('voucher.fingerFailed'))
          this.handleFingerprintError()
        }
      } catch (error) {
        console.error('查询指纹结果失败:', error)
      }
    },
    // å¤„理指纹录入超时
    handleFingerprintTimeout() {
      this.$message.error(this.$t('voucher.fingerInputTimeout'))
      this.fingerprintStatusText = this.$t('voucher.fingerTimeout')
      this.fingerprintStatusIcon = 'el-icon-error'
      this.resetFingerprintStatus()
    },
    // å¤„理指纹录入失败
    handleFingerprintError() {
      this.$message.error(this.$t('voucher.fingerInputError'))
      this.fingerprintStatusText = this.$t('voucher.fingerError')
      this.fingerprintStatusIcon = 'el-icon-error'
      this.resetFingerprintStatus()
    },
    // é‡ç½®æŒ‡çº¹å½•入状态
    resetFingerprintStatus(resetText = true) {
      this.fingerprintLoading = false
      this.clearFingerprintTimers()
      if (resetText) {
        setTimeout(() => {
          // æ ¹æ®æ˜¯å¦å·²æœ‰æŒ‡çº¹å‡­è¯è®¾ç½®çŠ¶æ€æ–‡æœ¬
          this.fingerprintStatusText = this.hasFingerprint ? this.$t('voucher.fingerInputed') : this.$t('voucher.fingerWaitInput')
          this.fingerprintStatusIcon = this.hasFingerprint ? 'el-icon-success' : 'el-icon-fingerprint'
        }, 2000)
      }
    },
    clearImage () {
      this.faceImage = ""
      this.form.face = ""
    },
    handleUploadChange (file) {
      const reader = new FileReader();
      reader.onload = (e) => {
        console.log(e);
        let result = e.target.result
        this.faceImage = result
        this.form.face = result.split(',').pop()
      };
      reader.readAsDataURL(file.raw);
    },
    addConfirm: throttle (async function() {
      if (this.isLoading) return
      // æäº¤å‡­è¯é€»è¾‘
      try {
        // å¦‚果是人脸凭证,需要根据当前类型设置数据
        if (this.activeTab === 'face') {
          if (this.faceType === 1) {
            // ç‰¹å¾å€¼æ³¨å†Œ,直接使用特征值
            this.form.face = this.faceFeature
          }
          // ç…§ç‰‡æ³¨å†Œå·²ç»åœ¨handleUploadChange中设置好了form.face
        }
        // å¦‚果是指纹凭证,需要根据当前类型设置数据
        if (this.activeTab === 'fingerprint') {
          if (this.fingerprintType === 1) {
            // ç‰¹å¾å€¼æ³¨å†Œ,直接使用特征值
            this.form.fingerprint = this.fingerprintFeature
          }
          // æŒ‡çº¹å½•入已经在checkFingerprintResult中设置好了form.fingerprint
        }
        await this.$refs.passwordForm.validate();
        await this.$refs.cardForm.validate();
        this.submitData();
      } catch (error) {
        console.log(error)
      }
    }, 2000),
    handleAddClose () {
      this.interruptFinger()
      this.clearFingerprintTimers()
      this.$emit('close-drawer', false);
    },
    async submitData () {
      this.isLoading = true
      try {
        if (!this.userId) {
        this.$message.error(this.$t('person.userNotExist'))
        return false
        }
        let keyAddList = []
        let keyEditList = []
        let keyDeleteList = []
        // å¤„理密码凭证
        this.processPasswordVoucher(keyAddList, keyEditList, keyDeleteList)
        // å¤„理卡片凭证
        this.processCardVoucher(keyAddList, keyEditList, keyDeleteList)
        // å¤„理人脸凭证
        this.processFaceVoucher(keyAddList, keyEditList, keyDeleteList)
        // å¤„理指纹凭证
        if (this.fingerprintEnabled) {
          this.processFingerprintVoucher(keyAddList, keyEditList, keyDeleteList)
        }
        if (keyAddList.length) {
          const res = await this.$http.post(
            "/insertKey",
            {
              data: keyAddList
            }
          );
          if (res.code == 200) {
            this.$message.success(this.$t('common.saveSuccess'))
            this.getVoucher()
          } else {
            if (res.data && res.data[0]) {
              this.$message.error(res.data[0].errmsg)
            }
          }
        }
        if (keyEditList.length) {
          const res = await this.$http.post(
            "/modifyKey",
            {
              data: keyEditList
            }
          );
          if (res.code == 200) {
            this.$message.success(this.$t('common.saveSuccess'))
            this.getVoucher()
          } else {
            if (res.data && res.data[0]) {
              this.$message.error(res.data[0].errmsg)
            }
          }
        }
        if (keyDeleteList.length) {
          const res = await this.$http.post(
            "/delKey",
            {
              data: { keyIds: keyDeleteList }
            }
          );
          if (res.code == 200) {
            this.$message.success(this.$t('common.saveSuccess'))
            this.getVoucher()
          } else {
            if (res.data && res.data[0]) {
              this.$message.error(res.data[0].errmsg)
            }
          }
        }
        // å¦‚果没有数据变化
        if (!keyAddList.length && !keyEditList.length && !keyDeleteList.length) {
          this.$message.info(this.$t('common.noDataSaved'))
        }
      } catch (error) {
        console.error('submitData error:', error)
      } finally {
        this.isLoading = false
      }
    },
    // å¤„理密码凭证
    processPasswordVoucher (keyAddList, keyEditList, keyDeleteList) {
      // å¦‚果存在密码凭证,且密码有变化,则修改
      if (this.keyId.password) {
        if (this.form.password) {
          if (this.form.password !== this.form.oldPassword) {
            keyEditList.push({
              keyId: this.keyId.password,
              userId: this.userId,
              type: '400',
              code: this.form.password
            })
          }
        } else {
          keyDeleteList.push(this.keyId.password)
        }
      } else {
        // ä¸å­˜åœ¨å¯†ç å‡­è¯,新增
        // å¦‚果密码为空,不处理
        if (!this.form.password) return
        keyAddList.push({
          keyId: generateRandomString(),
          userId: this.userId,
          type: '400',
          code: this.form.password
        })
      }
    },
    // å¤„理卡片凭证
    processCardVoucher (keyAddList, keyEditList, keyDeleteList) {
      // å¦‚果存在卡片凭证,且卡片号有变化,则修改
      if (this.keyId.card) {
        if (this.form.card) {
          if (this.form.card !== this.form.oldCard) {
            keyEditList.push({
              keyId: this.keyId.card,
              userId: this.userId,
              type: '200',
              code: this.form.card
            })
          }
        } else {
          keyDeleteList.push(this.keyId.card)
        }
      } else {
        // ä¸å­˜åœ¨å¡ç‰‡å‡­è¯,新增
        // å¦‚果卡片号为空,不处理
        if (!this.form.card) return
        keyAddList.push({
          keyId: generateRandomString(),
          userId: this.userId,
          type: '200',
          code: this.form.card
        })
      }
    },
    // å¤„理人脸凭证
    processFaceVoucher (keyAddList, keyEditList, keyDeleteList) {
      // å¦‚果人脸数据为空,不处理
      const hasFaceVoucher = this.originalFaceData !== null
      // å¦‚果存在人脸凭证,且人脸数据有变化,则修改
      if (hasFaceVoucher) {
        // åˆ¤æ–­æ•°æ®æ˜¯å¦æœ‰å˜åŒ–
        const isDataChanged =
          this.form.face !== this.form.oldFace ||
          this.faceType !== this.originalFaceData.faceType
        if (isDataChanged) {
          if (this.form.face) {
            keyEditList.push({
              keyId: this.keyId.face,
              userId: this.userId,
              type: '300',
              code: this.form.face,
              extra: {
                faceType: this.faceType
              }
            })
          } else {
            keyDeleteList.push(this.keyId.face)
          }
        }
      } else {
        // ä¸å­˜åœ¨äººè„¸å‡­è¯,新增
        if (!this.form.face) return
        // åˆ¤æ–­æ˜¯å¦å·²å­˜åœ¨äººè„¸å‡­è¯
        keyAddList.push({
          keyId: generateRandomString(),
          userId: this.userId,
          type: '300',
          code: this.form.face,
          extra: {
            faceType: this.faceType
          }
        })
      }
    },
    // å¤„理指纹凭证
    processFingerprintVoucher (keyAddList, keyEditList, keyDeleteList) {
      // å¦‚果指纹数据为空,不处理
      const hasFingerprintVoucher = this.originalFingerprintData !== null
      // å¦‚果存在指纹凭证,且指纹数据有变化,则修改
      if (hasFingerprintVoucher) {
        // ç¡®å®šè¦ä½¿ç”¨çš„ fingerprintType
        let finalFingerprintType = this.fingerprintType
        // å¦‚果当前在指纹录入页面(fingerprintType === 0)且指纹数据没有变化,说明没有重新录入,保持原始的 fingerprintType
        if (this.fingerprintType === 0 && this.form.fingerprint === this.form.oldFingerprint) {
          finalFingerprintType = this.originalFingerprintData.fingerprintType
        }
        // å¦‚果当前在特征值注册页面(fingerprintType === 1),使用特征值注册类型
        // åˆ¤æ–­æ•°æ®æ˜¯å¦æœ‰å˜åŒ–
        const isDataChanged =
          this.form.fingerprint !== this.form.oldFingerprint ||
          finalFingerprintType !== this.originalFingerprintData.fingerprintType
        if (isDataChanged) {
          if (this.form.fingerprint) {
            keyEditList.push({
              keyId: this.keyId.fingerprint,
              userId: this.userId,
              type: '500',
              code: this.form.fingerprint,
              extra: {
                fingerprintType: finalFingerprintType
              }
            })
          } else {
            keyDeleteList.push(this.keyId.fingerprint)
          }
        }
      } else {
        // ä¸å­˜åœ¨æŒ‡çº¹å‡­è¯,新增
        if (!this.form.fingerprint) return
        keyAddList.push({
          keyId: generateRandomString(),
          userId: this.userId,
          type: '500',
          code: this.form.fingerprint,
          extra: {
            fingerprintType: this.fingerprintType
          }
        })
      }
    },
    handleDeleteCode (keyId) {
      let that = this
      this.$confirm(
        this.$t('common.deleteTips'),
        this.$t('common.tips'),
        {
          confirmButtonText: this.$t('common.confirm'),
          cancelButtonText: this.$t('common.cancel'),
          type: 'warning'
        }
      ).then(async () => {
        try {
          const res = await that.$http.post("/delKey", {
            data: {
              keyIds: [keyId]
            }
          })
          if (res.code == 200) {
            that.$message.success(this.$t('common.deleteSuccess'))
            that.getVoucher()
          } else {
            that.$message.error(res.message)
          }
        } catch (even) {
          console.log(even)
        }
      }).catch(() => { })
    },
  },
};
</script>
<style lang='less' scoped>
.voucher-tabs {
  margin-top: 10px;
}
.tab-content {
  padding: 10px 0;
}
/* ä¸Šä¼ åŒºåŸŸ */
.avatar-uploader {
  border: 2px dashed #d9d9d9;
  border-radius: 8px;
  cursor: pointer;
  width: 150px;
  height: 150px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background-color: #fafafa;
}
.avatar-uploader:hover {
  border-color: #409eff;
}
.avatar {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #999;
}
.upload-tip {
  margin-top: 8px;
  font-size: 12px;
  color: #999;
}
/* å¡ç‰‡æç¤º */
.card-scan-tip {
  font-size: 13px;
  color: #666;
  margin-top: 10px;
  padding: 10px;
  background: #e6f7ff;
  border-radius: 4px;
  border-left: 4px solid #1890ff;
}
.face-register-container {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}
.register-type-selector {
  margin-bottom: 20px;
  text-align: center;
}
.upload-area {
  text-align: center;
  margin-bottom: 20px;
}
.avatar-uploader {
  position: relative;
  display: inline-block;
}
.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  width: 150px;
  height: 150px;
}
.avatar-uploader .el-upload:hover {
  border-color: #409EFF;
}
/deep/.clear-btn {
  position: absolute;
  top: -10px;
  left: 5px;
  width: 24px !important;
  height: 24px !important;
  font-size: 12px;
  z-index: 10;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 150px;
  height: 150px;
  line-height: 150px;
  text-align: center;
}
.avatar {
  width: 150px;
  height: 150px;
  display: block;
}
.feature-input-area {
  margin-top: 20px;
}
.feature-textarea {
  width: 100%;
}
.feature-tips {
  margin-top: 10px;
  font-size: 12px;
  color: #909399;
}
.action-buttons {
  margin-top: 20px;
  text-align: center;
}
.el-button {
  margin: 0 10px;
}
/* æŒ‡çº¹ç›¸å…³æ ·å¼ */
.fingerprint-tab {
  min-height: 200px;
}
.capture-area {
  text-align: center;
  padding: 20px;
}
.fingerprint-status {
  margin-bottom: 20px;
}
.status-icon {
  font-size: 64px;
  color: #409EFF;
  display: block;
  margin-bottom: 10px;
}
.status-icon.el-icon-loading {
  animation: rotating 2s linear infinite;
}
.status-icon.el-icon-success {
  color: #67C23A;
}
.status-icon.el-icon-error {
  color: #F56C6C;
}
@keyframes rotating {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
.status-text {
  font-size: 16px;
  color: #606266;
  margin: 10px 0;
}
.capture-tips {
  margin-top: 20px;
  font-size: 14px;
  color: #909399;
}
.capture-tips p {
  margin: 5px 0;
}
.timeout-text {
  color: #F56C6C;
  font-weight: bold;
}
</style>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/record/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,314 @@
<template>
  <el-main>
    <el-row>
      <el-col :span="24">
        <el-form :inline="true" class="el-InputForm">
          <!-- <el-form-item :label="$t('log.accessMethod')">
            <el-select v-model="searchForm.type" :placeholder="$t('common.placeholderSelect')" clearable>
              <el-option :label="$t('voucher.password')" value="400"></el-option>
              <el-option :label="$t('voucher.card')" value="200"></el-option>
              <el-option :label="$t('voucher.face')" value="300"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="通行结果">
            <el-select v-model="searchItem.result" placeholder="请选择通行结果" clearable>
              <el-option label="成功" value="1"></el-option>
              <el-option label="失败" value="0"></el-option>
            </el-select>
          </el-form-item> -->
          <el-form-item :label="$t('person.name')" label-width="80px">
            <el-input v-model="searchForm.name" :placeholder="$t('person.placeholderName')" clearable></el-input>
          </el-form-item>
          <el-form-item :label="$t('log.passingTime')" label-width="100px">
            <el-date-picker v-model="time" value-format="yyyy-MM-dd HH:mm:ss" type="datetimerange" :range-separator="$t('common.to')"
              :start-placeholder="$t('common.startTime')" :end-placeholder="$t('common.endTime')">
            </el-date-picker>
          </el-form-item>
          <div style="position: absolute; right: 25px; bottom: 25px;">
            <el-button type="info" size="medium" icon="el-icon-search" @click="search()"
              style="margin-left:10px;border:none;">{{ $t('common.query') }}</el-button>
            <el-button type="warning" size="medium" icon="el-icon-refresh-right" @click="doReset()">{{
              $t('common.reset')
            }}</el-button>
          </div>
        </el-form>
        <el-col :span="24" style="margin:20px 0">
          <el-button type="danger" size="mini" style="font-size:12px;" icon="el-icon-delete" @click="batchDelete()">{{
            $t('common.batchDelete') }}</el-button>
        </el-col>
      </el-col>
    </el-row>
    <el-col :span="24">
      <Table :table-label="tableHeader" v-loading="isSubmitLoading" :table-data="tableData" :table-option="tableOption"
        :table-selection="tableSelection" @onHandleSelectionChange="handleSelectionChange"></Table>
      <pagination ref="page" :total="total" @pageChange="pageChange"></pagination>
    </el-col>
    <!-- é€šè¡Œå›¾ç‰‡é¢„览 -->
    <el-dialog :visible.sync="dialogTableVisible" width="30%">
      <div class="imgDiv"><img :src="urlImg" alt=""></div>
    </el-dialog>
  </el-main>
</template>
<script>
import Pagination from "@/components/table/pagination.vue"
import Table from "@/components/table/tableList.vue"
import { removeEmptyValues, resetObjectValues, parseTime } from '@/utils/index.js'
export default {
  name: 'Record',
  components: { Table, Pagination },
  data () {
    return {
      tableHeader: [
        { label: this.$t('person.userId'), list: "userId", overflowShow: 'hidden' },
        {
          type: "html",
          label: this.$t('person.name'),
          code: (row) => {
            return row.name ? row.name : '-'
          }
        },
        {
          label: this.$t('log.passingTime'),
          type: "html",
          overflowShow: 'hidden',
          code: (row) => {
            return parseTime(row.timeStamp * 1000)
          }
        },
        {
          type: "html",
          label: this.$t('log.accessMethod'),
          code: (row) => {
            if (row.type == 200) {
              return this.$t('voucher.card')
            } else if (row.type == 300) {
              return this.$t('voucher.face')
            } else if (row.type == 400) {
              return this.$t('voucher.password')
            } else if (row.type == 500) {
              return this.$t('voucher.finger')
            } else if (row.type == 100 || row.type == 101 || row.type == 103 || row.type == 104) {
              return this.$t('voucher.code')
            }
          }
        },
        {
          type: "html",
          label: this.$t('log.accessPass'),
          overflowShow: 'hidden',
          code: (row) => {
            return row.type == 300 ? '-' : row.code
          }
        },
        {
          type: "html",
          label: this.$t('log.accessResult'),
          code: (row) => {
            if (row.result === 1) {
              return `<span style="color:#ff5722;font-weight:600">${this.$t('common.failure')}</span>`
            } else if (row.result === 0) {
              return `<span style="color:#1fab89;font-weight:600">${this.$t('common.success')}</span>`
            }
          }
        },
      ],
      tableSelection: {
        key: true,
        type: "selection",
        detaile: false,
      },
      tableOption: {
        label: this.$t('common.operation'),
        width: "180px",
        value: 0,
        options: [
          {
            label: this.$t('common.delete'),
            key: 0,
            type: "text",
            State: true,
            method: (row) => {
              this.handleDelete(row.id)
            },
          },
          {
            label: this.$t('log.viewPhotos'),
            key: 0,
            type: "text",
            State: true,
            show: (row) => {
              return row.type == 300
            },
            method: (row) => {
              this.getPhoto(row.code)
            },
          },
        ]
      },
      tableData: [],
      isSubmitLoading: false,
      dialogTableVisible: false,
      urlImg: "",
      total: 0,
      pageSize: 20,
      currentPage: 0,
      searchForm: {
        name: ''
      },
      time: [],
      selectIdList: [],
    };
  },
  created () {
    this.fetchData()
  },
  mounted () { },
  methods: {
    search () {
      let that = this
      that.currentPage = 0;
      that.$refs.page.Page(1);
      that.fetchData();
    },
    pageChange (item) {
      this.pageSize = item.limit;
      this.currentPage = item.page - 1;
      this.fetchData();
    },
    handleSelectionChange (vals) {
      this.selectIdList = [];
      vals.map(v => {
        this.selectIdList.push(v.id);
      });
    },
    // èŽ·å–é€šè¡Œè®°å½•
    async fetchData () {
      let searchForm = removeEmptyValues({ ...this.searchForm })
      if (this.time.length) {
        searchForm.startTime = new Date(this.time[0]).getTime() / 1000
        searchForm.endTime = new Date(this.time[1]).getTime() / 1000
      }
      searchForm.page = this.currentPage
      searchForm.size = this.pageSize
      searchForm.flag = false
      try {
        const res = await this.$http.post('/getRecord', { data: searchForm })
        if (res.code === 200) {
          this.tableData = res.data.content
          this.total = res.data.total
        } else {
          this.$message.error(res.message)
        }
      } catch (even) {
        console.log(even)
      }
    },
    handleDelete (id) {
      let that = this
      this.$confirm(
        this.$t('common.deleteTips'),
        this.$t('common.tips'),
        {
          confirmButtonText: this.$t('common.confirm'),
          cancelButtonText: this.$t('common.cancel'),
          type: 'warning'
        }
      ).then(async () => {
        try {
          const res = await that.$http.post("delRecord", { data: { recordId: [id] } })
          if (res.code == 200) {
            that.$message.success(this.$t('common.deleteSuccess'))
            that.fetchData()
          } else {
            that.$message.error(res.message)
          }
        } catch (even) {
          console.log(even)
        }
      }).catch(() => { })
    },
    batchDelete () {
      let that = this
      if (this.selectIdList.length <= 0) {
        this.$message.warning(this.$t('common.placeholderSelect'));
        return;
      }
      this.$confirm(this.$t('common.deleteTips'),
        this.$t('common.tips'),
        {
          confirmButtonText: this.$t('common.confirm'),
          cancelButtonText: this.$t('common.cancel'),
          type: 'warning'
        })
        .then(async () => {
          that.isSubmitLoading = true;
          const res = await this.$http.post("delRecord", { data: { recordId: this.selectIdList }});
          that.isSubmitLoading = false;
          if (res.code == 200) {
            that.$message.success(this.$t('common.deleteSuccess'))
            that.fetchData()
          } else {
            that.$message.error(res.message)
          }
        })
        .catch(() => {
          return false;
        });
    },
    doReset () {
      resetObjectValues(this.searchForm)
      this.time = []
      this.fetchData()
    },
    async getPhoto (code) {
      try {
        const res = await this.$http.post('/getRecordMsg', { data: code })
        if (res.code == 200) {
          console.log(res);
          this.urlImg = 'data:image/png;base64,' + res.data
          this.dialogTableVisible = true
        } else {
          this.$message.error(res.message)
        }
      } catch (even) {
        console.log(even)
      }
    },
    // å›¾ç‰‡æŸ¥çœ‹
    handleCellClick (e, column) {
      if (JSON.parse(e.extra).pic && column.label === "通行照片") {
        this.dialogTableVisible = true
        let imgUrl = JSON.parse(e.extra).pic
        this.urlImg = imgUrl.split('&imageView')[0]
      }
    },
  },
};
</script>
<style lang="less" scoped>
.el-main {
  .imgDiv {
    width: 100%;
    text-align: center;
    img {
      height: 100%;
    }
  }
}
</style>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/security/add.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,132 @@
<!-- æ–°å¢žå¯†é’¥ -->
<template>
  <div>
    <el-drawer size="40%" :with-header="false" :visible.sync="visible" :before-close="handleAddClose">
      <el-form ref="form" :model="form" label-width="100px" :rules="rules" style="width: 90%;margin: 50px auto;">
        <el-form-item :label="$t('security.keyId')" v-if="type == 'detail'">
          <el-input v-model="form.securityId" :placeholder="$t('common.placeholder')"></el-input>
        </el-form-item>
        <el-form-item :label="$t('security.keyType')">
          <el-radio-group v-model="form.type">
            <el-radio label="RSA">RSA</el-radio>
            <el-radio label="AES">AES</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item :label="$t('security.keyEncoding')" prop="key">
          <el-input v-model="form.key" :placeholder="$t('common.placeholder')"></el-input>
        </el-form-item>
        <el-form-item :label="$t('security.keyValue')" prop="value">
          <el-input type="textarea" clearable :rows="5" v-model="form.value"
            :placeholder="$t('common.placeholder')"></el-input>
        </el-form-item>
        <el-form-item :label="$t('security.validTime')">
          <el-date-picker :clearable="false" v-model="dateTimes" value-format="timestamp"
            :default-time="['00:00:00', '23:59:59']" type="datetimerange" :range-separator="$t('common.to')"
            :start-placeholder="$t('common.startTime')" :end-placeholder="$t('common.endTime')">
          </el-date-picker>
        </el-form-item>
      </el-form>
      <div class="dialog-footer">
        <el-button type="warning" @click="handleAddClose(false)">{{ $t('common.cancel') }}</el-button>
        <el-button v-if="type == 'add'" type="primary" @click="updata()">{{ $t('common.confirm') }}</el-button>
      </div>
    </el-drawer>
  </div>
</template>
<script>
import { generateRandomString, resetObjectValues } from '@/utils/index.js'
export default {
  data () {
    return {
      form: {
        securityId: "",
        type: "",
        key: "",
        value: ""
      },
      rules: {
        key: [{ required: true, message: this.$t('common.placeholder'), trigger: ["blur"] },],
        value: [{ required: true, message: this.$t('common.placeholder'), trigger: ["blur"] },],
      },
      dateTimes: [],
      visible: false,
      type: 'info'
    }
  },
  created () {
    this.initialForm = JSON.parse(JSON.stringify(this.form));
  },
  mounted () {
  },
  methods: {
    open (type, row) {
      if (type == 'add') {
        this.form.type = 'RSA'
        console.log('add')
      } else {
        this.form.securityId = row.securityId
        this.form.type = row.type
        this.form.key = row.key
        this.form.value = row.value
        this.dateTimes = [row.startTime * 1000, row.endTime * 1000]
      }
      this.type = type
      this.visible = true
    },
    handleAddClose (showConfirm = true) {
      const closeAction = () => {
        if (this.$refs.form) {
          this.$refs.form.clearValidate();
        }
        this.visible = false;
        this.form = { ...this.initialForm };
        this.dateTimes = []
      };
      if (showConfirm) {
        this.$confirm(this.$t('common.closeTips'))
          .then(closeAction)
          .catch(() => { });
      } else {
        closeAction();
      }
    },
    updata () {
      let that = this
      this.$refs['form'].validate(async (valid) => {
        if (valid) {
          if (!that.dateTimes.length) {
            that.$message.warning(this.$t('permission.choose_time_range'));
            return false
          }
          let data = {
            securityId: generateRandomString(),
            type: that.form.type,
            key: that.form.key,
            value: that.form.value,
            startTime: new Date(that.dateTimes[0]).getTime() / 1000,
            endTime: new Date(that.dateTimes[1]).getTime() / 1000
          }
          const res = await this.$http.post(
            "/insertSecurity",
            { data: [data] }
          );
          if (res.code == 200) {
            this.$message.success(this.$t('common.addSuccess'))
            this.visible = false
            resetObjectValues(this.form)
            this.$emit("operation-success")
          } else {
            if (res.data && res.data[0]) {
              this.$message.error(res.data[0].errmsg)
            }
          }
        }
      })
    }
  }
}
</script>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/src/views/security/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,217 @@
<template>
  <el-main>
    <el-row>
      <el-col :span="24">
        <el-form :inline="true" class="el-InputForm">
          <el-form-item :label="$t('security.keyId')" label-width="80px">
            <el-input v-model="searchForm.securityId" :placeholder="$t('common.placeholder')" clearable></el-input>
          </el-form-item>
          <div style="position: absolute; right: 25px; bottom: 25px;">
            <el-button type="info" size="medium" icon="el-icon-search" @click="search()"
              style="margin-left:10px;border:none;">{{ $t('common.query') }}</el-button>
            <el-button type="warning" size="medium" icon="el-icon-refresh-right" @click="doReset()">{{
              $t('common.reset')
              }}</el-button>
          </div>
        </el-form>
        <el-col :span="24" style="margin:20px 0">
          <el-button type="primary" style="border:none;" size='mini' icon="el-icon-plus" @click="doAdd">{{
            $t('security.newKey') }}</el-button>
          <el-button type="danger" size="mini" style="font-size:12px;" icon="el-icon-delete" @click="batchDelete()">{{
            $t('security.clearKey') }}</el-button>
        </el-col>
      </el-col>
    </el-row>
    <el-col :span="24">
      <Table :table-label="tableHeader" v-loading="isSubmitLoading" :table-data="tableData" :table-option="tableOption">
      </Table>
      <pagination ref="page" :total="total" @pageChange="pageChange"></pagination>
    </el-col>
    <add ref="add" @operation-success="fetchData"></add>
  </el-main>
</template>
<script>
import Pagination from "@/components/table/pagination.vue"
import Table from "@/components/table/tableList.vue"
import Add from "./add.vue"
import { removeEmptyValues, resetObjectValues, parseTime } from '@/utils/index.js'
export default {
  name: 'Record',
  components: { Table, Pagination, Add },
  data () {
    return {
      tableHeader: [
        { label: this.$t('security.keyId'), list: "securityId", overflowShow: 'hidden' },
        { label: this.$t('security.keyType'), list: "type", overflowShow: 'hidden' },
        { label: this.$t('security.keyEncoding'), list: "key", overflowShow: 'hidden' },
        { label: this.$t('security.keyValue'), list: "value", overflowShow: 'hidden' },
        {
          label: this.$t('security.startTime'),
          type: "html",
          overflowShow: 'hidden',
          code: (row) => {
            return parseTime(row.startTime * 1000)
          }
        },
        {
          label: this.$t('security.expirationTime'),
          type: "html",
          overflowShow: 'hidden',
          code: (row) => {
            return parseTime(row.endTime * 1000)
          }
        }
      ],
      tableOption: {
        label: this.$t('common.operation'),
        width: "180px",
        value: 0,
        options: [
          {
            label: this.$t('common.delete'),
            key: 0,
            type: "text",
            State: true,
            method: (row) => {
              this.handleDelete(row.securityId)
            },
          },
          {
            label: this.$t('common.detail'),
            key: 0,
            type: "text",
            State: true,
            method: (row) => {
              this.$refs.add.open("detail", { ...row })
            },
          },
        ]
      },
      tableData: [],
      isSubmitLoading: false,
      total: 0,
      pageSize: 20,
      currentPage: 0,
      searchForm: {
        securityId: ''
      },
      time: [],
      selectIdList: [],
    };
  },
  created () {
    this.fetchData()
  },
  mounted () { },
  methods: {
    search () {
      let that = this
      that.currentPage = 0;
      that.$refs.page.Page(1);
      that.fetchData();
    },
    pageChange (item) {
      this.pageSize = item.limit;
      this.currentPage = item.page - 1;
      this.fetchData();
    },
    // èŽ·å–å¯†é’¥
    async fetchData () {
      let searchForm = removeEmptyValues({ ...this.searchForm })
      searchForm.page = this.currentPage
      searchForm.size = this.pageSize
      try {
        const res = await this.$http.post('/getSecurity', { data: searchForm })
        if (res.code === 200) {
          this.tableData = res.data.content
          this.total = res.data.total
        } else {
          this.$message.error(res.message)
        }
      } catch (even) {
        console.log(even)
      }
    },
    doAdd () {
      this.$refs.add.open('add')
    },
    handleDelete (id) {
      let that = this
      this.$confirm(
        this.$t('common.deleteTips'),
        this.$t('common.tips'),
        {
          confirmButtonText: this.$t('common.confirm'),
          cancelButtonText: this.$t('common.cancel'),
          type: 'warning'
        }
      ).then(async () => {
        try {
          const res = await that.$http.post("/delSecurity", { data: [id] })
          if (res.code == 200) {
            that.$message.success(this.$t('common.deleteSuccess'))
            that.fetchData()
          } else {
            if (res.data && res.data[0]) {
              that.$message.error(res.data[0].errmsg)
            }
          }
        } catch (even) {
          console.log(even)
        }
      }).catch(() => { })
    },
    batchDelete () {
      let that = this
      this.$confirm(this.$t('common.clearTips'),
        this.$t('common.tips'),
        {
          confirmButtonText: this.$t('common.confirm'),
          cancelButtonText: this.$t('common.cancel'),
          type: 'warning'
        })
        .then(async () => {
          that.isSubmitLoading = true;
          const res = await this.$http.post("/clearSecurity", { data: {} });
          that.isSubmitLoading = false;
          if (res.code == 200) {
            that.$message.success(this.$t('common.clearSuccess'))
            that.fetchData()
          } else {
            that.$message.error(res.message)
          }
        })
        .catch(() => {
          return false;
        });
    },
    doReset () {
      resetObjectValues(this.searchForm)
      this.time = []
      this.fetchData()
    },
  },
};
</script>
<style lang="less" scoped>
.el-main {
  .imgDiv {
    width: 100%;
    text-align: center;
    img {
      height: 100%;
    }
  }
}
</style>
face_webserver/face_webserver-ÐÞ¸´2.0.2°æ±¾ºÍÆó΢bug_20260302-ca91388fcbac2fc1005beb7fb78ec5f434495242/vue.config.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
const http = require('http');
// è‡ªå®šä¹‰ä»£ç†ä¸­é—´ä»¶ï¼Œå®Œå…¨æŽ§åˆ¶è¯·æ±‚头
module.exports = {
  devServer: {
    port: 8080,
    open: true,
    before: function(app) {
      app.post('/api/*', (req, res) => {
        const options = {
          hostname: '192.168.10.132',
          port: 8080,
          path: req.url.replace('/api', ''),
          method: 'POST',
          headers: {
            // åªå¤åˆ¶å¿…要的头部,完全模拟Postman
            'Content-Type': req.headers['content-type'],
            'Content-Length': req.headers['content-length'],
            'Host': '192.168.10.132:8080',
            'Connection': 'keep-alive',
            'User-Agent': 'Mozilla/5.0'
          }
        };
        const proxyReq = http.request(options, (proxyRes) => {
          res.writeHead(proxyRes.statusCode, proxyRes.headers);
          proxyRes.pipe(res);
        });
        proxyReq.on('error', (err) => {
          console.error('代理请求失败:', err);
          res.status(500).json({ error: '设备连接失败' });
        });
        req.pipe(proxyReq);
      });
    }
  }
};
vf107/.temp/dxide_debug.log
@@ -1,10 +1,10 @@
[2026/4/11 13:35:44] --- Start runUsb ---
[2026/4/11 13:35:44] Platform: win32
[2026/4/11 13:35:44] Kill command defined: wmic process where "name='node.exe' and commandline like '%device_manager.js%'" delete || powershell -NoProfile -ExecutionPolicy Bypass -Command "Get-CimInstance Win32_Process | Where-Object { $_.Name -eq 'node.exe' -and $_.CommandLine -like '*device_manager.js*' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }"
[2026/4/11 13:35:44] Node.js check passed
[2026/4/11 13:35:45] Process exited with code: 0, signal: null
[2026/4/11 13:35:45] Kill stdout: É¾ï¿½ï¿½Êµï¿½ï¿½ \\ACER-LGQ\ROOT\CIMV2:Win32_Process.Handle="31720"
[2026/4/20 13:31:51] --- Start runUsb ---
[2026/4/20 13:31:51] Platform: win32
[2026/4/20 13:31:51] Kill command defined: wmic process where "name='node.exe' and commandline like '%device_manager.js%'" delete || powershell -NoProfile -ExecutionPolicy Bypass -Command "Get-CimInstance Win32_Process | Where-Object { $_.Name -eq 'node.exe' -and $_.CommandLine -like '*device_manager.js*' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }"
[2026/4/20 13:31:54] Node.js check passed
[2026/4/20 13:31:54] Process exited with code: 0, signal: null
[2026/4/20 13:31:54] Kill stdout: É¾ï¿½ï¿½Êµï¿½ï¿½ \\ACER-LGQ\ROOT\CIMV2:Win32_Process.Handle="39096"
ʵ��ɾ���ɹ���
[2026/4/11 13:35:45] Manager script path: c:\Users\lgq10\.vscode\extensions\dxide.dxide-1.0.41\src\device\device_manager.js
[2026/4/11 13:35:45] Spawning child process...
[2026/4/11 13:35:45] Sending connect command: {"cmd":"connect","lang":"zh","model":"VF105_V12"}
[2026/4/20 13:31:54] Manager script path: c:\Users\lgq10\.vscode\extensions\dxide.dxide-1.0.41\src\device\device_manager.js
[2026/4/20 13:31:54] Spawning child process...
[2026/4/20 13:31:55] Sending connect command: {"cmd":"connect","lang":"zh","model":"VF105_V12"}
vf107/.temp/md5s.json
@@ -45,7 +45,7 @@
  "dxmodules\\dxCommonUtils.js": "9d408cedb29cce9838c10ae5d1a8c2aa",
  "dxmodules\\dxConfig.js": "24072559b3cb1ec5c206cb358eaaaaae",
  "dxmodules\\dxDisplay.js": "5c80ee7de62e3f7b08eed6e3e0a8b3c1",
  "dxmodules\\dxDriver.js": "1e27304f7ecc691ac66293cd065e6a14",
  "dxmodules\\dxDriver.js": "63c8348a2a4a31911ce8a1626aa8f5c4",
  "dxmodules\\dxEventBus.js": "075ac9f2d465c92a9446ea07a6e2bfe6",
  "dxmodules\\dxFacial.js": "08f40369fd06cb3f5f69cd035d4ecab2",
  "dxmodules\\dxFacialBarcode.js": "67508897203cf7382f4bb20c4044dc17",
@@ -63,7 +63,7 @@
  "dxmodules\\dxNfcCard.js": "689161d840c1ee82107e55b3e43da5c3",
  "dxmodules\\dxNtp.js": "54a83064faa67f8f4991e1da56a3caf4",
  "dxmodules\\dxOs.js": "8171120055734e75dd6c878862ae965a",
  "dxmodules\\dxOta.js": "a4bf84aaead7298c5da10161644f2b1f",
  "dxmodules\\dxOta.js": "2dd095485717018eb451e5110aca4934",
  "dxmodules\\dxPwm.js": "d8238b43ef7c0d77a33108290394e12c",
  "dxmodules\\dxQrRule.js": "bfa9e2b4725c4fa358a2988f63c67210",
  "dxmodules\\dxSqliteDB.js": "4762a29fb74d837d43d1d19415f77536",
@@ -2465,7 +2465,7 @@
  "resource\\image\\voiceBroadcast.png": "e6f1a31ba7159962d18b77deef71c106",
  "resource\\image\\wifi.png": "fd668b648ac984ed92fad8e40e151283",
  "resource\\image\\wifi_dark.png": "37505f892ac6a43cb8dc5ea685de9740",
  "resource\\langPack.js": "956b0d902f723e4817319436ee7df117",
  "resource\\langPack.js": "72c5238aa93a1dc6a79b9466b5ebcf44",
  "resource\\wav\\alarm.wav": "fe9d43cfb930f873973cc31fd6e8c132",
  "resource\\wav\\AR\\calibration_1s.wav": "a5ed7bdf10f5c0bec1f964cbaf8d7c59",
  "resource\\wav\\AR\\calibration_2s.wav": "1a533e06dd209b7b87d48947357a0ca0",
@@ -2670,23 +2670,24 @@
  "src\\common\\utils\\stringUtils.js": "425d9cd0390ab8a60f1507544c2cb00a",
  "src\\common\\utils\\tokenUtil.js": "b7abd3698644aadb7d3e936fb6f7fc8b",
  "src\\common\\utils\\utils.js": "bbee074a060e962a570dddc4d586504d",
  "src\\config.json": "6619f6367ac0230030de3131160a2178",
  "src\\controller.js": "954bd6baaf4a7f91b7534f71b0e2365b",
  "src\\driver.js": "42e819e90d7d5b00191447edb06e928c",
  "src\\main.js": "51de780e61133a6f39ba5554dacf7644",
  "src\\screen.js": "b8a256f4f3ba51b4c1e49fcb69e06b28",
  "src\\service\\accessService.js": "304718a9a06ded28947373c3d9a21295",
  "src\\service\\api.js": "def5b52441ccc827913a73478cc7c32a",
  "src\\config.json": "53cf90582fedb3aca8e3ac2848386f36",
  "src\\controller.js": "35dd417593c4510119576717c717e20b",
  "src\\driver.js": "b0c3958d69f7e0021a70d8537077ff5d",
  "src\\main.js": "d455a38100d386fc2eed8bef4c92e7ba",
  "src\\screen.js": "01812eb2816143c98595939d2c54f2ff",
  "src\\service\\accessService.js": "e242b879cf8d1e4f326aee9168e9d4d5",
  "src\\service\\api.js": "24a45cb2acc4306a1f4dd54de559f9f0",
  "src\\service\\codeService.js": "e3f53b983c37a91d85a3de95fab7745d",
  "src\\service\\configService.js": "d1c3c533c1d451c2eaf46758b4efeb19",
  "src\\service\\configService.js": "c45b2005ba236b74be9f04ab76635abf",
  "src\\service\\demo.js": "1f9a570135f63aa5b607743bb5a9e7f4",
  "src\\service\\faceService.js": "cfc3cf7d510f2ba6d6fcedb1251e0795",
  "src\\service\\fingerService.js": "0fee47e46ded411c2da21eb508d8f9ba",
  "src\\service\\gpiokeyService.js": "7aeb81b1577df935f147f3817a0a144b",
  "src\\service\\grainService.js": "3ec28482d55059fd09d091145fad041c",
  "src\\service\\httpService.js": "567d6f52d5392e3d95a54264e631944e",
  "src\\service\\mqttService.js": "eafd4b82b1d5a7d02b32d046bf492ac9",
  "src\\service\\grainService.js": "940788cbc2aea5b20789b752641df6a7",
  "src\\service\\httpService.js": "6c62661a42e45226f3f82df3d7e0a37e",
  "src\\service\\mqttService.js": "b7f598ec25422433bccd821b8b29d5f3",
  "src\\service\\nfcService.js": "fff19d7f08d3a499455ed93a5376c0fd",
  "src\\service\\sqliteService.js": "9dd93a7afd36b50e6be015e950aaff83",
  "src\\service\\sqliteService.js": "f1816a98a378b3ed2fbd759f6965a8df",
  "src\\service\\uartBleService.js": "f8816894598283c7114b4d8708bb65aa",
  "src\\service\\uartCodeService.js": "ed1952645c055885c78bc6c02699906f",
  "src\\service\\weComService.js": "14896c15403107f7225fce6db9b8a049",
@@ -2700,21 +2701,21 @@
  "src\\view\\config\\menu\\deviceInfo\\dataCapacityInfoView.js": "81fbf258e0ba70c126f850658981469e",
  "src\\view\\config\\menu\\deviceInfo\\systemInfoView.js": "95a96c6d8586410be1fe085dfa61db9f",
  "src\\view\\config\\menu\\deviceInfoView.js": "b703a2f9053c1b089294b3dffdf6a449",
  "src\\view\\config\\menu\\doorControlView.js": "2b29eff26537ab92171a40110b8e8c0e",
  "src\\view\\config\\menu\\doorControlView.js": "f2476da5f529cac859dd24776b210bf6",
  "src\\view\\config\\menu\\helpView.js": "b3cf2078efe3a274740276b550b68b63",
  "src\\view\\config\\menu\\localUser\\faceEnterView.js": "f6dc4e020a4eddba902820d4b2444ee3",
  "src\\view\\config\\menu\\localUser\\fingerApplyView.js": "b2b5a46109f7ac768ae6d18131a33fbe",
  "src\\view\\config\\menu\\localUser\\fingerEnterView.js": "46dd45fe0e8d06410a5f2bcbeb7be5ad",
  "src\\view\\config\\menu\\localUser\\localUserAddView.js": "8266626bf9cadde968e2b439ef9073b0",
  "src\\view\\config\\menu\\localUserView.js": "f7881e4a47fe06648d0934f286d59775",
  "src\\view\\config\\menu\\networkSettingView.js": "7cb7b7ef6c1d49f819f509de43e49a56",
  "src\\view\\config\\menu\\recordQuery\\recordQueryDetailView.js": "79e15d726de5146bab15a3dba533a82b",
  "src\\view\\config\\menu\\localUser\\localUserAddView.js": "e9991161a3e5d4344b22f7c4ad3dac4c",
  "src\\view\\config\\menu\\localUserView.js": "2533606f36feba75efe922cc6b6f883a",
  "src\\view\\config\\menu\\networkSettingView.js": "740f7a643a0fb4c3caa3eb85482d85cc",
  "src\\view\\config\\menu\\recordQuery\\recordQueryDetailView.js": "b3ef27a7b02bc59a5c2aa4d977efc30d",
  "src\\view\\config\\menu\\recordQueryView.js": "f640c2f2d560213c44633d7ea36c6960",
  "src\\view\\config\\menu\\systemSetting\\displaySettingView.js": "1a376eaccd07b12bd80773be83e080fc",
  "src\\view\\config\\menu\\systemSetting\\displaySettingView.js": "c92914b05b2353c446fa66c519b95f2f",
  "src\\view\\config\\menu\\systemSetting\\faceRecognitionSettingView.js": "8ac866907bd817d51cde8d757e33b852",
  "src\\view\\config\\menu\\systemSetting\\passLogSettingView.js": "b3fb57b54b5079075c331aceab74a052",
  "src\\view\\config\\menu\\systemSetting\\passwordManagementView.js": "720e32768be124d5591cc0914c9ba340",
  "src\\view\\config\\menu\\systemSetting\\passwordOpenDoorSettingView.js": "a56728c84d37129fbae06c3a23b01092",
  "src\\view\\config\\menu\\systemSetting\\passwordOpenDoorSettingView.js": "36107f1e81acd1f711fcb44b246981a5",
  "src\\view\\config\\menu\\systemSetting\\swipeCardRecognitionSettingView.js": "d23ab8015ac4dc7f316fc7ca4b852f6f",
  "src\\view\\config\\menu\\systemSetting\\timeSettingView.js": "7c5744868ad7e40b3d9ce0708e3f7b85",
  "src\\view\\config\\menu\\systemSettingView.js": "91aa4b58c5da0ba01e7a56ee348fee11",
@@ -2724,7 +2725,7 @@
  "src\\view\\gasDetailView.js": "e7f705f9b89459726064e51d57ed9d7c",
  "src\\view\\i18n.js": "94f43798d35026189125bf2534d1bffd",
  "src\\view\\idleView.js": "a1a6f9fb0ec44c59330a7461298aec46",
  "src\\view\\mainView.js": "0c49e5905a87a5cfd33062df8531e421",
  "src\\view\\mainView.js": "1a0d51f7ece88d821f4ba9938a505cd9",
  "src\\view\\pinyin\\dict.js": "a7812c30b956099fd248271ad6fd5ac9",
  "src\\view\\pinyin\\pinyin.js": "84e7c2ac116f5c22cf07b563ba230c68",
  "src\\view\\pwdView.js": "e46813353af5b4cd9f9b776328aa1987",
@@ -2733,8 +2734,8 @@
  "src\\view\\wechatBindView.js": "6d0239d71de3c1cf4c2e0817cd65ce3f",
  "src\\view\\wechatFaceView.js": "65f5edfbe0ef402c66bd62bd3eebec49",
  "src\\view\\wechatNetView.js": "84d8dd397ed6483a25ab99174e652362",
  "src\\worker\\mqttWorker.js": "2dda93d129f1edd9f8a52aceddb0079d",
  "src\\worker\\netWorker.js": "e38b00c21c77dc5b98425f12068f85ff",
  "src\\worker\\passRecordWorker.js": "ad27fa58b7de2b8155bb3517e601bef4",
  "src\\worker\\mqttWorker.js": "611a50d831add8ca508fc69dbef69905",
  "src\\worker\\netWorker.js": "6ee5ea8dedc2a330974d72a448175d45",
  "src\\worker\\passRecordWorker.js": "8b2ce900d926878f65db8fdc69b1eb3a",
  "src\\worker\\screenWorker.js": "57a6f54f05c57e186e44892c97e36647"
}
vf107/.temp/md5snew.json
@@ -45,7 +45,7 @@
  "dxmodules\\dxCommonUtils.js": "9d408cedb29cce9838c10ae5d1a8c2aa",
  "dxmodules\\dxConfig.js": "24072559b3cb1ec5c206cb358eaaaaae",
  "dxmodules\\dxDisplay.js": "5c80ee7de62e3f7b08eed6e3e0a8b3c1",
  "dxmodules\\dxDriver.js": "1e27304f7ecc691ac66293cd065e6a14",
  "dxmodules\\dxDriver.js": "63c8348a2a4a31911ce8a1626aa8f5c4",
  "dxmodules\\dxEventBus.js": "075ac9f2d465c92a9446ea07a6e2bfe6",
  "dxmodules\\dxFacial.js": "08f40369fd06cb3f5f69cd035d4ecab2",
  "dxmodules\\dxFacialBarcode.js": "67508897203cf7382f4bb20c4044dc17",
@@ -63,7 +63,7 @@
  "dxmodules\\dxNfcCard.js": "689161d840c1ee82107e55b3e43da5c3",
  "dxmodules\\dxNtp.js": "54a83064faa67f8f4991e1da56a3caf4",
  "dxmodules\\dxOs.js": "8171120055734e75dd6c878862ae965a",
  "dxmodules\\dxOta.js": "a4bf84aaead7298c5da10161644f2b1f",
  "dxmodules\\dxOta.js": "2dd095485717018eb451e5110aca4934",
  "dxmodules\\dxPwm.js": "d8238b43ef7c0d77a33108290394e12c",
  "dxmodules\\dxQrRule.js": "bfa9e2b4725c4fa358a2988f63c67210",
  "dxmodules\\dxSqliteDB.js": "4762a29fb74d837d43d1d19415f77536",
@@ -2465,7 +2465,7 @@
  "resource\\image\\voiceBroadcast.png": "e6f1a31ba7159962d18b77deef71c106",
  "resource\\image\\wifi.png": "fd668b648ac984ed92fad8e40e151283",
  "resource\\image\\wifi_dark.png": "37505f892ac6a43cb8dc5ea685de9740",
  "resource\\langPack.js": "956b0d902f723e4817319436ee7df117",
  "resource\\langPack.js": "72c5238aa93a1dc6a79b9466b5ebcf44",
  "resource\\wav\\alarm.wav": "fe9d43cfb930f873973cc31fd6e8c132",
  "resource\\wav\\AR\\calibration_1s.wav": "a5ed7bdf10f5c0bec1f964cbaf8d7c59",
  "resource\\wav\\AR\\calibration_2s.wav": "1a533e06dd209b7b87d48947357a0ca0",
@@ -2670,23 +2670,24 @@
  "src\\common\\utils\\stringUtils.js": "425d9cd0390ab8a60f1507544c2cb00a",
  "src\\common\\utils\\tokenUtil.js": "b7abd3698644aadb7d3e936fb6f7fc8b",
  "src\\common\\utils\\utils.js": "bbee074a060e962a570dddc4d586504d",
  "src\\config.json": "6619f6367ac0230030de3131160a2178",
  "src\\controller.js": "954bd6baaf4a7f91b7534f71b0e2365b",
  "src\\driver.js": "42e819e90d7d5b00191447edb06e928c",
  "src\\main.js": "51de780e61133a6f39ba5554dacf7644",
  "src\\screen.js": "b8a256f4f3ba51b4c1e49fcb69e06b28",
  "src\\service\\accessService.js": "304718a9a06ded28947373c3d9a21295",
  "src\\service\\api.js": "def5b52441ccc827913a73478cc7c32a",
  "src\\config.json": "53cf90582fedb3aca8e3ac2848386f36",
  "src\\controller.js": "35dd417593c4510119576717c717e20b",
  "src\\driver.js": "b0c3958d69f7e0021a70d8537077ff5d",
  "src\\main.js": "d455a38100d386fc2eed8bef4c92e7ba",
  "src\\screen.js": "01812eb2816143c98595939d2c54f2ff",
  "src\\service\\accessService.js": "e242b879cf8d1e4f326aee9168e9d4d5",
  "src\\service\\api.js": "24a45cb2acc4306a1f4dd54de559f9f0",
  "src\\service\\codeService.js": "e3f53b983c37a91d85a3de95fab7745d",
  "src\\service\\configService.js": "d1c3c533c1d451c2eaf46758b4efeb19",
  "src\\service\\configService.js": "c45b2005ba236b74be9f04ab76635abf",
  "src\\service\\demo.js": "1f9a570135f63aa5b607743bb5a9e7f4",
  "src\\service\\faceService.js": "cfc3cf7d510f2ba6d6fcedb1251e0795",
  "src\\service\\fingerService.js": "0fee47e46ded411c2da21eb508d8f9ba",
  "src\\service\\gpiokeyService.js": "7aeb81b1577df935f147f3817a0a144b",
  "src\\service\\grainService.js": "3ec28482d55059fd09d091145fad041c",
  "src\\service\\httpService.js": "567d6f52d5392e3d95a54264e631944e",
  "src\\service\\mqttService.js": "eafd4b82b1d5a7d02b32d046bf492ac9",
  "src\\service\\grainService.js": "940788cbc2aea5b20789b752641df6a7",
  "src\\service\\httpService.js": "6c62661a42e45226f3f82df3d7e0a37e",
  "src\\service\\mqttService.js": "b7f598ec25422433bccd821b8b29d5f3",
  "src\\service\\nfcService.js": "fff19d7f08d3a499455ed93a5376c0fd",
  "src\\service\\sqliteService.js": "9dd93a7afd36b50e6be015e950aaff83",
  "src\\service\\sqliteService.js": "f1816a98a378b3ed2fbd759f6965a8df",
  "src\\service\\uartBleService.js": "f8816894598283c7114b4d8708bb65aa",
  "src\\service\\uartCodeService.js": "ed1952645c055885c78bc6c02699906f",
  "src\\service\\weComService.js": "14896c15403107f7225fce6db9b8a049",
@@ -2700,21 +2701,21 @@
  "src\\view\\config\\menu\\deviceInfo\\dataCapacityInfoView.js": "81fbf258e0ba70c126f850658981469e",
  "src\\view\\config\\menu\\deviceInfo\\systemInfoView.js": "95a96c6d8586410be1fe085dfa61db9f",
  "src\\view\\config\\menu\\deviceInfoView.js": "b703a2f9053c1b089294b3dffdf6a449",
  "src\\view\\config\\menu\\doorControlView.js": "2b29eff26537ab92171a40110b8e8c0e",
  "src\\view\\config\\menu\\doorControlView.js": "f2476da5f529cac859dd24776b210bf6",
  "src\\view\\config\\menu\\helpView.js": "b3cf2078efe3a274740276b550b68b63",
  "src\\view\\config\\menu\\localUser\\faceEnterView.js": "f6dc4e020a4eddba902820d4b2444ee3",
  "src\\view\\config\\menu\\localUser\\fingerApplyView.js": "b2b5a46109f7ac768ae6d18131a33fbe",
  "src\\view\\config\\menu\\localUser\\fingerEnterView.js": "46dd45fe0e8d06410a5f2bcbeb7be5ad",
  "src\\view\\config\\menu\\localUser\\localUserAddView.js": "8266626bf9cadde968e2b439ef9073b0",
  "src\\view\\config\\menu\\localUserView.js": "f7881e4a47fe06648d0934f286d59775",
  "src\\view\\config\\menu\\networkSettingView.js": "7cb7b7ef6c1d49f819f509de43e49a56",
  "src\\view\\config\\menu\\recordQuery\\recordQueryDetailView.js": "79e15d726de5146bab15a3dba533a82b",
  "src\\view\\config\\menu\\localUser\\localUserAddView.js": "e9991161a3e5d4344b22f7c4ad3dac4c",
  "src\\view\\config\\menu\\localUserView.js": "2533606f36feba75efe922cc6b6f883a",
  "src\\view\\config\\menu\\networkSettingView.js": "740f7a643a0fb4c3caa3eb85482d85cc",
  "src\\view\\config\\menu\\recordQuery\\recordQueryDetailView.js": "b3ef27a7b02bc59a5c2aa4d977efc30d",
  "src\\view\\config\\menu\\recordQueryView.js": "f640c2f2d560213c44633d7ea36c6960",
  "src\\view\\config\\menu\\systemSetting\\displaySettingView.js": "1a376eaccd07b12bd80773be83e080fc",
  "src\\view\\config\\menu\\systemSetting\\displaySettingView.js": "c92914b05b2353c446fa66c519b95f2f",
  "src\\view\\config\\menu\\systemSetting\\faceRecognitionSettingView.js": "8ac866907bd817d51cde8d757e33b852",
  "src\\view\\config\\menu\\systemSetting\\passLogSettingView.js": "b3fb57b54b5079075c331aceab74a052",
  "src\\view\\config\\menu\\systemSetting\\passwordManagementView.js": "720e32768be124d5591cc0914c9ba340",
  "src\\view\\config\\menu\\systemSetting\\passwordOpenDoorSettingView.js": "a56728c84d37129fbae06c3a23b01092",
  "src\\view\\config\\menu\\systemSetting\\passwordOpenDoorSettingView.js": "36107f1e81acd1f711fcb44b246981a5",
  "src\\view\\config\\menu\\systemSetting\\swipeCardRecognitionSettingView.js": "d23ab8015ac4dc7f316fc7ca4b852f6f",
  "src\\view\\config\\menu\\systemSetting\\timeSettingView.js": "7c5744868ad7e40b3d9ce0708e3f7b85",
  "src\\view\\config\\menu\\systemSettingView.js": "91aa4b58c5da0ba01e7a56ee348fee11",
@@ -2724,7 +2725,7 @@
  "src\\view\\gasDetailView.js": "e7f705f9b89459726064e51d57ed9d7c",
  "src\\view\\i18n.js": "94f43798d35026189125bf2534d1bffd",
  "src\\view\\idleView.js": "a1a6f9fb0ec44c59330a7461298aec46",
  "src\\view\\mainView.js": "0c49e5905a87a5cfd33062df8531e421",
  "src\\view\\mainView.js": "1a0d51f7ece88d821f4ba9938a505cd9",
  "src\\view\\pinyin\\dict.js": "a7812c30b956099fd248271ad6fd5ac9",
  "src\\view\\pinyin\\pinyin.js": "84e7c2ac116f5c22cf07b563ba230c68",
  "src\\view\\pwdView.js": "e46813353af5b4cd9f9b776328aa1987",
@@ -2733,8 +2734,8 @@
  "src\\view\\wechatBindView.js": "6d0239d71de3c1cf4c2e0817cd65ce3f",
  "src\\view\\wechatFaceView.js": "65f5edfbe0ef402c66bd62bd3eebec49",
  "src\\view\\wechatNetView.js": "84d8dd397ed6483a25ab99174e652362",
  "src\\worker\\mqttWorker.js": "2dda93d129f1edd9f8a52aceddb0079d",
  "src\\worker\\netWorker.js": "e38b00c21c77dc5b98425f12068f85ff",
  "src\\worker\\passRecordWorker.js": "ad27fa58b7de2b8155bb3517e601bef4",
  "src\\worker\\mqttWorker.js": "611a50d831add8ca508fc69dbef69905",
  "src\\worker\\netWorker.js": "6ee5ea8dedc2a330974d72a448175d45",
  "src\\worker\\passRecordWorker.js": "8b2ce900d926878f65db8fdc69b1eb3a",
  "src\\worker\\screenWorker.js": "57a6f54f05c57e186e44892c97e36647"
}
vf107/src/config.json
@@ -139,5 +139,7 @@
    // HTTP接口路径
    "http.safeInputAccess": "http://192.168.1.227:80/cgi-bin/safeInputAccess",
    // æ˜¯å¦å¼€å¯æ°”体浓度验证 1:是 0:否
    "gas.verification": 1
    "gas.verification": 0,
    //是否开启安全入仓联动控制 1:是 0:否
    "safeInputControl": 0
}
vf107/src/service/grainService.js
@@ -13,21 +13,25 @@
const grainService = {}
// ä»Žé…ç½®ä¸­èŽ·å–ä¸šåŠ¡ç¼–ç å®šä¹‰
const functionId = {
function getFunctionId() {
    return {
    gasDetection: config.get('functionId.gasDetection') || "1000",
    safeInputControl: config.get('functionId.safeInputControl') || "2000",
    lightControl: config.get('functionId.lightControl') || "3000",
    doorStatus: config.get('functionId.doorStatus') || "4000"
}
}
// ä»Žé…ç½®ä¸­èŽ·å–æŽ¥å£è¿”å›žç¼–ç 
const respCode = {
    success: config.get('respCode.success') || "200",
function getRespCode() {
    return {
        success: config.get('getRespCode().success') || "200",
    badRequest: config.get('respCode.badRequest') || "400",
    unauthorized: config.get('respCode.unauthorized') || "401",
    forbidden: config.get('respCode.forbidden') || "403",
    notFound: config.get('respCode.notFound') || "404",
    serverError: config.get('respCode.serverError') || "500"
    }
}
// é”™è¯¯ç å¯¹åº”的错误信息
@@ -120,6 +124,17 @@
 * @returns {boolean} true表示气体浓度合格,false表示气体浓度不合格
 */
grainService.checkGasConcentration = function(callback) {
    // æ£€æŸ¥æ˜¯å¦å¼€å¯æ°”体浓度验证
    const isGasDetectionEnabled = config.get('gas.verification') !== 0
    if (!isGasDetectionEnabled) {
        logger.info('[grain]: æ°”体浓度验证未启用,直接返回')
        // è°ƒç”¨å›žè°ƒå‡½æ•°
        if (callback) {
            callback()
        }
        return true
    }
    // ä½¿ç”¨setTimeout将HTTP请求放入后台执行,避免阻塞主线程
    std.setTimeout(() => {
        try {
@@ -138,7 +153,7 @@
                sn: config.get("sys.sn") || " ", // è®¾å¤‡åºåˆ—号,从配置中获取
                houseId: "0000", // ä»“廒编码,默认填充"0000"
                outId: "0000", // è‡ªå®šä¹‰ç¼–码,默认填充"0000"
                functionId: functionId.gasDetection, // æ°”体浓度检测功能码
                functionId: getFunctionId().gasDetection, // æ°”体浓度检测功能码
                timestamp: Date.now().toString() // æ—¶é—´æˆ³
            }
            
@@ -163,7 +178,7 @@
                    logger.info(`[grain]: è§£æžåŽçš„æ°”体浓度数据: ${JSON.stringify(gasData)}`)
                    
                    // æ ¹æ®æŽ¥å£è¿”回编码进行日志输出
                    if (gasData.respCode === respCode.success) {
                    if (gasData.respCode === getRespCode().success) {
                        logger.info(`[grain]: æ°”体浓度检测接口调用成功`)
                    } else {
                        logger.error(`[grain]: æ°”体浓度检测接口调用失败,返回编码: ${gasData.respCode}, è¿”回信息: ${gasData.respMsg}`)
@@ -178,7 +193,7 @@
                        logger.info(`[grain]: æ¸…理后解析的气体浓度数据: ${JSON.stringify(gasData)}`)
                        
                        // æ ¹æ®æŽ¥å£è¿”回编码进行日志输出
                        if (gasData.respCode === respCode.success) {
                        if (gasData.respCode === getRespCode().success) {
                            logger.info(`[grain]: æ°”体浓度检测接口调用成功`)
                        } else {
                            logger.error(`[grain]: æ°”体浓度检测接口调用失败,返回编码: ${gasData.respCode}, è¿”回信息: ${gasData.respMsg}`)
@@ -259,6 +274,13 @@
 * @returns {boolean} true表示状态信息获取成功,false表示失败
 */
grainService.checkDevConcentration = function(params) {
    // æ£€æŸ¥æ˜¯å¦å¼€å¯å®‰å…¨å…¥ä»“联动控制
    const isSafeInputControlEnabled = config.get('safeInputControl') === 1
    if (!isSafeInputControlEnabled) {
        logger.info('[grain]: å®‰å…¨å…¥ä»“联动控制功能未启用')
        return false
    }
    // ä½¿ç”¨setTimeout将HTTP请求放入后台执行,避免阻塞主线程
    std.setTimeout(() => {
        try {
@@ -277,7 +299,7 @@
                sn: config.get("sys.sn") || " ", // è®¾å¤‡åºåˆ—号,从配置中获取
                houseId: "0000", // ä»“廒编码,默认填充"0000"
                outId: "0000", // è‡ªå®šä¹‰ç¼–码,默认填充"0000"
                functionId: params && params.functionId ? params.functionId : functionId.safeInputControl, // åŠŸèƒ½ç ï¼Œé»˜è®¤ä¸ºå®‰å…¨å…¥ä»“è”åŠ¨æŽ§åˆ¶
                functionId: params && params.functionId ? params.functionId : getFunctionId().safeInputControl, // åŠŸèƒ½ç ï¼Œé»˜è®¤ä¸ºå®‰å…¨å…¥ä»“è”åŠ¨æŽ§åˆ¶
                timestamp: Date.now().toString(), // æ—¶é—´æˆ³
                data: {}
            }
@@ -311,7 +333,7 @@
                    logger.info(`[grain]: è§£æžåŽçš„状态信息数据: ${JSON.stringify(statusData)}`)
                    
                    // æ ¹æ®æŽ¥å£è¿”回编码进行日志输出
                    if (statusData.respCode === respCode.success) {
                    if (statusData.respCode === getRespCode().success) {
                        logger.info(`[grain]: çŠ¶æ€ä¿¡æ¯æŽ¥å£è°ƒç”¨æˆåŠŸ`)
                        // æ£€æŸ¥data是否为空,不为空才触发通知
                        if (Object.keys(postData.data).length > 0) {
@@ -364,7 +386,7 @@
                        logger.info(`[grain]: æ¸…理后解析的状态信息数据: ${JSON.stringify(statusData)}`)
                        
                        // æ ¹æ®æŽ¥å£è¿”回编码进行日志输出
                        if (statusData.respCode === respCode.success) {
                        if (statusData.respCode === getRespCode().success) {
                            logger.info(`[grain]: çŠ¶æ€ä¿¡æ¯æŽ¥å£è°ƒç”¨æˆåŠŸ`)
                            // æ£€æŸ¥data是否为空,不为空才触发通知
                            if (Object.keys(postData.data).length > 0) {
@@ -458,7 +480,7 @@
                sn: config.get("sys.sn") || " ", // è®¾å¤‡åºåˆ—号,从配置中获取
                houseId: "0000", // ä»“廒编码,默认填充"0000"
                outId: "0000", // è‡ªå®šä¹‰ç¼–码,默认填充"0000"
                functionId: functionId.lightControl, // ä»“内照明联动控制功能码
                functionId: getFunctionId().lightControl, // ä»“内照明联动控制功能码
                timestamp: Date.now().toString(), // æ—¶é—´æˆ³
                data: {}
            }
@@ -489,7 +511,7 @@
                    logger.info(`[grain]: è§£æžåŽçš„仓内照明联动控制响应数据: ${JSON.stringify(statusData)}`)
                    
                    // æ ¹æ®æŽ¥å£è¿”回编码进行日志输出
                    if (statusData.respCode === respCode.success) {
                    if (statusData.respCode === getRespCode().success) {
                        logger.info(`[grain]: ä»“内照明联动控制接口调用成功`)
                        // è§¦å‘成功弹窗
                        bus.fire('showAccessResult', {
@@ -536,7 +558,7 @@
                        logger.info(`[grain]: æ¸…理后解析的仓内照明联动控制响应数据: ${JSON.stringify(statusData)}`)
                        
                        // æ ¹æ®æŽ¥å£è¿”回编码进行日志输出
                        if (statusData.respCode === respCode.success) {
                        if (statusData.respCode === getRespCode().success) {
                            logger.info(`[grain]: ä»“内照明联动控制接口调用成功`)
                            // è§¦å‘成功弹窗
                            bus.fire('showAccessResult', {
@@ -625,7 +647,7 @@
                    sn: config.get("sys.sn") || " ", // è®¾å¤‡åºåˆ—号,从配置中获取
                    houseId: "0000", // ä»“廒编码,默认填充"0000"
                    outId: "0000", // è‡ªå®šä¹‰ç¼–码,默认填充"0000"
                    functionId: functionId.doorStatus, // é—¨ç£çŠ¶æ€åŠŸèƒ½ç 
                    functionId: getFunctionId().doorStatus, // é—¨ç£çŠ¶æ€åŠŸèƒ½ç 
                    timestamp: Date.now().toString(), // æ—¶é—´æˆ³
                    data: {
                        doorStatus: doorStatusData.status // é—¨ç£çŠ¶æ€
vf107/src/view/config/menu/doorControlView.js
@@ -27,6 +27,7 @@
        onlineCheckingSettingSwitch.select(configAll['mqtt.onlinecheck'] == 1)
        onlineCheckingTimeoutSettingInput.text(configAll['mqtt.timeout'] + '')
        gasVerificationSwitch.select(configAll['gas.verification'] == 1)
        safeInputControlSwitch.select(configAll['safeInputControl'] == 1)
    })
    const titleBox = viewUtils.title(screenMain, configView.screenMain, 'doorControlViewTitle', 'doorControlView.title')
@@ -243,6 +244,25 @@
    gasVerificationSwitch.setSize(screen.screenSize.width * (70 / 600), screen.screenSize.height * (35 / 1280))
    gasVerificationSwitch.bgColor(0x000000, NativeObject.APP.NativeComponents.NativeEnum.LV_PART_INDICATOR | NativeObject.APP.NativeComponents.NativeEnum.LV_STATE_CHECKED)
    // å®‰å…¨å…¥ä»“联动控制设置
    const safeInputControlBox = dxui.View.build('safeInputControlBox', screenMain)
    viewUtils._clearStyle(safeInputControlBox)
    safeInputControlBox.align(dxui.Utils.ALIGN.TOP_MID, 0, screen.screenSize.height * (956 / 1280))
    safeInputControlBox.setSize(screen.screenSize.width * (550 / 600), screen.screenSize.height * (76 / 1280))
    safeInputControlBox.borderWidth(1)
    safeInputControlBox.setBorderColor(0xDEDEDE)
    safeInputControlBox.obj.setStyleBorderSide(dxui.Utils.ENUM.LV_BORDER_SIDE_BOTTOM, 0)
    const safeInputControlLbl = dxui.Label.build('safeInputControlLbl', safeInputControlBox)
    safeInputControlLbl.text('安全入仓联动控制')
    safeInputControlLbl.align(dxui.Utils.ALIGN.LEFT_MID, 0, 0)
    safeInputControlLbl.textFont(viewUtils.font(26))
    const safeInputControlSwitch = dxui.Switch.build('safeInputControlSwitch', safeInputControlBox)
    safeInputControlSwitch.align(dxui.Utils.ALIGN.RIGHT_MID, 0, 0)
    safeInputControlSwitch.setSize(screen.screenSize.width * (70 / 600), screen.screenSize.height * (35 / 1280))
    safeInputControlSwitch.bgColor(0x000000, NativeObject.APP.NativeComponents.NativeEnum.LV_PART_INDICATOR | NativeObject.APP.NativeComponents.NativeEnum.LV_STATE_CHECKED)
    const saveBtn = viewUtils.bottomBtn(screenMain, screenMain.id + 'saveBtn', 'doorControlView.save', () => {
        const saveConfigData = {
            access: {
@@ -257,6 +277,7 @@
            gas: {
                verification: gasVerificationSwitch.isSelect() ? 1 : 0
            },
            safeInputControl: safeInputControlSwitch.isSelect() ? 1 : 0,
            mqtt: {
                addr: mqttSettingInput.text(),
                username: mqttUserSettingInput.text(),
vf107/src/view/mainView.js
@@ -21,7 +21,8 @@
const mainView = {
    authComplete: false, // è®¤è¯æ˜¯å¦å®Œæˆï¼Œç”¨äºŽæŽ§åˆ¶UI更新
    verifiedUsers: {}, // å­˜å‚¨å·²æ ¸éªŒæˆåŠŸçš„ç”¨æˆ·ä¿¡æ¯
    eventListenersRegistered: false // äº‹ä»¶ç›‘听器是否已注册
    eventListenersRegistered: false, // äº‹ä»¶ç›‘听器是否已注册
    resetTimerId: null // å­˜å‚¨é‡ç½®ç”¨æˆ·UI的定时器ID
}
// åŠ è½½ä¸»è§†å›¾çš„æ–¹æ³•
@@ -30,8 +31,8 @@
    // ä¾‹å¦‚更新设备信息、气体浓度数据等
    try {
        // æ›´æ–°è®¾å¤‡ä¿¡æ¯
        let config = screen.getConfig()
        let sn = config["sys.sn"] || ""
        let screenConfig = screen.getConfig()
        let sn = screenConfig["sys.sn"] || ""
        // ç›´æŽ¥ä»Žnet模块获取IP地址,确保获取到最新的IP
        let ip = ""
        try {
@@ -41,8 +42,8 @@
                ip = param.ip
            }
        } catch (error) {
            // å‡ºé”™æ—¶ä½¿ç”¨config中的IP
            ip = config["net.ip"] || ""
            // å‡ºé”™æ—¶ä½¿ç”¨screenConfig中的IP
            ip = screenConfig["net.ip"] || ""
        }
        // é€šè¿‡mainView对象访问标签
        if (mainView.snInfoLbl && mainView.ipInfoLbl) {
@@ -81,8 +82,8 @@
    // æ›´æ–°è®¾å¤‡ä¿¡æ¯ï¼ˆSN和IP)
    function updateDeviceInfo() {
        let config = screen.getConfig()
        let sn = config["sys.sn"] || ""
        let screenConfig = screen.getConfig()
        let sn = screenConfig["sys.sn"] || ""
        // ç›´æŽ¥ä»Žnet模块获取IP地址,确保获取到最新的IP
        let ip = ""
        try {
@@ -92,8 +93,8 @@
                ip = param.ip
            }
        } catch (error) {
            // å‡ºé”™æ—¶ä½¿ç”¨config中的IP
            ip = config["net.ip"] || ""
            // å‡ºé”™æ—¶ä½¿ç”¨screenConfig中的IP
            ip = screenConfig["net.ip"] || ""
        }
        // é€šè¿‡mainView对象访问标签
        if (mainView.snInfoLbl && mainView.ipInfoLbl) {
@@ -109,11 +110,11 @@
    // æ›´æ–°åº“区名称和仓号(只在程序启动和配置修改时调用)
    function updateWarehouseInfo() {
        let config = screen.getConfig()
        let screenConfig = screen.getConfig()
        // èŽ·å–ä»“å·ä¿¡æ¯
        let houseName = config["houseName"] || "01号仓"
        let houseName = screenConfig["houseName"] || "01号仓"
        // èŽ·å–åº“åŒºåç§°
        let GranaryName = config["GranaryName"] || "中央储备粮某某直属库"
        let GranaryName = screenConfig["GranaryName"] || "中央储备粮某某直属库"
        logger.info(`[mainView]: æ›´æ–°åº“区名称和仓号: ä»“号=${houseName}, åº“区名称=${GranaryName}`)
        // æ›´æ–°é¡¶éƒ¨æ ‡é¢˜æ çš„库区名称
        if (mainView.headerLbl) {
@@ -131,8 +132,8 @@
    // æ›´æ–°ç½‘络图标
    function updateNetworkIcon() {
        try {
            let config = screen.getConfig()
            let netType = config["net.type"] || 1
            let screenConfig = screen.getConfig()
            let netType = screenConfig["net.type"] || 1
            let isConnected = net.isConnected()
            
            // éšè—æ‰€æœ‰ç½‘络图标
@@ -293,9 +294,9 @@
                logger.info('[mainView]: accessRes事件触发, result=' + result + ', data=' + JSON.stringify(data))
                
                // æ¸…除之前的定时器
                if (resetTimerId) {
                    std.clearTimeout(resetTimerId)
                    resetTimerId = null
                if (mainView.resetTimerId) {
                    std.clearTimeout(mainView.resetTimerId)
                    mainView.resetTimerId = null
                    logger.info('[mainView]: æ¸…除之前的重置定时器')
                }
                
@@ -415,7 +416,7 @@
                }
                
                // è®¾ç½®1分钟后重置用户UI的定时器
                resetTimerId = std.setTimeout(() => {
                mainView.resetTimerId = std.setTimeout(() => {
                    logger.info('[mainView]: 1分钟定时器触发,重置用户UI')
                    // è§¦å‘通行解锁完成事件,通知UI重置
                    bus.fire("accessUnlockComplete")
@@ -439,6 +440,11 @@
                            let dualAuthInfo = data.dualAuthInfo
                            logger.info('[mainView]: åŒäººè®¤è¯æˆåŠŸï¼Œæ›´æ–°ç”¨æˆ·UI, user1=' + dualAuthInfo.firstUserId + ', user2=' + dualAuthInfo.secondUserId)
                            
                            // å­˜å‚¨å·²æ ¸éªŒæˆåŠŸçš„ç”¨æˆ·ä¿¡æ¯
                            mainView.verifiedUsers[1] = dualAuthInfo.firstUserId
                            mainView.verifiedUsers[2] = dualAuthInfo.secondUserId
                            logger.info('[mainView]: å­˜å‚¨åŒäººè®¤è¯ç”¨æˆ·ä¿¡æ¯, user1=' + dualAuthInfo.firstUserId + ', user2=' + dualAuthInfo.secondUserId)
                            // æ›´æ–°ç”¨æˆ·1的UI
                            mainView.updateUserUI(1, dualAuthInfo.firstUserId, true, data.firstUserFileName || fileName)
                            
@@ -449,12 +455,21 @@
                            let dualAuthInfo = data.dualAuthInfo
                            logger.info('[mainView]: ç¬¬ä¸€ç”¨æˆ·è®¤è¯æˆåŠŸï¼Œç­‰å¾…ç¬¬äºŒç”¨æˆ·è®¤è¯ï¼Œæ›´æ–°ç”¨æˆ·UI, user1=' + dualAuthInfo.firstUserId)
                            
                            // å­˜å‚¨å·²æ ¸éªŒæˆåŠŸçš„ç”¨æˆ·ä¿¡æ¯
                            mainView.verifiedUsers[1] = dualAuthInfo.firstUserId
                            logger.info('[mainView]: å­˜å‚¨ç¬¬ä¸€ç”¨æˆ·è®¤è¯ä¿¡æ¯, user1=' + dualAuthInfo.firstUserId)
                            // æ›´æ–°ç”¨æˆ·1的UI
                            mainView.updateUserUI(1, dualAuthInfo.firstUserId, true, data.firstUserFileName || fileName)
                        } else {
                            // å•人认证,更新用户1的UI
                            if (data.userId) {
                                logger.info('[mainView]: å•人认证成功,更新用户UI, userId=' + data.userId)
                                // å­˜å‚¨å·²æ ¸éªŒæˆåŠŸçš„ç”¨æˆ·ä¿¡æ¯
                                mainView.verifiedUsers[1] = data.userId
                                logger.info('[mainView]: å­˜å‚¨å•人认证用户信息, user1=' + data.userId)
                                mainView.updateUserUI(1, data.userId, true, fileName)
                            }
                        }
@@ -653,8 +668,7 @@
            logger.info('[mainView]: äº‹ä»¶ç›‘听器注册完成')
        }
        // å­˜å‚¨å®šæ—¶å™¨ID
        let resetTimerId = null
        // èŽ·å–æ°”ä½“æµ“åº¦å’ŒçŠ¶æ€ä¿¡æ¯
        grainService.checkGasConcentration()
@@ -785,8 +799,8 @@
        const headerLbl = dxui.Label.build('headerLbl', headerBox)
        mainView.headerLbl = headerLbl
        // ä»Žé…ç½®ä¸­èŽ·å–åº“åŒºåç§°
        const config = screen.getConfig()
        const GranaryName = config['GranaryName'] || '中央储备粮某某直属库'
        const screenConfig = screen.getConfig()
        const GranaryName = screenConfig['GranaryName'] || '中央储备粮某某直属库'
        headerLbl.text(GranaryName)
        headerLbl.textFont(viewUtils.font(30)) // å¢žå¤§å­—体
        headerLbl.textColor(0xffffff)
@@ -795,7 +809,7 @@
        const warehouseLbl = dxui.Label.build('warehouseLbl', overlayBox)
        mainView.warehouseLbl = warehouseLbl
        // ä»Žé…ç½®ä¸­èŽ·å–ä»“å·ä¿¡æ¯
        const houseName = config['houseName'] || '01号仓'
        const houseName = screenConfig['houseName'] || '01号仓'
        warehouseLbl.text(houseName)
        warehouseLbl.textFont(viewUtils.font(30, dxui.Utils.FONT_STYLE.BOLD))
        warehouseLbl.textColor(0x000000)
@@ -1385,6 +1399,18 @@
        // è®¾ç½®æŒ‰é’®ç‚¹å‡»äº‹ä»¶
        mode1Btn.on(dxui.Utils.EVENT.CLICK, () => {
            logger.info('允许进仓模式按钮点击')
            const isSafeInputControlEnabled = config.get('safeInputControl') === 1
            if (!isSafeInputControlEnabled) {
                logger.info('[grain]: å®‰å…¨å…¥ä»“联动控制功能未启用')
                // è§¦å‘未启用弹窗
                bus.fire('showAccessResult', {
                faceAuth: true,
                gasConcentration: true,
                accessAllowed: false,
                message: "*安全入仓联动控制功能未启用*"
                })
                return false
            }
            // æ£€æŸ¥æ˜¯å¦æœ‰ç”¨æˆ·å·²æ ¸éªŒæˆåŠŸ
            if (!mainView.verifiedUsers[1] && !mainView.verifiedUsers[2]) {
                // æ˜¾ç¤ºå¼¹çª—通知
@@ -1410,6 +1436,18 @@
    
        inBtn.on(dxui.Utils.EVENT.CLICK, () => {
            logger.info('入仓按钮点击')
            const isSafeInputControlEnabled = config.get('safeInputControl') === 1
            if (!isSafeInputControlEnabled) {
                logger.info('[grain]: å®‰å…¨å…¥ä»“联动控制功能未启用')
                // è§¦å‘未启用弹窗
                bus.fire('showAccessResult', {
                faceAuth: true,
                gasConcentration: true,
                accessAllowed: false,
                message: "*安全入仓联动控制功能未启用*"
                })
                return false
            }
            // æ£€æŸ¥æ˜¯å¦æœ‰ç”¨æˆ·å·²æ ¸éªŒæˆåŠŸ
            if (!mainView.verifiedUsers[1] && !mainView.verifiedUsers[2]) {
                // æ˜¾ç¤ºå¼¹çª—通知
@@ -1436,6 +1474,18 @@
    
        outBtn.on(dxui.Utils.EVENT.CLICK, () => {
            logger.info('出仓按钮点击')
            const isSafeInputControlEnabled = config.get('safeInputControl') === 1
            if (!isSafeInputControlEnabled) {
                logger.info('[grain]: å®‰å…¨å…¥ä»“联动控制功能未启用')
                // è§¦å‘未启用弹窗
                bus.fire('showAccessResult', {
                faceAuth: true,
                gasConcentration: true,
                accessAllowed: false,
                message: "*安全入仓联动控制功能未启用*"
                })
                return false
            }
            // æ£€æŸ¥æ˜¯å¦æœ‰ç”¨æˆ·å·²æ ¸éªŒæˆåŠŸ
            if (!mainView.verifiedUsers[1] && !mainView.verifiedUsers[2]) {
                // æ˜¾ç¤ºå¼¹çª—通知
@@ -1462,6 +1512,18 @@
    
        mode2Btn.on(dxui.Utils.EVENT.CLICK, () => {
            logger.info('冬季通风模式按钮点击')
            const isSafeInputControlEnabled = config.get('safeInputControl') === 1
            if (!isSafeInputControlEnabled) {
                logger.info('[grain]: å®‰å…¨å…¥ä»“联动控制功能未启用')
                // è§¦å‘未启用弹窗
                bus.fire('showAccessResult', {
                faceAuth: true,
                gasConcentration: true,
                accessAllowed: false,
                message: "*安全入仓联动控制功能未启用*"
                })
                return false
            }
            // æ£€æŸ¥æ˜¯å¦æœ‰ç”¨æˆ·å·²æ ¸éªŒæˆåŠŸ
            if (!mainView.verifiedUsers[1] && !mainView.verifiedUsers[2]) {
                // æ˜¾ç¤ºå¼¹çª—通知
@@ -1470,6 +1532,49 @@
                    gasConcentration: true,
                    accessAllowed: false,
                    message: "*联动控制操作无权限*"
                })
                // æ’­æ”¾è¯­éŸ³æç¤º
                driver.audio.play('/app/code/resource/CN/wav/control_f.wav')
                return
            }
            // æ£€æŸ¥æ˜¯å¦æœ‰ç§‘长权限
            let hasSectionChief = false
            // æ£€æŸ¥ç”¨æˆ·1
            if (mainView.verifiedUsers[1]) {
                let user1 = sqliteService.d1_person.find({ userId: mainView.verifiedUsers[1] })
                if (user1 && user1.length > 0) {
                    try {
                        let userType = JSON.parse(user1[0].extra).type || 0
                        if (userType === 1) {
                            hasSectionChief = true
                        }
                    } catch (error) {
                        logger.error("解析用户1类型失败")
                    }
                }
            }
            // æ£€æŸ¥ç”¨æˆ·2
            if (!hasSectionChief && mainView.verifiedUsers[2]) {
                let user2 = sqliteService.d1_person.find({ userId: mainView.verifiedUsers[2] })
                if (user2 && user2.length > 0) {
                    try {
                        let userType = JSON.parse(user2[0].extra).type || 0
                        if (userType === 1) {
                            hasSectionChief = true
                        }
                    } catch (error) {
                        logger.error("解析用户2类型失败")
                    }
                }
            }
            // å¦‚果没有科长权限,显示核验失败
            if (!hasSectionChief) {
                // æ˜¾ç¤ºå¼¹çª—通知
                bus.fire('showAccessResult', {
                    faceAuth: false,
                    gasConcentration: true,
                    accessAllowed: false,
                    message: "*无科长权限,禁止操作*"
                })
                // æ’­æ”¾è¯­éŸ³æç¤º
                driver.audio.play('/app/code/resource/CN/wav/control_f.wav')
@@ -1487,6 +1592,18 @@
    
        startBtn.on(dxui.Utils.EVENT.CLICK, () => {
            logger.info('启动按钮点击')
            const isSafeInputControlEnabled = config.get('safeInputControl') === 1
            if (!isSafeInputControlEnabled) {
                logger.info('[grain]: å®‰å…¨å…¥ä»“联动控制功能未启用')
                // è§¦å‘未启用弹窗
                bus.fire('showAccessResult', {
                faceAuth: true,
                gasConcentration: true,
                accessAllowed: false,
                message: "*安全入仓联动控制功能未启用*"
                })
                return false
            }
            // æ£€æŸ¥æ˜¯å¦æœ‰ç”¨æˆ·å·²æ ¸éªŒæˆåŠŸ
            if (!mainView.verifiedUsers[1] && !mainView.verifiedUsers[2]) {
                // æ˜¾ç¤ºå¼¹çª—通知
@@ -1513,6 +1630,18 @@
    
        stopBtn.on(dxui.Utils.EVENT.CLICK, () => {
            logger.info('关闭按钮点击')
            const isSafeInputControlEnabled = config.get('safeInputControl') === 1
            if (!isSafeInputControlEnabled) {
                logger.info('[grain]: å®‰å…¨å…¥ä»“联动控制功能未启用')
                // è§¦å‘未启用弹窗
                bus.fire('showAccessResult', {
                faceAuth: true,
                gasConcentration: true,
                accessAllowed: false,
                message: "*安全入仓联动控制功能未启用*"
                })
                return false
            }
            // æ£€æŸ¥æ˜¯å¦æœ‰ç”¨æˆ·å·²æ ¸éªŒæˆåŠŸ
            if (!mainView.verifiedUsers[1] && !mainView.verifiedUsers[2]) {
                // æ˜¾ç¤ºå¼¹çª—通知
@@ -1539,6 +1668,18 @@
    
        mode3Btn.on(dxui.Utils.EVENT.CLICK, () => {
            logger.info('禁止进仓模式按钮点击')
            const isSafeInputControlEnabled = config.get('safeInputControl') === 1
            if (!isSafeInputControlEnabled) {
                logger.info('[grain]: å®‰å…¨å…¥ä»“联动控制功能未启用')
                // è§¦å‘未启用弹窗
                bus.fire('showAccessResult', {
                faceAuth: true,
                gasConcentration: true,
                accessAllowed: false,
                message: "*安全入仓联动控制功能未启用*"
                })
                return false
            }
            // æ£€æŸ¥æ˜¯å¦æœ‰ç”¨æˆ·å·²æ ¸éªŒæˆåŠŸ
            if (!mainView.verifiedUsers[1] && !mainView.verifiedUsers[2]) {
                // æ˜¾ç¤ºå¼¹çª—通知
@@ -1547,6 +1688,49 @@
                    gasConcentration: true,
                    accessAllowed: false,
                    message: "*联动控制操作无权限*"
                })
                // æ’­æ”¾è¯­éŸ³æç¤º
                driver.audio.play('/app/code/resource/CN/wav/control_f.wav')
                return
            }
            // æ£€æŸ¥æ˜¯å¦æœ‰ç§‘长权限
            let hasSectionChief = false
            // æ£€æŸ¥ç”¨æˆ·1
            if (mainView.verifiedUsers[1]) {
                let user1 = sqliteService.d1_person.find({ userId: mainView.verifiedUsers[1] })
                if (user1 && user1.length > 0) {
                    try {
                        let userType = JSON.parse(user1[0].extra).type || 0
                        if (userType === 1) {
                            hasSectionChief = true
                        }
                    } catch (error) {
                        logger.error("解析用户1类型失败")
                    }
                }
            }
            // æ£€æŸ¥ç”¨æˆ·2
            if (!hasSectionChief && mainView.verifiedUsers[2]) {
                let user2 = sqliteService.d1_person.find({ userId: mainView.verifiedUsers[2] })
                if (user2 && user2.length > 0) {
                    try {
                        let userType = JSON.parse(user2[0].extra).type || 0
                        if (userType === 1) {
                            hasSectionChief = true
                        }
                    } catch (error) {
                        logger.error("解析用户2类型失败")
                    }
                }
            }
            // å¦‚果没有科长权限,显示核验失败
            if (!hasSectionChief) {
                // æ˜¾ç¤ºå¼¹çª—通知
                bus.fire('showAccessResult', {
                    faceAuth: false,
                    gasConcentration: true,
                    accessAllowed: false,
                    message: "*无科长权限,禁止操作*"
                })
                // æ’­æ”¾è¯­éŸ³æç¤º
                driver.audio.play('/app/code/resource/CN/wav/control_f.wav')
@@ -1564,6 +1748,18 @@
    
        emergencyInBtn.on(dxui.Utils.EVENT.CLICK, () => {
            logger.info('紧急入仓按钮点击')
            const isSafeInputControlEnabled = config.get('safeInputControl') === 1
            if (!isSafeInputControlEnabled) {
                logger.info('[grain]: å®‰å…¨å…¥ä»“联动控制功能未启用')
                // è§¦å‘未启用弹窗
                bus.fire('showAccessResult', {
                faceAuth: true,
                gasConcentration: true,
                accessAllowed: false,
                message: "*安全入仓联动控制功能未启用*"
                })
                return false
            }
            // æ£€æŸ¥æ˜¯å¦æœ‰ç”¨æˆ·å·²æ ¸éªŒæˆåŠŸ
            if (!mainView.verifiedUsers[1] && !mainView.verifiedUsers[2]) {
                // æ˜¾ç¤ºå¼¹çª—通知
@@ -1590,6 +1786,18 @@
    
        emergencyOutBtn.on(dxui.Utils.EVENT.CLICK, () => {
            logger.info('出仓按钮点击')
            const isSafeInputControlEnabled = config.get('safeInputControl') === 1
            if (!isSafeInputControlEnabled) {
                logger.info('[grain]: å®‰å…¨å…¥ä»“联动控制功能未启用')
                // è§¦å‘未启用弹窗
                bus.fire('showAccessResult', {
                faceAuth: true,
                gasConcentration: true,
                accessAllowed: false,
                message: "*安全入仓联动控制功能未启用*"
                })
                return false
            }
            // æ£€æŸ¥æ˜¯å¦æœ‰ç”¨æˆ·å·²æ ¸éªŒæˆåŠŸ
            if (!mainView.verifiedUsers[1] && !mainView.verifiedUsers[2]) {
                // æ˜¾ç¤ºå¼¹çª—通知
@@ -1616,6 +1824,18 @@
    
        lightOnBtn.on(dxui.Utils.EVENT.CLICK, () => {
            logger.info('开灯按钮点击')
            const isSafeInputControlEnabled = config.get('safeInputControl') === 1
            if (!isSafeInputControlEnabled) {
                logger.info('[grain]: å®‰å…¨å…¥ä»“联动控制功能未启用')
                // è§¦å‘未启用弹窗
                bus.fire('showAccessResult', {
                faceAuth: true,
                gasConcentration: true,
                accessAllowed: false,
                message: "*安全入仓联动控制功能未启用*"
                })
                return false
            }
            // æ£€æŸ¥æ˜¯å¦æœ‰ç”¨æˆ·å·²æ ¸éªŒæˆåŠŸ
            if (!mainView.verifiedUsers[1] && !mainView.verifiedUsers[2]) {
                // æ˜¾ç¤ºå¼¹çª—通知
@@ -1642,6 +1862,18 @@
    
        lightOffBtn.on(dxui.Utils.EVENT.CLICK, () => {
            logger.info('关灯按钮点击')
            const isSafeInputControlEnabled = config.get('safeInputControl') === 1
            if (!isSafeInputControlEnabled) {
                logger.info('[grain]: å®‰å…¨å…¥ä»“联动控制功能未启用')
                // è§¦å‘未启用弹窗
                bus.fire('showAccessResult', {
                faceAuth: true,
                gasConcentration: true,
                accessAllowed: false,
                message: "*安全入仓联动控制功能未启用*"
                })
                return false
            }
            // æ£€æŸ¥æ˜¯å¦æœ‰ç”¨æˆ·å·²æ ¸éªŒæˆåŠŸ
            if (!mainView.verifiedUsers[1] && !mainView.verifiedUsers[2]) {
                // æ˜¾ç¤ºå¼¹çª—通知