diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 36b56de..0000000 --- a/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.vscode -.idea -.vite \ No newline at end of file diff --git a/server/.gitignore b/server/.gitignore index 49330ee..6601ee8 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -51,4 +51,4 @@ rebel.xml application-my.yaml /yudao-ui-app/unpackage/ -**/.DS_Store +**/.DS_Store \ No newline at end of file diff --git a/web/.env.dev.tauri b/web/.env.dev.tauri new file mode 100644 index 0000000..e5cc71b --- /dev/null +++ b/web/.env.dev.tauri @@ -0,0 +1,30 @@ +NODE_ENV=production + +VITE_DEV=true + +# 请求路径 +VITE_BASE_URL='https://lock.zdhlcn.com:9807' + +# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务 +VITE_UPLOAD_TYPE=server + +# 接口地址 +VITE_API_URL=/admin-api + +# 是否删除debugger +VITE_DROP_DEBUGGER=false + +# 是否删除console.log +VITE_DROP_CONSOLE=false + +# 是否sourcemap +VITE_SOURCEMAP=true + +# 打包路径 +VITE_BASE_PATH=/ + +# 输出路径 +VITE_OUT_DIR=dist + +# TAURI启动标记 +VITE_IS_TAURI=true \ No newline at end of file diff --git a/web/.env.prod b/web/.env.prod index c496724..00dc78d 100644 --- a/web/.env.prod +++ b/web/.env.prod @@ -15,7 +15,7 @@ VITE_API_URL=/admin-api VITE_DROP_DEBUGGER=true # 是否删除console.log -VITE_DROP_CONSOLE=false +VITE_DROP_CONSOLE=true # 是否sourcemap VITE_SOURCEMAP=false diff --git a/web/.env.test b/web/.env.prod.tauri similarity index 78% rename from web/.env.test rename to web/.env.prod.tauri index 45f9735..067665d 100644 --- a/web/.env.test +++ b/web/.env.prod.tauri @@ -3,7 +3,7 @@ NODE_ENV=production VITE_DEV=false # 请求路径 -VITE_BASE_URL='' +VITE_BASE_URL='https://lock.zdhlcn.com:9807' # 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务 VITE_UPLOAD_TYPE=server @@ -24,4 +24,7 @@ VITE_SOURCEMAP=false VITE_BASE_PATH=/ # 输出路径 -VITE_OUT_DIR=dist-test +VITE_OUT_DIR=dist-prod + +# TAURI启动标记 +VITE_IS_TAURI=true \ No newline at end of file diff --git a/web/.eslintrc.js b/web/.eslintrc.js index b28255c..4e7041d 100644 --- a/web/.eslintrc.js +++ b/web/.eslintrc.js @@ -17,6 +17,12 @@ module.exports = defineConfig({ jsx: true } }, + ignorePatterns: [ + 'src/components/Tinyflow/**/*.js', + 'src/components/Tinyflow/**/*.umd.js', + 'src/types/auto-imports.d.ts', + 'src/types/auto-components.d.ts' + ], extends: [ 'plugin:vue/vue3-recommended', 'plugin:@typescript-eslint/recommended', diff --git a/web/.gitignore b/web/.gitignore index 848638a..2e44b2e 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -7,3 +7,6 @@ pnpm-debug auto-*.d.ts .idea .history +pnpm-lock.yaml +.vite +.vscode \ No newline at end of file diff --git a/web/.vscode/extensions.json b/web/.vscode/extensions.json deleted file mode 100644 index 65288b5..0000000 --- a/web/.vscode/extensions.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "recommendations": [ - "christian-kohler.path-intellisense", - "vscode-icons-team.vscode-icons", - "davidanson.vscode-markdownlint", - "dbaeumer.vscode-eslint", - "esbenp.prettier-vscode", - "mrmlnc.vscode-less", - "lokalise.i18n-ally", - "redhat.vscode-yaml", - "csstools.postcss", - "mikestead.dotenv", - "eamodio.gitlens", - "antfu.iconify", - "antfu.unocss", - "Vue.volar" - ] -} diff --git a/web/.vscode/launch.json b/web/.vscode/launch.json deleted file mode 100644 index f43edc0..0000000 --- a/web/.vscode/launch.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "msedge", - "request": "launch", - "name": "Launch Edge against localhost", - "url": "http://localhost", - "webRoot": "${workspaceFolder}/src", - "sourceMaps": true - } - ] -} diff --git a/web/.vscode/settings.json b/web/.vscode/settings.json deleted file mode 100644 index 9b7b553..0000000 --- a/web/.vscode/settings.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "typescript.tsdk": "node_modules/typescript/lib", - "npm.packageManager": "pnpm", - "editor.tabSize": 2, - "prettier.printWidth": 100, // 超过最大值换行 - "editor.defaultFormatter": "esbenp.prettier-vscode", - "files.eol": "\n", - "search.exclude": { - "**/node_modules": true, - "**/*.log": true, - "**/*.log*": true, - "**/bower_components": true, - "**/dist": true, - "**/elehukouben": true, - "**/.git": true, - "**/.gitignore": true, - "**/.svn": true, - "**/.DS_Store": true, - "**/.idea": true, - "**/.vscode": false, - "**/yarn.lock": true, - "**/tmp": true, - "out": true, - "dist": true, - "node_modules": true, - "CHANGELOG.md": true, - "examples": true, - "res": true, - "screenshots": true, - "yarn-error.log": true, - "**/.yarn": true - }, - "files.exclude": { - "**/.cache": true, - "**/.editorconfig": true, - "**/.eslintcache": true, - "**/bower_components": true, - "**/.idea": true, - "**/tmp": true, - "**/.git": true, - "**/.svn": true, - "**/.hg": true, - "**/CVS": true, - "**/.DS_Store": true - }, - "files.watcherExclude": { - "**/.git/objects/**": true, - "**/.git/subtree-cache/**": true, - "**/.vscode/**": true, - "**/node_modules/**": true, - "**/tmp/**": true, - "**/bower_components/**": true, - "**/dist/**": true, - "**/yarn.lock": true - }, - "stylelint.enable": true, - "stylelint.validate": ["css", "less", "postcss", "scss", "vue", "sass"], - "path-intellisense.mappings": { - "@/": "${workspaceRoot}/src" - }, - "[javascriptreact]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[typescriptreact]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[html]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[css]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[less]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[scss]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[markdown]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "editor.codeActionsOnSave": { - "source.fixAll.eslint": "explicit", - "source.fixAll.stylelint": "explicit" - }, - "editor.formatOnSave": true, - "[vue]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "i18n-ally.localesPaths": ["src/locales"], - "i18n-ally.keystyle": "nested", - "i18n-ally.sortKeys": true, - "i18n-ally.namespace": false, - "i18n-ally.enabledParsers": ["ts"], - "i18n-ally.sourceLanguage": "en", - "i18n-ally.displayLanguage": "zh-CN", - "i18n-ally.enabledFrameworks": ["vue", "react"], - "cSpell.words": [ - "brotli", - "browserslist", - "codemirror", - "commitlint", - "cropperjs", - "echart", - "echarts", - "esnext", - "esno", - "iconify", - "INTLIFY", - "lintstagedrc", - "logicflow", - "nprogress", - "pinia", - "pnpm", - "qrcode", - "sider", - "sortablejs", - "stylelint", - "svgs", - "unocss", - "unplugin", - "unref", - "videojs", - "VITE", - "vitejs", - "vueuse", - "wangeditor", - "zxcvbn" - ], - // 控制相关文件嵌套展示 - "explorer.fileNesting.enabled": true, - "explorer.fileNesting.expand": false, - "explorer.fileNesting.patterns": { - "*.ts": "$(capture).test.ts, $(capture).test.tsx", - "*.tsx": "$(capture).test.ts, $(capture).test.tsx", - "*.env": "$(capture).env.*", - "package.json": "pnpm-lock.yaml,yarn.lock,LICENSE,README*,CHANGELOG*,CNAME,.gitattributes,.eslintrc-auto-import.json,.gitignore,prettier.config.js,stylelint.config.js,commitlint.config.js,.stylelintignore,.prettierignore,.gitpod.yml,.eslintrc.js,.eslintignore" - }, - "terminal.integrated.scrollback": 10000, - "nuxt.isNuxtApp": false -} diff --git a/web/package.json b/web/package.json index 45e16e1..6271f14 100644 --- a/web/package.json +++ b/web/package.json @@ -1,26 +1,26 @@ { - "name": "yudao-ui-admin-vue3", - "version": "2.6.1-snapshot", - "description": "基于vue3、vite4、element-plus、typesScript", - "author": "xingyu", - "private": false, + "name": "ElectronicLock", + "version": "0.0.1", + "private": true, "scripts": { "i": "pnpm install", "dev": "vite --mode dev", "ts:check": "vue-tsc --noEmit", - "build:local": "node --max_old_space_size=4096 ./node_modules/vite/bin/vite.js build", - "build:dev": "node --max_old_space_size=4096 ./node_modules/vite/bin/vite.js build --mode dev", - "build:test": "node --max_old_space_size=4096 ./node_modules/vite/bin/vite.js build --mode test", - "build:prod": "node --max_old_space_size=4096 ./node_modules/vite/bin/vite.js build --mode prod", + "build:dev": "vite build --mode dev", + "build:prod": "vite build --mode prod", "serve:dev": "vite preview --mode dev", "serve:prod": "vite preview --mode prod", - "preview": "pnpm build:local && vite preview", + "preview": "pnpm build:dev && vite preview", "clean": "npx rimraf node_modules", "clean:cache": "npx rimraf node_modules/.cache", "lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src", "lint:format": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,scss,vue,html,md}\"", "lint:style": "stylelint --fix \"./src/**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/", - "lint:lint-staged": "lint-staged -c " + "dev:tauri": "vite --mode dev.tauri", + "build:tauri": "vite build --mode prod.tauri", + "tauri:dev": "tauri dev", + "tauri:build": "tauri build", + "tauri:icon": "tauri icon" }, "dependencies": { "@element-plus/icons-vue": "^2.1.0", @@ -86,6 +86,7 @@ "@iconify/json": "^2.2.187", "@intlify/unplugin-vue-i18n": "^2.0.0", "@purge-icons/generated": "^0.9.0", + "@tauri-apps/cli": "^2.8.1", "@types/lodash-es": "^4.17.12", "@types/node": "^20.11.21", "@types/nprogress": "^0.2.3", @@ -139,18 +140,9 @@ "vue-eslint-parser": "^9.3.2", "vue-tsc": "^1.8.27" }, - "license": "MIT", - "repository": { - "type": "git", - "url": "git+https://gitee.com/yudaocode/yudao-ui-admin-vue3" - }, - "bugs": { - "url": "https://gitee.com/yudaocode/yudao-ui-admin-vue3/issues" - }, - "homepage": "https://gitee.com/yudaocode/yudao-ui-admin-vue3", "web-types": "./web-types.json", "engines": { "node": ">= 16.0.0", "pnpm": ">=8.6.0" } -} +} \ No newline at end of file diff --git a/web/src-tauri/.gitignore b/web/src-tauri/.gitignore new file mode 100644 index 0000000..502406b --- /dev/null +++ b/web/src-tauri/.gitignore @@ -0,0 +1,4 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +/gen/schemas diff --git a/web/src-tauri/Cargo.lock b/web/src-tauri/Cargo.lock new file mode 100644 index 0000000..5d51533 --- /dev/null +++ b/web/src-tauri/Cargo.lock @@ -0,0 +1,4995 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.16", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_log-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d" + +[[package]] +name = "android_logger" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb4e440d04be07da1f1bf44fb4495ebd58669372fe0cffa6e48595ac5bd88a3" +dependencies = [ + "android_log-sys", + "env_filter", + "log", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "atk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" +dependencies = [ + "serde", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2 0.5.2", +] + +[[package]] +name = "block2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" +dependencies = [ + "objc2 0.6.2", +] + +[[package]] +name = "borsh" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d4d6dafc1a3bb54687538972158f07b2c948bc57d5890df22c0739098b3028" +dependencies = [ + "borsh-derive", + "cfg_aliases 0.1.1", +] + +[[package]] +name = "borsh-derive" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4918709cc4dd777ad2b6303ed03cb37f3ca0ccede8c1b0d28ac6db8f4710e0" +dependencies = [ + "once_cell", + "proc-macro-crate 2.0.2", + "proc-macro2", + "quote", + "syn 2.0.106", + "syn_derive", +] + +[[package]] +name = "brotli" +version = "8.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byte-unit" +version = "5.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cd29c3c585209b0cbc7309bfe3ed7efd8c84c21b7af29c8bfae908f8777174" +dependencies = [ + "rust_decimal", + "serde", + "utf8-width", +] + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bytemuck" +version = "1.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +dependencies = [ + "serde", +] + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.9.2", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror 1.0.69", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d07aa9a93b00c76f71bc35d598bed923f6d4f3a9ca5c24b7737ae1a292841c0" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.16", +] + +[[package]] +name = "cargo_toml" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" +dependencies = [ + "serde", + "toml 0.9.5", +] + +[[package]] +name = "cc" +version = "1.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" +dependencies = [ + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "time", + "version_check", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +dependencies = [ + "bitflags 2.9.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.9.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.29.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93d03419cb5950ccfd3daf3ff1c7a36ace64609a1a8746d493df1ca0afde0fa" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "matches", + "phf 0.10.1", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.106", +] + +[[package]] +name = "ctor" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +dependencies = [ + "quote", + "syn 2.0.106", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.106", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derive_more" +version = "0.99.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.106", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.60.2", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.9.2", + "objc2 0.6.2", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "dlopen2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b54f373ccf864bf587a89e880fb7610f8d73f3045f13580948ccbcaff26febff" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "788160fb30de9cdd857af31c6a2675904b16ece8fc2737b2c7127ba368c9d0f4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +dependencies = [ + "serde", +] + +[[package]] +name = "dtoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "embed-resource" +version = "3.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6d81016d6c977deefb2ef8d8290da019e27cc26167e102185da528e6c0ab38" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 0.9.5", + "vswhom", + "winreg", +] + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" + +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" +dependencies = [ + "serde", + "typeid", +] + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "fern" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4316185f709b23713e41e3195f90edef7fb00c3ed4adc79769cf09cc762a3b29" +dependencies = [ + "log", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.9.2", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "html5ever" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" +dependencies = [ + "log", + "mac", + "markup5ever", + "match_token", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-util" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ico" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98" +dependencies = [ + "byteorder", + "png", +] + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown 0.15.5", + "serde", +] + +[[package]] +name = "infer" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +dependencies = [ + "cfb", +] + +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags 2.9.2", + "cfg-if", + "libc", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "jsonptr" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.9.2", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "kuchikiki" +version = "0.8.8-speedreader" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" +dependencies = [ + "cssparser", + "html5ever", + "indexmap 2.10.0", + "selectors", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading", + "once_cell", +] + +[[package]] +name = "libc" +version = "0.2.175" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libredox" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" +dependencies = [ + "bitflags 2.9.2", + "libc", +] + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +dependencies = [ + "value-bag", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "markup5ever" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18" +dependencies = [ + "log", + "phf 0.11.3", + "phf_codegen 0.11.3", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "match_token" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + +[[package]] +name = "muda" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c1738382f66ed56b3b9c8119e794a2e23148ac8ea214eda86622d4cb9d415a" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "objc2 0.6.2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.1", + "once_cell", + "png", + "serde", + "thiserror 2.0.16", + "windows-sys 0.60.2", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.9.2", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +dependencies = [ + "proc-macro-crate 2.0.2", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc" +dependencies = [ + "objc2-encode", + "objc2-exception-helper", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" +dependencies = [ + "bitflags 2.9.2", + "block2 0.6.1", + "libc", + "objc2 0.6.2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-foundation 0.3.1", + "objc2-quartz-core 0.3.1", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17614fdcd9b411e6ff1117dfb1d0150f908ba83a7df81b1f118005fe0a8ea15d" +dependencies = [ + "bitflags 2.9.2", + "objc2 0.6.2", + "objc2-foundation 0.3.1", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291fbbf7d29287518e8686417cf7239c74700fd4b607623140a7d4a3c834329d" +dependencies = [ + "bitflags 2.9.2", + "objc2 0.6.2", + "objc2-foundation 0.3.1", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +dependencies = [ + "bitflags 2.9.2", + "dispatch2", + "objc2 0.6.2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" +dependencies = [ + "bitflags 2.9.2", + "dispatch2", + "objc2 0.6.2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79b3dc0cc4386b6ccf21c157591b34a7f44c8e75b064f85502901ab2188c007e" +dependencies = [ + "objc2 0.6.2", + "objc2-foundation 0.3.1", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-exception-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" +dependencies = [ + "cc", +] + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.9.2", + "block2 0.5.1", + "libc", + "objc2 0.5.2", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" +dependencies = [ + "bitflags 2.9.2", + "block2 0.6.1", + "libc", + "objc2 0.6.2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" +dependencies = [ + "bitflags 2.9.2", + "objc2 0.6.2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-javascript-core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9052cb1bb50a4c161d934befcf879526fb87ae9a68858f241e693ca46225cf5a" +dependencies = [ + "objc2 0.6.2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.9.2", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.9.2", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5" +dependencies = [ + "bitflags 2.9.2", + "objc2 0.6.2", + "objc2-foundation 0.3.1", +] + +[[package]] +name = "objc2-security" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1f8e0ef3ab66b08c42644dcb34dba6ec0a574bbd8adbb8bdbdc7a2779731a44" +dependencies = [ + "bitflags 2.9.2", + "objc2 0.6.2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b1312ad7bc8a0e92adae17aa10f90aae1fb618832f9b993b022b591027daed" +dependencies = [ + "bitflags 2.9.2", + "objc2 0.6.2", + "objc2-core-foundation", + "objc2-foundation 0.3.1", +] + +[[package]] +name = "objc2-web-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91672909de8b1ce1c2252e95bbee8c1649c9ad9d14b9248b3d7b4c47903c47ad" +dependencies = [ + "bitflags 2.9.2", + "block2 0.6.1", + "objc2 0.6.2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.1", + "objc2-javascript-core", + "objc2-security", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_shared 0.8.0", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_macros 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared 0.11.3", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher 0.3.11", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher 0.3.11", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher 1.0.1", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "plist" +version = "1.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" +dependencies = [ + "base64 0.22.1", + "indexmap 2.10.0", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quick-xml" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d200a41a7797e6461bd04e4e95c3347053a731c32c87f066f2f0dda22dbdbba8" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +dependencies = [ + "bitflags 2.9.2", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 2.0.16", +] + +[[package]] +name = "ref-cast" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "reqwest" +version = "0.12.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "rkyv" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rust_decimal" +version = "1.37.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b203a6425500a03e0919c42d3c47caca51e79f1132046626d2c8871c5092035d" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.106", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "selectors" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" +dependencies = [ + "bitflags 1.3.2", + "cssparser", + "derive_more", + "fxhash", + "log", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc", + "smallvec", +] + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-untagged" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34836a629bcbc6f1afdf0907a744870039b1e14c0561cb26094fa683b158eff3" +dependencies = [ + "erased-serde", + "serde", + "typeid", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "serde_json" +version = "1.0.143" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.10.0", + "schemars 0.9.0", + "schemars 1.0.4", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "serialize-to-javascript" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "servo_arc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52aa42f8fdf0fed91e5ce7f23d8138441002fa31dca008acf47e6fd4721f741" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "softbuffer" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" +dependencies = [ + "bytemuck", + "cfg_aliases 0.2.1", + "core-graphics", + "foreign-types", + "js-sys", + "log", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-quartz-core 0.2.2", + "raw-window-handle", + "redox_syscall", + "wasm-bindgen", + "web-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.11.3", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "swift-rs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.2", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.34.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4daa814018fecdfb977b59a094df4bd43b42e8e21f88fddfc05807e6f46efaaf" +dependencies = [ + "bitflags 2.9.2", + "block2 0.6.1", + "core-foundation", + "core-graphics", + "crossbeam-channel", + "dispatch", + "dlopen2", + "dpi", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "jni", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc2 0.6.2", + "objc2-app-kit", + "objc2-foundation 0.3.1", + "once_cell", + "parking_lot", + "raw-window-handle", + "scopeguard", + "tao-macros", + "unicode-segmentation", + "url", + "windows", + "windows-core", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tauri" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a54629607ea3084a8b455c1ebe888cbdfc4de02fa5edb2e40db0dc97091007e3" +dependencies = [ + "anyhow", + "bytes", + "cookie", + "dirs", + "dunce", + "embed_plist", + "getrandom 0.3.3", + "glob", + "gtk", + "heck 0.5.0", + "http", + "jni", + "libc", + "log", + "mime", + "muda", + "objc2 0.6.2", + "objc2-app-kit", + "objc2-foundation 0.3.1", + "objc2-ui-kit", + "objc2-web-kit", + "percent-encoding", + "plist", + "raw-window-handle", + "reqwest", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "swift-rs", + "tauri-build", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "thiserror 2.0.16", + "tokio", + "tray-icon", + "url", + "urlpattern", + "webkit2gtk", + "webview2-com", + "window-vibrancy", + "windows", +] + +[[package]] +name = "tauri-build" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67945dbaf8920dbe3a1e56721a419a0c3d085254ab24cff5b9ad55e2b0016e0b" +dependencies = [ + "anyhow", + "cargo_toml", + "dirs", + "glob", + "heck 0.5.0", + "json-patch", + "schemars 0.8.22", + "semver", + "serde", + "serde_json", + "tauri-utils", + "tauri-winres", + "toml 0.9.5", + "walkdir", +] + +[[package]] +name = "tauri-codegen" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ab3a62cf2e6253936a8b267c2e95839674e7439f104fa96ad0025e149d54d8a" +dependencies = [ + "base64 0.22.1", + "brotli", + "ico", + "json-patch", + "plist", + "png", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "sha2", + "syn 2.0.106", + "tauri-utils", + "thiserror 2.0.16", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4368ea8094e7045217edb690f493b55b30caf9f3e61f79b4c24b6db91f07995e" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.106", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-plugin" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9946a3cede302eac0c6eb6c6070ac47b1768e326092d32efbb91f21ed58d978f" +dependencies = [ + "anyhow", + "glob", + "plist", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri-utils", + "toml 0.9.5", + "walkdir", +] + +[[package]] +name = "tauri-plugin-log" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a59139183e0907cec1499dddee4e085f5a801dc659efa0848ee224f461371426" +dependencies = [ + "android_logger", + "byte-unit", + "fern", + "log", + "objc2 0.6.2", + "objc2-foundation 0.3.1", + "serde", + "serde_json", + "serde_repr", + "swift-rs", + "tauri", + "tauri-plugin", + "thiserror 2.0.16", + "time", +] + +[[package]] +name = "tauri-runtime" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4cfc9ad45b487d3fded5a4731a567872a4812e9552e3964161b08edabf93846" +dependencies = [ + "cookie", + "dpi", + "gtk", + "http", + "jni", + "objc2 0.6.2", + "objc2-ui-kit", + "objc2-web-kit", + "raw-window-handle", + "serde", + "serde_json", + "tauri-utils", + "thiserror 2.0.16", + "url", + "webkit2gtk", + "webview2-com", + "windows", +] + +[[package]] +name = "tauri-runtime-wry" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb0f10f831f75832ac74d14d98f701868f9a8adccef2c249b466cf70b607db9" +dependencies = [ + "gtk", + "http", + "jni", + "log", + "objc2 0.6.2", + "objc2-app-kit", + "objc2-foundation 0.3.1", + "once_cell", + "percent-encoding", + "raw-window-handle", + "softbuffer", + "tao", + "tauri-runtime", + "tauri-utils", + "url", + "webkit2gtk", + "webview2-com", + "windows", + "wry", +] + +[[package]] +name = "tauri-utils" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a3852fdf9a4f8fbeaa63dc3e9a85284dd6ef7200751f0bd66ceee30c93f212" +dependencies = [ + "anyhow", + "brotli", + "cargo_metadata", + "ctor", + "dunce", + "glob", + "html5ever", + "http", + "infer", + "json-patch", + "kuchikiki", + "log", + "memchr", + "phf 0.11.3", + "proc-macro2", + "quote", + "regex", + "schemars 0.8.22", + "semver", + "serde", + "serde-untagged", + "serde_json", + "serde_with", + "swift-rs", + "thiserror 2.0.16", + "toml 0.9.5", + "url", + "urlpattern", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-winres" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd21509dd1fa9bd355dc29894a6ff10635880732396aa38c0066c1e6c1ab8074" +dependencies = [ + "embed-resource", + "toml 0.9.5", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl 2.0.16", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.47.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +dependencies = [ + "backtrace", + "bytes", + "io-uring", + "libc", + "mio", + "pin-project-lite", + "slab", + "socket2", + "windows-sys 0.59.0", +] + +[[package]] +name = "tokio-util" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" +dependencies = [ + "indexmap 2.10.0", + "serde", + "serde_spanned 1.0.0", + "toml_datetime 0.7.0", + "toml_parser", + "toml_writer", + "winnow 0.7.12", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.10.0", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.10.0", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_parser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +dependencies = [ + "winnow 0.7.12", +] + +[[package]] +name = "toml_writer" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags 2.9.2", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tray-icon" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d92153331e7d02ec09137538996a7786fe679c629c279e82a6be762b7e6fe2" +dependencies = [ + "crossbeam-channel", + "dirs", + "libappindicator", + "muda", + "objc2 0.6.2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.1", + "once_cell", + "png", + "serde", + "thiserror 2.0.16", + "windows-sys 0.59.0", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" +dependencies = [ + "getrandom 0.3.3", + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "value-bag" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.106", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webview2-com" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ba622a989277ef3886dd5afb3e280e3dd6d974b766118950a08f8f678ad6a4" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows-core", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "webview2-com-sys" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c" +dependencies = [ + "thiserror 2.0.16", + "windows", + "windows-core", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" +dependencies = [ + "windows-sys 0.60.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "window-vibrancy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" +dependencies = [ + "objc2 0.6.2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.1", + "raw-window-handle", + "windows-sys 0.59.0", + "windows-version", +] + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-version" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" + +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.2", +] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "wry" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5698e50a589268aec06d2219f48b143222f7b5ad9aa690118b8dce0a8dcac574" +dependencies = [ + "base64 0.22.1", + "block2 0.6.1", + "cookie", + "crossbeam-channel", + "dirs", + "dpi", + "dunce", + "gdkx11", + "gtk", + "html5ever", + "http", + "javascriptcore-rs", + "jni", + "kuchikiki", + "libc", + "ndk", + "objc2 0.6.2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.1", + "objc2-ui-kit", + "objc2-web-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "sha2", + "soup3", + "tao-macros", + "thiserror 2.0.16", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-core", + "windows-version", + "x11-dl", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "挂牌上锁平台" +version = "0.1.0" +dependencies = [ + "log", + "serde", + "serde_json", + "tauri", + "tauri-build", + "tauri-plugin-log", +] diff --git a/web/src-tauri/Cargo.toml b/web/src-tauri/Cargo.toml new file mode 100644 index 0000000..a3b9b57 --- /dev/null +++ b/web/src-tauri/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "挂牌上锁平台" +version = "0.1.0" +description = "挂牌上锁平台" +authors = ["you"] +license = "" +repository = "" +edition = "2021" +rust-version = "1.77.2" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +name = "app_lib" +crate-type = ["staticlib", "cdylib", "rlib"] + +[build-dependencies] +tauri-build = { version = "2.4.0", features = [] } + +[dependencies] +serde_json = "1.0" +serde = { version = "1.0", features = ["derive"] } +log = "0.4" +tauri = { version = "2.8.2", features = [] } +tauri-plugin-log = "2" diff --git a/web/src-tauri/build.rs b/web/src-tauri/build.rs new file mode 100644 index 0000000..795b9b7 --- /dev/null +++ b/web/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/web/src-tauri/capabilities/default.json b/web/src-tauri/capabilities/default.json new file mode 100644 index 0000000..c135d7f --- /dev/null +++ b/web/src-tauri/capabilities/default.json @@ -0,0 +1,11 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "enables the default permissions", + "windows": [ + "main" + ], + "permissions": [ + "core:default" + ] +} diff --git a/web/src-tauri/icons/128x128.png b/web/src-tauri/icons/128x128.png new file mode 100644 index 0000000..e52e550 Binary files /dev/null and b/web/src-tauri/icons/128x128.png differ diff --git a/web/src-tauri/icons/128x128@2x.png b/web/src-tauri/icons/128x128@2x.png new file mode 100644 index 0000000..62b2219 Binary files /dev/null and b/web/src-tauri/icons/128x128@2x.png differ diff --git a/web/src-tauri/icons/32x32.png b/web/src-tauri/icons/32x32.png new file mode 100644 index 0000000..7f0c690 Binary files /dev/null and b/web/src-tauri/icons/32x32.png differ diff --git a/web/src-tauri/icons/64x64.png b/web/src-tauri/icons/64x64.png new file mode 100644 index 0000000..580b862 Binary files /dev/null and b/web/src-tauri/icons/64x64.png differ diff --git a/web/src-tauri/icons/Square107x107Logo.png b/web/src-tauri/icons/Square107x107Logo.png new file mode 100644 index 0000000..c62f71f Binary files /dev/null and b/web/src-tauri/icons/Square107x107Logo.png differ diff --git a/web/src-tauri/icons/Square142x142Logo.png b/web/src-tauri/icons/Square142x142Logo.png new file mode 100644 index 0000000..52bb50d Binary files /dev/null and b/web/src-tauri/icons/Square142x142Logo.png differ diff --git a/web/src-tauri/icons/Square150x150Logo.png b/web/src-tauri/icons/Square150x150Logo.png new file mode 100644 index 0000000..37b11cb Binary files /dev/null and b/web/src-tauri/icons/Square150x150Logo.png differ diff --git a/web/src-tauri/icons/Square284x284Logo.png b/web/src-tauri/icons/Square284x284Logo.png new file mode 100644 index 0000000..7683a5c Binary files /dev/null and b/web/src-tauri/icons/Square284x284Logo.png differ diff --git a/web/src-tauri/icons/Square30x30Logo.png b/web/src-tauri/icons/Square30x30Logo.png new file mode 100644 index 0000000..9f7c764 Binary files /dev/null and b/web/src-tauri/icons/Square30x30Logo.png differ diff --git a/web/src-tauri/icons/Square310x310Logo.png b/web/src-tauri/icons/Square310x310Logo.png new file mode 100644 index 0000000..7cece64 Binary files /dev/null and b/web/src-tauri/icons/Square310x310Logo.png differ diff --git a/web/src-tauri/icons/Square44x44Logo.png b/web/src-tauri/icons/Square44x44Logo.png new file mode 100644 index 0000000..609968b Binary files /dev/null and b/web/src-tauri/icons/Square44x44Logo.png differ diff --git a/web/src-tauri/icons/Square71x71Logo.png b/web/src-tauri/icons/Square71x71Logo.png new file mode 100644 index 0000000..50ceb99 Binary files /dev/null and b/web/src-tauri/icons/Square71x71Logo.png differ diff --git a/web/src-tauri/icons/Square89x89Logo.png b/web/src-tauri/icons/Square89x89Logo.png new file mode 100644 index 0000000..b6eabde Binary files /dev/null and b/web/src-tauri/icons/Square89x89Logo.png differ diff --git a/web/src-tauri/icons/StoreLogo.png b/web/src-tauri/icons/StoreLogo.png new file mode 100644 index 0000000..2cc5083 Binary files /dev/null and b/web/src-tauri/icons/StoreLogo.png differ diff --git a/web/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png b/web/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..e943acd Binary files /dev/null and b/web/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png differ diff --git a/web/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png b/web/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..8f9a132 Binary files /dev/null and b/web/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/web/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png b/web/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..e943acd Binary files /dev/null and b/web/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png differ diff --git a/web/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png b/web/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..417440e Binary files /dev/null and b/web/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png differ diff --git a/web/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png b/web/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..6e2cfd7 Binary files /dev/null and b/web/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/web/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png b/web/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..417440e Binary files /dev/null and b/web/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png differ diff --git a/web/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png b/web/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..03670f0 Binary files /dev/null and b/web/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png differ diff --git a/web/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png b/web/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..c96b2c5 Binary files /dev/null and b/web/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/web/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png b/web/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..03670f0 Binary files /dev/null and b/web/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/web/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png b/web/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d4c158 Binary files /dev/null and b/web/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png differ diff --git a/web/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png b/web/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..f8605ec Binary files /dev/null and b/web/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/web/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png b/web/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..4d4c158 Binary files /dev/null and b/web/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/web/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png b/web/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..7049949 Binary files /dev/null and b/web/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/web/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png b/web/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..1763331 Binary files /dev/null and b/web/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/web/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png b/web/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..7049949 Binary files /dev/null and b/web/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/web/src-tauri/icons/icon.icns b/web/src-tauri/icons/icon.icns new file mode 100644 index 0000000..25c9cf1 Binary files /dev/null and b/web/src-tauri/icons/icon.icns differ diff --git a/web/src-tauri/icons/icon.ico b/web/src-tauri/icons/icon.ico new file mode 100644 index 0000000..d0ef496 Binary files /dev/null and b/web/src-tauri/icons/icon.ico differ diff --git a/web/src-tauri/icons/icon.png b/web/src-tauri/icons/icon.png new file mode 100644 index 0000000..9a7d2e6 Binary files /dev/null and b/web/src-tauri/icons/icon.png differ diff --git a/web/src-tauri/icons/ios/AppIcon-20x20@1x.png b/web/src-tauri/icons/ios/AppIcon-20x20@1x.png new file mode 100644 index 0000000..ad5f43c Binary files /dev/null and b/web/src-tauri/icons/ios/AppIcon-20x20@1x.png differ diff --git a/web/src-tauri/icons/ios/AppIcon-20x20@2x-1.png b/web/src-tauri/icons/ios/AppIcon-20x20@2x-1.png new file mode 100644 index 0000000..cef1a45 Binary files /dev/null and b/web/src-tauri/icons/ios/AppIcon-20x20@2x-1.png differ diff --git a/web/src-tauri/icons/ios/AppIcon-20x20@2x.png b/web/src-tauri/icons/ios/AppIcon-20x20@2x.png new file mode 100644 index 0000000..cef1a45 Binary files /dev/null and b/web/src-tauri/icons/ios/AppIcon-20x20@2x.png differ diff --git a/web/src-tauri/icons/ios/AppIcon-20x20@3x.png b/web/src-tauri/icons/ios/AppIcon-20x20@3x.png new file mode 100644 index 0000000..97d7e62 Binary files /dev/null and b/web/src-tauri/icons/ios/AppIcon-20x20@3x.png differ diff --git a/web/src-tauri/icons/ios/AppIcon-29x29@1x.png b/web/src-tauri/icons/ios/AppIcon-29x29@1x.png new file mode 100644 index 0000000..f218429 Binary files /dev/null and b/web/src-tauri/icons/ios/AppIcon-29x29@1x.png differ diff --git a/web/src-tauri/icons/ios/AppIcon-29x29@2x-1.png b/web/src-tauri/icons/ios/AppIcon-29x29@2x-1.png new file mode 100644 index 0000000..4062b85 Binary files /dev/null and b/web/src-tauri/icons/ios/AppIcon-29x29@2x-1.png differ diff --git a/web/src-tauri/icons/ios/AppIcon-29x29@2x.png b/web/src-tauri/icons/ios/AppIcon-29x29@2x.png new file mode 100644 index 0000000..4062b85 Binary files /dev/null and b/web/src-tauri/icons/ios/AppIcon-29x29@2x.png differ diff --git a/web/src-tauri/icons/ios/AppIcon-29x29@3x.png b/web/src-tauri/icons/ios/AppIcon-29x29@3x.png new file mode 100644 index 0000000..e714b1c Binary files /dev/null and b/web/src-tauri/icons/ios/AppIcon-29x29@3x.png differ diff --git a/web/src-tauri/icons/ios/AppIcon-40x40@1x.png b/web/src-tauri/icons/ios/AppIcon-40x40@1x.png new file mode 100644 index 0000000..cef1a45 Binary files /dev/null and b/web/src-tauri/icons/ios/AppIcon-40x40@1x.png differ diff --git a/web/src-tauri/icons/ios/AppIcon-40x40@2x-1.png b/web/src-tauri/icons/ios/AppIcon-40x40@2x-1.png new file mode 100644 index 0000000..725a2d7 Binary files /dev/null and b/web/src-tauri/icons/ios/AppIcon-40x40@2x-1.png differ diff --git a/web/src-tauri/icons/ios/AppIcon-40x40@2x.png b/web/src-tauri/icons/ios/AppIcon-40x40@2x.png new file mode 100644 index 0000000..725a2d7 Binary files /dev/null and b/web/src-tauri/icons/ios/AppIcon-40x40@2x.png differ diff --git a/web/src-tauri/icons/ios/AppIcon-40x40@3x.png b/web/src-tauri/icons/ios/AppIcon-40x40@3x.png new file mode 100644 index 0000000..59530ba Binary files /dev/null and b/web/src-tauri/icons/ios/AppIcon-40x40@3x.png differ diff --git a/web/src-tauri/icons/ios/AppIcon-512@2x.png b/web/src-tauri/icons/ios/AppIcon-512@2x.png new file mode 100644 index 0000000..ecd5ab7 Binary files /dev/null and b/web/src-tauri/icons/ios/AppIcon-512@2x.png differ diff --git a/web/src-tauri/icons/ios/AppIcon-60x60@2x.png b/web/src-tauri/icons/ios/AppIcon-60x60@2x.png new file mode 100644 index 0000000..59530ba Binary files /dev/null and b/web/src-tauri/icons/ios/AppIcon-60x60@2x.png differ diff --git a/web/src-tauri/icons/ios/AppIcon-60x60@3x.png b/web/src-tauri/icons/ios/AppIcon-60x60@3x.png new file mode 100644 index 0000000..886f928 Binary files /dev/null and b/web/src-tauri/icons/ios/AppIcon-60x60@3x.png differ diff --git a/web/src-tauri/icons/ios/AppIcon-76x76@1x.png b/web/src-tauri/icons/ios/AppIcon-76x76@1x.png new file mode 100644 index 0000000..9a44013 Binary files /dev/null and b/web/src-tauri/icons/ios/AppIcon-76x76@1x.png differ diff --git a/web/src-tauri/icons/ios/AppIcon-76x76@2x.png b/web/src-tauri/icons/ios/AppIcon-76x76@2x.png new file mode 100644 index 0000000..2658c34 Binary files /dev/null and b/web/src-tauri/icons/ios/AppIcon-76x76@2x.png differ diff --git a/web/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png b/web/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000..a8ef867 Binary files /dev/null and b/web/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png differ diff --git a/web/src-tauri/src/lib.rs b/web/src-tauri/src/lib.rs new file mode 100644 index 0000000..9c3118c --- /dev/null +++ b/web/src-tauri/src/lib.rs @@ -0,0 +1,16 @@ +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .setup(|app| { + if cfg!(debug_assertions) { + app.handle().plugin( + tauri_plugin_log::Builder::default() + .level(log::LevelFilter::Info) + .build(), + )?; + } + Ok(()) + }) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/web/src-tauri/src/main.rs b/web/src-tauri/src/main.rs new file mode 100644 index 0000000..ad5fe83 --- /dev/null +++ b/web/src-tauri/src/main.rs @@ -0,0 +1,6 @@ +// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + app_lib::run(); +} diff --git a/web/src-tauri/tauri.conf.json b/web/src-tauri/tauri.conf.json new file mode 100644 index 0000000..8fceb06 --- /dev/null +++ b/web/src-tauri/tauri.conf.json @@ -0,0 +1,37 @@ +{ + "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", + "productName": "ElectronicLock", + "version": "0.1.0", + "identifier": "lock.zdhlcn.com", + "build": { + "frontendDist": "../dist-prod", + "devUrl": "http://localhost", + "beforeDevCommand": "pnpm dev:tauri", + "beforeBuildCommand": "pnpm build:tauri" + }, + "app": { + "windows": [ + { + "title": "挂牌上锁平台", + "width": 800, + "height": 600, + "resizable": true, + "fullscreen": false + } + ], + "security": { + "csp": null + } + }, + "bundle": { + "active": true, + "targets": "all", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ] + } +} diff --git a/web/src/api/bpm/processExpression/index.ts b/web/src/api/bpm/processExpression/index.ts index af6a737..e2a654d 100644 --- a/web/src/api/bpm/processExpression/index.ts +++ b/web/src/api/bpm/processExpression/index.ts @@ -39,4 +39,4 @@ export const ProcessExpressionApi = { exportProcessExpression: async (params) => { return await request.download({ url: `/bpm/process-expression/export-excel`, params }) } -} \ No newline at end of file +} diff --git a/web/src/api/bpm/simple/index.ts b/web/src/api/bpm/simple/index.ts index 6e1e995..937c2b1 100644 --- a/web/src/api/bpm/simple/index.ts +++ b/web/src/api/bpm/simple/index.ts @@ -1,6 +1,5 @@ import request from '@/config/axios' - export const updateBpmSimpleModel = async (data) => { return await request.post({ url: '/bpm/model/simple/update', diff --git a/web/src/api/electron/lock/index.ts b/web/src/api/electron/lock/index.ts index eed3c62..e2820e9 100644 --- a/web/src/api/electron/lock/index.ts +++ b/web/src/api/electron/lock/index.ts @@ -1,16 +1,16 @@ import request from '@/config/axios' -import type { Dayjs } from 'dayjs'; +import type { Dayjs } from 'dayjs' /** 电子锁信息 */ export interface Lock { - id?: number; // 主键ID - lockNumber?: number; // 编号 - lockName?: string; // 名称 - lockStatus?: number; // 状态 - lockType?: number; // 锁具类型 - lockEnableStatus?: number; // 启用状态: 0=未启用, 1=已启用 - lockLastChargeTime?: string | Dayjs; // 上次充电时间 - lockBluetoothId?: string; // 蓝牙ID + id?: number // 主键ID + lockNumber?: number // 编号 + lockName?: string // 名称 + lockStatus?: number // 状态 + lockType?: number // 锁具类型 + lockEnableStatus?: number // 启用状态: 0=未启用, 1=已启用 + lockLastChargeTime?: string | Dayjs // 上次充电时间 + lockBluetoothId?: string // 蓝牙ID } // 电子锁 API diff --git a/web/src/api/electron/lockworkcord/index.ts b/web/src/api/electron/lockworkcord/index.ts index f8e1369..0169408 100644 --- a/web/src/api/electron/lockworkcord/index.ts +++ b/web/src/api/electron/lockworkcord/index.ts @@ -1,17 +1,17 @@ import request from '@/config/axios' -import type { Dayjs } from 'dayjs'; +import type { Dayjs } from 'dayjs' /** 电子锁操作记录信息 */ export interface LockWorkRecord { - id?: number; // 主键ID - operatorId?: number; // 操作人ID - lockId?: number; // 电子锁ID - isolationPlanItemDetailId?: number; // 关联的子项详情ID (某些操作可能不关联) - recordType?: number; // 记录类型 - signaturePath?: string; // 操作签名 (图片路径) - beforePhotoPath?: string; // 操作前照片 (图片路径) - afterPhotoPath?: string; // 操作后照片 (图片路径) - gpsCoordinates?: string; // 操作GPS坐标 + id?: number // 主键ID + operatorId?: number // 操作人ID + lockId?: number // 电子锁ID + isolationPlanItemDetailId?: number // 关联的子项详情ID (某些操作可能不关联) + recordType?: number // 记录类型 + signaturePath?: string // 操作签名 (图片路径) + beforePhotoPath?: string // 操作前照片 (图片路径) + afterPhotoPath?: string // 操作后照片 (图片路径) + gpsCoordinates?: string // 操作GPS坐标 } // 电子锁操作记录 API @@ -43,7 +43,9 @@ export const LockWorkRecordApi = { /** 批量删除电子锁操作记录 */ deleteLockWorkRecordList: async (ids: number[]) => { - return await request.delete({ url: `/electron/lock-word-record/delete-list?ids=${ids.join(',')}` }) + return await request.delete({ + url: `/electron/lock-word-record/delete-list?ids=${ids.join(',')}` + }) }, // 导出电子锁操作记录 Excel diff --git a/web/src/api/guide/isolationpoint/index.ts b/web/src/api/guide/isolationpoint/index.ts index 43ecef1..c281aa0 100644 --- a/web/src/api/guide/isolationpoint/index.ts +++ b/web/src/api/guide/isolationpoint/index.ts @@ -1,11 +1,11 @@ import request from '@/config/axios' -import type { Dayjs } from 'dayjs'; +import type { Dayjs } from 'dayjs' /** 指导书与隔离点关联信息 */ export interface IsolationPoint { - id?: number; // id - guideId?: number; // 隔离指导书ID - isolationPointId?: number; // 隔离点ID + id?: number // id + guideId?: number // 隔离指导书ID + isolationPointId?: number // 隔离点ID } // 指导书与隔离点关联 API diff --git a/web/src/api/guide/lockguide/index.ts b/web/src/api/guide/lockguide/index.ts index 8a2fb50..2916c15 100644 --- a/web/src/api/guide/lockguide/index.ts +++ b/web/src/api/guide/lockguide/index.ts @@ -1,18 +1,18 @@ import request from '@/config/axios' -import type { Dayjs } from 'dayjs'; +import type { Dayjs } from 'dayjs' /** 隔离指导书信息 */ export interface LockGuide { - id?: number; // 主键ID - name?: string; // 指导书名称 - code?: string; // 指导书编码 - operatorId?: number; // 操作人ID - operatorHelperId?: number; // 操作协助人ID - verifierId?: number; // 验证人ID - verifierHelperId?: number; // 验证协助人ID - guideContent?: string; // 工作内容和范围 - guideLockNums?: number; // 所需设备锁数量 - isolationPointIds?: number[]; // 关联隔离点ID + id?: number // 主键ID + name?: string // 指导书名称 + code?: string // 指导书编码 + operatorId?: number // 操作人ID + operatorHelperId?: number // 操作协助人ID + verifierId?: number // 验证人ID + verifierHelperId?: number // 验证协助人ID + guideContent?: string // 工作内容和范围 + guideLockNums?: number // 所需设备锁数量 + isolationPointIds?: number[] // 关联隔离点ID } // 隔离指导书 API diff --git a/web/src/api/infra/dataSourceConfig/index.ts b/web/src/api/infra/dataSourceConfig/index.ts index 55bd6a3..bdc1d80 100644 --- a/web/src/api/infra/dataSourceConfig/index.ts +++ b/web/src/api/infra/dataSourceConfig/index.ts @@ -26,7 +26,10 @@ export const deleteDataSourceConfig = (id: number) => { // 批量删除数据源配置 export const deleteDataSourceConfigList = (ids: number[]) => { - return request.delete({ url: '/infra/data-source-config/delete-list', params: { ids: ids.join(',') } }) + return request.delete({ + url: '/infra/data-source-config/delete-list', + params: { ids: ids.join(',') } + }) } // 查询数据源配置详情 diff --git a/web/src/api/infra/demo/demo03/erp/index.ts b/web/src/api/infra/demo/demo03/erp/index.ts index c641ed0..5deba7c 100644 --- a/web/src/api/infra/demo/demo03/erp/index.ts +++ b/web/src/api/infra/demo/demo03/erp/index.ts @@ -1,29 +1,29 @@ import request from '@/config/axios' -import type { Dayjs } from 'dayjs'; +import type { Dayjs } from 'dayjs' /** 学生课程信息 */ export interface Demo03Course { - id: number; // 编号 - studentId?: number; // 学生编号 - name?: string; // 名字 - score?: number; // 分数 + id: number // 编号 + studentId?: number // 学生编号 + name?: string // 名字 + score?: number // 分数 } /** 学生班级信息 */ export interface Demo03Grade { - id: number; // 编号 - studentId?: number; // 学生编号 - name?: string; // 名字 - teacher?: string; // 班主任 + id: number // 编号 + studentId?: number // 学生编号 + name?: string // 名字 + teacher?: string // 班主任 } /** 学生信息 */ export interface Demo03Student { - id: number; // 编号 - name?: string; // 名字 - sex?: number; // 性别 - birthday?: string | Dayjs; // 出生日期 - description?: string; // 简介 + id: number // 编号 + name?: string // 名字 + sex?: number // 性别 + birthday?: string | Dayjs // 出生日期 + description?: string // 简介 } // 学生 API @@ -55,7 +55,9 @@ export const Demo03StudentApi = { /** 批量删除学生 */ deleteDemo03StudentList: async (ids: number[]) => { - return await request.delete({ url: `/infra/demo03-student-erp/delete-list?ids=${ids.join(',')}` }) + return await request.delete({ + url: `/infra/demo03-student-erp/delete-list?ids=${ids.join(',')}` + }) }, // 导出学生 Excel @@ -63,7 +65,7 @@ export const Demo03StudentApi = { return await request.download({ url: `/infra/demo03-student-erp/export-excel`, params }) }, -// ==================== 子表(学生课程) ==================== + // ==================== 子表(学生课程) ==================== // 获得学生课程分页 getDemo03CoursePage: async (params) => { @@ -86,7 +88,9 @@ export const Demo03StudentApi = { /** 批量删除学生课程 */ deleteDemo03CourseList: async (ids: number[]) => { - return await request.delete({ url: `/infra/demo03-student-erp/demo03-course/delete-list?ids=${ids.join(',')}` }) + return await request.delete({ + url: `/infra/demo03-student-erp/demo03-course/delete-list?ids=${ids.join(',')}` + }) }, // 获得学生课程 @@ -94,7 +98,7 @@ export const Demo03StudentApi = { return await request.get({ url: `/infra/demo03-student-erp/demo03-course/get?id=` + id }) }, -// ==================== 子表(学生班级) ==================== + // ==================== 子表(学生班级) ==================== // 获得学生班级分页 getDemo03GradePage: async (params) => { @@ -117,11 +121,13 @@ export const Demo03StudentApi = { /** 批量删除学生班级 */ deleteDemo03GradeList: async (ids: number[]) => { - return await request.delete({ url: `/infra/demo03-student-erp/demo03-grade/delete-list?ids=${ids.join(',')}` }) + return await request.delete({ + url: `/infra/demo03-student-erp/demo03-grade/delete-list?ids=${ids.join(',')}` + }) }, // 获得学生班级 getDemo03Grade: async (id: number) => { return await request.get({ url: `/infra/demo03-student-erp/demo03-grade/get?id=` + id }) - }, + } } diff --git a/web/src/api/infra/demo/demo03/inner/index.ts b/web/src/api/infra/demo/demo03/inner/index.ts index 1000d9c..5c22aa4 100644 --- a/web/src/api/infra/demo/demo03/inner/index.ts +++ b/web/src/api/infra/demo/demo03/inner/index.ts @@ -1,29 +1,29 @@ import request from '@/config/axios' -import type { Dayjs } from 'dayjs'; +import type { Dayjs } from 'dayjs' /** 学生课程信息 */ export interface Demo03Course { - id: number; // 编号 - studentId?: number; // 学生编号 - name?: string; // 名字 - score?: number; // 分数 + id: number // 编号 + studentId?: number // 学生编号 + name?: string // 名字 + score?: number // 分数 } /** 学生班级信息 */ export interface Demo03Grade { - id: number; // 编号 - studentId?: number; // 学生编号 - name?: string; // 名字 - teacher?: string; // 班主任 + id: number // 编号 + studentId?: number // 学生编号 + name?: string // 名字 + teacher?: string // 班主任 } /** 学生信息 */ export interface Demo03Student { - id: number; // 编号 - name?: string; // 名字 - sex?: number; // 性别 - birthday?: string | Dayjs; // 出生日期 - description?: string; // 简介 + id: number // 编号 + name?: string // 名字 + sex?: number // 性别 + birthday?: string | Dayjs // 出生日期 + description?: string // 简介 demo03courses?: Demo03Course[] demo03grade?: Demo03Grade } @@ -57,7 +57,9 @@ export const Demo03StudentApi = { /** 批量删除学生 */ deleteDemo03StudentList: async (ids: number[]) => { - return await request.delete({ url: `/infra/demo03-student-inner/delete-list?ids=${ids.join(',')}` }) + return await request.delete({ + url: `/infra/demo03-student-inner/delete-list?ids=${ids.join(',')}` + }) }, // 导出学生 Excel @@ -65,17 +67,21 @@ export const Demo03StudentApi = { return await request.download({ url: `/infra/demo03-student-inner/export-excel`, params }) }, -// ==================== 子表(学生课程) ==================== + // ==================== 子表(学生课程) ==================== // 获得学生课程列表 getDemo03CourseListByStudentId: async (studentId) => { - return await request.get({ url: `/infra/demo03-student-inner/demo03-course/list-by-student-id?studentId=` + studentId }) + return await request.get({ + url: `/infra/demo03-student-inner/demo03-course/list-by-student-id?studentId=` + studentId + }) }, -// ==================== 子表(学生班级) ==================== + // ==================== 子表(学生班级) ==================== // 获得学生班级 getDemo03GradeByStudentId: async (studentId) => { - return await request.get({ url: `/infra/demo03-student-inner/demo03-grade/get-by-student-id?studentId=` + studentId }) - }, + return await request.get({ + url: `/infra/demo03-student-inner/demo03-grade/get-by-student-id?studentId=` + studentId + }) + } } diff --git a/web/src/api/infra/demo/demo03/normal/index.ts b/web/src/api/infra/demo/demo03/normal/index.ts index 56a824d..ff3d2f4 100644 --- a/web/src/api/infra/demo/demo03/normal/index.ts +++ b/web/src/api/infra/demo/demo03/normal/index.ts @@ -1,29 +1,29 @@ import request from '@/config/axios' -import type { Dayjs } from 'dayjs'; +import type { Dayjs } from 'dayjs' /** 学生课程信息 */ export interface Demo03Course { - id: number; // 编号 - studentId?: number; // 学生编号 - name?: string; // 名字 - score?: number; // 分数 + id: number // 编号 + studentId?: number // 学生编号 + name?: string // 名字 + score?: number // 分数 } /** 学生班级信息 */ export interface Demo03Grade { - id: number; // 编号 - studentId?: number; // 学生编号 - name?: string; // 名字 - teacher?: string; // 班主任 + id: number // 编号 + studentId?: number // 学生编号 + name?: string // 名字 + teacher?: string // 班主任 } /** 学生信息 */ export interface Demo03Student { - id: number; // 编号 - name?: string; // 名字 - sex?: number; // 性别 - birthday?: string | Dayjs; // 出生日期 - description?: string; // 简介 + id: number // 编号 + name?: string // 名字 + sex?: number // 性别 + birthday?: string | Dayjs // 出生日期 + description?: string // 简介 demo03courses?: Demo03Course[] demo03grade?: Demo03Grade } @@ -57,7 +57,9 @@ export const Demo03StudentApi = { /** 批量删除学生 */ deleteDemo03StudentList: async (ids: number[]) => { - return await request.delete({ url: `/infra/demo03-student-normal/delete-list?ids=${ids.join(',')}` }) + return await request.delete({ + url: `/infra/demo03-student-normal/delete-list?ids=${ids.join(',')}` + }) }, // 导出学生 Excel @@ -65,17 +67,21 @@ export const Demo03StudentApi = { return await request.download({ url: `/infra/demo03-student-normal/export-excel`, params }) }, -// ==================== 子表(学生课程) ==================== + // ==================== 子表(学生课程) ==================== // 获得学生课程列表 getDemo03CourseListByStudentId: async (studentId) => { - return await request.get({ url: `/infra/demo03-student-normal/demo03-course/list-by-student-id?studentId=` + studentId }) + return await request.get({ + url: `/infra/demo03-student-normal/demo03-course/list-by-student-id?studentId=` + studentId + }) }, -// ==================== 子表(学生班级) ==================== + // ==================== 子表(学生班级) ==================== // 获得学生班级 getDemo03GradeByStudentId: async (studentId) => { - return await request.get({ url: `/infra/demo03-student-normal/demo03-grade/get-by-student-id?studentId=` + studentId }) - }, + return await request.get({ + url: `/infra/demo03-student-normal/demo03-grade/get-by-student-id?studentId=` + studentId + }) + } } diff --git a/web/src/api/isolation/plan/index.ts b/web/src/api/isolation/plan/index.ts index 3d24090..c006ab4 100644 --- a/web/src/api/isolation/plan/index.ts +++ b/web/src/api/isolation/plan/index.ts @@ -1,11 +1,11 @@ import request from '@/config/axios' -import type { Dayjs } from 'dayjs'; +import type { Dayjs } from 'dayjs' /** 检修任务信息 */ export interface Plan { - id: number; // 主键ID - ipName?: string; // 任务名称 - status?: number; // 状态 + id: number // 主键ID + ipName?: string // 任务名称 + status?: number // 状态 } // 检修任务 API diff --git a/web/src/api/isolation/planitem/index.ts b/web/src/api/isolation/planitem/index.ts index 00af72f..7a03962 100644 --- a/web/src/api/isolation/planitem/index.ts +++ b/web/src/api/isolation/planitem/index.ts @@ -1,16 +1,16 @@ import request from '@/config/axios' -import type { Dayjs } from 'dayjs'; +import type { Dayjs } from 'dayjs' /** 检修任务子项信息 */ export interface PlanItem { - id?: number; // 主键ID - isolationPlanId?: number; // 检修任务ID - guideId?: number; // 隔离指导书ID - operatorId?: number; // 集中挂牌人ID - operatorHelperId?: number; // 集中挂牌协助人ID - verifierId?: number; // 验证人ID - verifierHelperId?: number; // 验证协助人ID - status?: number; // 子项状态: 0=未完成, 1=已完成 + id?: number // 主键ID + isolationPlanId?: number // 检修任务ID + guideId?: number // 隔离指导书ID + operatorId?: number // 集中挂牌人ID + operatorHelperId?: number // 集中挂牌协助人ID + verifierId?: number // 验证人ID + verifierHelperId?: number // 验证协助人ID + status?: number // 子项状态: 0=未完成, 1=已完成 } // 检修任务子项 API diff --git a/web/src/api/isolation/planitemdetail/index.ts b/web/src/api/isolation/planitemdetail/index.ts index cd0c232..79e74ed 100644 --- a/web/src/api/isolation/planitemdetail/index.ts +++ b/web/src/api/isolation/planitemdetail/index.ts @@ -1,13 +1,13 @@ import request from '@/config/axios' -import type { Dayjs } from 'dayjs'; +import type { Dayjs } from 'dayjs' /** 检修任务子项详情信息 */ export interface PlanItemDetail { - id?: number; // 主键ID - isolationPlanItemId?: number; // 检修任务子项ID - isolationPointId?: number; // 隔离点ID - lockId?: number; // 电子锁ID - lockStatus?: number; // 锁状态: 0=未上锁, 1=已上锁, 2=已解锁 + id?: number // 主键ID + isolationPlanItemId?: number // 检修任务子项ID + isolationPointId?: number // 隔离点ID + lockId?: number // 电子锁ID + lockStatus?: number // 锁状态: 0=未上锁, 1=已上锁, 2=已解锁 } // 检修任务子项详情 API @@ -39,7 +39,9 @@ export const PlanItemDetailApi = { /** 批量删除检修任务子项详情 */ deletePlanItemDetailList: async (ids: number[]) => { - return await request.delete({ url: `/isolation/plan-item-detail/delete-list?ids=${ids.join(',')}` }) + return await request.delete({ + url: `/isolation/plan-item-detail/delete-list?ids=${ids.join(',')}` + }) }, // 导出检修任务子项详情 Excel diff --git a/web/src/api/isolation/planlifelock/index.ts b/web/src/api/isolation/planlifelock/index.ts index dc86bb9..22ecf4f 100644 --- a/web/src/api/isolation/planlifelock/index.ts +++ b/web/src/api/isolation/planlifelock/index.ts @@ -1,15 +1,15 @@ import request from '@/config/axios' -import type { Dayjs } from 'dayjs'; +import type { Dayjs } from 'dayjs' /** 个人生命锁信息 */ export interface PlanLifeLock { - id: number; // 主键ID - isolationPlanItemDetailId?: number; // 子项详情ID - userId?: number; // 上锁人ID - lockType?: number; // 生命锁类型 - lockStatus?: number; // 锁定状态: 0=未上锁, 1=已上锁 - lockTime?: number; // 上锁时间 - unlockTime?: number; // 解锁时间 + id: number // 主键ID + isolationPlanItemDetailId?: number // 子项详情ID + userId?: number // 上锁人ID + lockType?: number // 生命锁类型 + lockStatus?: number // 锁定状态: 0=未上锁, 1=已上锁 + lockTime?: number // 上锁时间 + unlockTime?: number // 解锁时间 } // 个人生命锁 API @@ -41,7 +41,9 @@ export const PlanLifeLockApi = { /** 批量删除个人生命锁 */ deletePlanLifeLockList: async (ids: number[]) => { - return await request.delete({ url: `/isolation/plan-life-lock/delete-list?ids=${ids.join(',')}` }) + return await request.delete({ + url: `/isolation/plan-life-lock/delete-list?ids=${ids.join(',')}` + }) }, // 导出个人生命锁 Excel diff --git a/web/src/api/isolation/point/index.ts b/web/src/api/isolation/point/index.ts index c31acfa..d2820c3 100644 --- a/web/src/api/isolation/point/index.ts +++ b/web/src/api/isolation/point/index.ts @@ -1,15 +1,15 @@ import request from '@/config/axios' -import type { Dayjs } from 'dayjs'; +import type { Dayjs } from 'dayjs' /** 隔离点信息 */ export interface Point { - id?: number; // 主键ID - ipType?: string; // 隔离点类型 - ipName?: string; // 隔离点名称 - ipLocation?: string; // 隔离点位置 - ipNumber?: number; // 隔离点编号 - status?: number; // 隔离点状态 - guideLockNums?: number; // 电子锁数量 + id?: number // 主键ID + ipType?: string // 隔离点类型 + ipName?: string // 隔离点名称 + ipLocation?: string // 隔离点位置 + ipNumber?: number // 隔离点编号 + status?: number // 隔离点状态 + guideLockNums?: number // 电子锁数量 } // 隔离点 API diff --git a/web/src/api/lock/index.ts b/web/src/api/lock/index.ts index ac6d0d8..69663c8 100644 --- a/web/src/api/lock/index.ts +++ b/web/src/api/lock/index.ts @@ -1,7 +1,5 @@ import request from '@/config/axios' - - export const getAllLock = (params: PageParam = { pageSize: 9999, pageNo: 1 }) => { return request.get({ url: `/electron/lock/page`, params }) } @@ -29,13 +27,13 @@ export const getAllPlanLifeLock = (params: PageParam = { pageSize: 9999, pageNo: } const baseUrl = import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL export const getAgentConfigSignature = (url: string) => { - return fetch(`${baseUrl}/js/weixin/getAgentConfigSignature?url=${url}`).then(async res => { + return fetch(`${baseUrl}/js/weixin/getAgentConfigSignature?url=${url}`).then(async (res) => { return await res.json() }) } export const getConfigSignature = (url: string) => { - return fetch(`${baseUrl}/js/weixin/getConfigSignature?url=${url}`).then(async res => { + return fetch(`${baseUrl}/js/weixin/getConfigSignature?url=${url}`).then(async (res) => { return await res.json() }) } @@ -44,23 +42,30 @@ export const getAllFormattedIsolationPlan = () => { return request.get({ url: `/isolation/plan/planListAll` }) } - // 获取所有基础数据 export const getAllBaseData = () => request.get({ url: `isolation/point/getListAll` }) - // 查询用户管理列表 export const getAllUser = () => request.get({ url: 'system/user/list-all-simple' }) // 根据隔离点获取相关记录 -export const getIsolationPointDetail = (id: number) => request.get({ url: `isolation/point/getPointListAll`, params: { id } }) +export const getIsolationPointDetail = (id: number) => + request.get({ url: `isolation/point/getPointListAll`, params: { id } }) -export const bindLock = (data: { planItemDetailId: number; lockId: number }) => request.put({ url: `isolation/point/bindlock`, data }) +export const bindLock = (data: { planItemDetailId: number; lockId: number }) => + request.put({ url: `isolation/point/bindlock`, data }) -export const lockAction = (data: { planItemDetailId: number, operateRecordId: number }) => request.put({ url: `isolation/point/createLock`, data }) +export const lockAction = (data: { planItemDetailId: number; operateRecordId: number }) => + request.put({ url: `isolation/point/createLock`, data }) -export const verifyLockAction = (data: { planItemDetailId: number, verifyRecordId: number }) => request.put({ url: `isolation/point/verifyLock`, data }) +export const verifyLockAction = (data: { planItemDetailId: number; verifyRecordId: number }) => + request.put({ url: `isolation/point/verifyLock`, data }) -export const verifyUnlockAction = (data: { planItemDetailId: number, lifelockId: number }) => request.put({ url: `isolation/point/verifyUnLock`, data }) +export const verifyUnlockAction = (data: { planItemDetailId: number; lifelockId: number }) => + request.put({ url: `isolation/point/verifyUnLock`, data }) -export const unLockAction = (data: { planItemDetailId: number, planId: number, lifelockId: number }) => request.put({ url: `isolation/point/unLock`, data }) +export const unLockAction = (data: { + planItemDetailId: number + planId: number + lifelockId: number +}) => request.put({ url: `isolation/point/unLock`, data }) diff --git a/web/src/api/system/mail/account/index.ts b/web/src/api/system/mail/account/index.ts index 97b574a..f9c3340 100644 --- a/web/src/api/system/mail/account/index.ts +++ b/web/src/api/system/mail/account/index.ts @@ -38,7 +38,10 @@ export const deleteMailAccount = async (id: number) => { // 批量删除邮箱账号 export const deleteMailAccountList = async (ids: number[]) => { - return await request.delete({ url: '/system/mail-account/delete-list', params: { ids: ids.join(',') } }) + return await request.delete({ + url: '/system/mail-account/delete-list', + params: { ids: ids.join(',') } + }) } // 获得邮箱账号精简列表 diff --git a/web/src/api/system/mail/template/index.ts b/web/src/api/system/mail/template/index.ts index c6dae68..f9169ea 100644 --- a/web/src/api/system/mail/template/index.ts +++ b/web/src/api/system/mail/template/index.ts @@ -46,7 +46,10 @@ export const deleteMailTemplate = async (id: number) => { // 批量删除邮件模版 export const deleteMailTemplateList = async (ids: number[]) => { - return await request.delete({ url: '/system/mail-template/delete-list', params: { ids: ids.join(',') } }) + return await request.delete({ + url: '/system/mail-template/delete-list', + params: { ids: ids.join(',') } + }) } // 发送邮件 diff --git a/web/src/api/system/notify/template/index.ts b/web/src/api/system/notify/template/index.ts index c6bc548..2236e76 100644 --- a/web/src/api/system/notify/template/index.ts +++ b/web/src/api/system/notify/template/index.ts @@ -45,7 +45,10 @@ export const deleteNotifyTemplate = async (id: number) => { // 批量删除站内信模板 export const deleteNotifyTemplateList = async (ids: number[]) => { - return await request.delete({ url: '/system/notify-template/delete-list', params: { ids: ids.join(',') } }) + return await request.delete({ + url: '/system/notify-template/delete-list', + params: { ids: ids.join(',') } + }) } // 发送站内信 diff --git a/web/src/api/system/oauth2/client.ts b/web/src/api/system/oauth2/client.ts index 348aed5..d869f48 100644 --- a/web/src/api/system/oauth2/client.ts +++ b/web/src/api/system/oauth2/client.ts @@ -48,5 +48,8 @@ export const deleteOAuth2Client = (id: number) => { // 批量删除 OAuth2 客户端 export const deleteOAuth2ClientList = (ids: number[]) => { - return request.delete({ url: '/system/oauth2-client/delete-list', params: { ids: ids.join(',') } }) + return request.delete({ + url: '/system/oauth2-client/delete-list', + params: { ids: ids.join(',') } + }) } diff --git a/web/src/api/system/tenantPackage/index.ts b/web/src/api/system/tenantPackage/index.ts index 49d9d40..ac0017c 100644 --- a/web/src/api/system/tenantPackage/index.ts +++ b/web/src/api/system/tenantPackage/index.ts @@ -39,7 +39,10 @@ export const deleteTenantPackage = (id: number) => { // 批量删除租户套餐 export const deleteTenantPackageList = (ids: number[]) => { - return request.delete({ url: '/system/tenant-package/delete-list', params: { ids: ids.join(',') } }) + return request.delete({ + url: '/system/tenant-package/delete-list', + params: { ids: ids.join(',') } + }) } // 获取租户套餐精简信息列表 diff --git a/web/src/assets/imgs/iot/device.png b/web/src/assets/imgs/iot/device.png new file mode 100644 index 0000000..c5d50d2 Binary files /dev/null and b/web/src/assets/imgs/iot/device.png differ diff --git a/web/src/components/Cropper/src/CopperModal.vue b/web/src/components/Cropper/src/CopperModal.vue index d9a4e34..d6f61ed 100644 --- a/web/src/components/Cropper/src/CopperModal.vue +++ b/web/src/components/Cropper/src/CopperModal.vue @@ -208,7 +208,8 @@ $prefix-cls: #{$namespace}-cropper-am; &-cropper { height: 300px; background: #eee; - background-image: linear-gradient( + background-image: + linear-gradient( 45deg, rgb(0 0 0 / 25%) 25%, transparent 0, diff --git a/web/src/components/Descriptions/src/Descriptions.vue b/web/src/components/Descriptions/src/Descriptions.vue index 184d95c..ad9711c 100644 --- a/web/src/components/Descriptions/src/Descriptions.vue +++ b/web/src/components/Descriptions/src/Descriptions.vue @@ -130,9 +130,7 @@ const toggleClick = () => { - {{ - item.mappedField ? data[item.mappedField] : data[item.field] - }} + {{ item.mappedField ? data[item.mappedField] : data[item.field] }} diff --git a/web/src/components/DiyEditor/components/mobile/Carousel/property.vue b/web/src/components/DiyEditor/components/mobile/Carousel/property.vue index 8da98ff..d2524f6 100644 --- a/web/src/components/DiyEditor/components/mobile/Carousel/property.vue +++ b/web/src/components/DiyEditor/components/mobile/Carousel/property.vue @@ -17,7 +17,12 @@ - px + + px diff --git a/web/src/components/DiyEditor/components/mobile/MenuSwiper/index.vue b/web/src/components/DiyEditor/components/mobile/MenuSwiper/index.vue index fc6d718..e4a713b 100644 --- a/web/src/components/DiyEditor/components/mobile/MenuSwiper/index.vue +++ b/web/src/components/DiyEditor/components/mobile/MenuSwiper/index.vue @@ -39,7 +39,7 @@ - + diff --git a/web/src/components/DiyEditor/components/mobile/PromotionPoint/config.ts b/web/src/components/DiyEditor/components/mobile/PromotionPoint/config.ts index 75aa0ff..46004cd 100644 --- a/web/src/components/DiyEditor/components/mobile/PromotionPoint/config.ts +++ b/web/src/components/DiyEditor/components/mobile/PromotionPoint/config.ts @@ -1,4 +1,4 @@ -import {ComponentStyle, DiyComponent} from '@/components/DiyEditor/util' +import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util' /** 积分商城属性 */ export interface PromotionPointProperty { diff --git a/web/src/components/DiyEditor/components/mobile/TitleBar/property.vue b/web/src/components/DiyEditor/components/mobile/TitleBar/property.vue index 5eebb15..696b7c8 100644 --- a/web/src/components/DiyEditor/components/mobile/TitleBar/property.vue +++ b/web/src/components/DiyEditor/components/mobile/TitleBar/property.vue @@ -31,13 +31,7 @@ /> - + diff --git a/web/src/components/FormCreate/src/type/index.ts b/web/src/components/FormCreate/src/type/index.ts index 711a88d..aa603df 100644 --- a/web/src/components/FormCreate/src/type/index.ts +++ b/web/src/components/FormCreate/src/type/index.ts @@ -46,6 +46,6 @@ export interface SelectRuleOption { label: string // label 名称 name: string // 组件名称 icon: string // 组件图标 - props?: any[], // 组件规则 + props?: any[] // 组件规则 event?: any[] // 事件配置 } diff --git a/web/src/components/FormCreate/src/utils/index.ts b/web/src/components/FormCreate/src/utils/index.ts index a2b3e67..53b60d8 100644 --- a/web/src/components/FormCreate/src/utils/index.ts +++ b/web/src/components/FormCreate/src/utils/index.ts @@ -19,7 +19,7 @@ export const localeProps = (t, prefix, rules) => { /** * 解析表单组件的 field, title 等字段(递归,如果组件包含子组件) - * + * * @param rule 组件的生成规则 https://www.form-create.com/v3/guide/rule * @param fields 解析后表单组件字段 * @param parentTitle 如果是子表单,子表单的标题,默认为空 diff --git a/web/src/components/Icon/src/IconSelect.vue b/web/src/components/Icon/src/IconSelect.vue index 76cc6d5..dd4a437 100644 --- a/web/src/components/Icon/src/IconSelect.vue +++ b/web/src/components/Icon/src/IconSelect.vue @@ -125,7 +125,12 @@ watch( - + - + diff --git a/web/src/components/Map/index.vue b/web/src/components/Map/index.vue index 9d93743..22e74fa 100644 --- a/web/src/components/Map/index.vue +++ b/web/src/components/Map/index.vue @@ -1,5 +1,5 @@ - + diff --git a/web/src/components/SimpleProcessDesignerV2/src/index.ts b/web/src/components/SimpleProcessDesignerV2/src/index.ts index 88de07f..cacfbc1 100644 --- a/web/src/components/SimpleProcessDesignerV2/src/index.ts +++ b/web/src/components/SimpleProcessDesignerV2/src/index.ts @@ -2,4 +2,4 @@ import SimpleProcessDesigner from './SimpleProcessDesigner.vue' import SimpleProcessViewer from './SimpleProcessViewer.vue' import '../theme/simple-process-designer.scss' -export { SimpleProcessDesigner, SimpleProcessViewer} +export { SimpleProcessDesigner, SimpleProcessViewer } diff --git a/web/src/components/SimpleProcessDesignerV2/src/nodes-config/ChildProcessNodeConfig.vue b/web/src/components/SimpleProcessDesignerV2/src/nodes-config/ChildProcessNodeConfig.vue index 7ec382f..e0542de 100644 --- a/web/src/components/SimpleProcessDesignerV2/src/nodes-config/ChildProcessNodeConfig.vue +++ b/web/src/components/SimpleProcessDesignerV2/src/nodes-config/ChildProcessNodeConfig.vue @@ -295,10 +295,20 @@ /> - + - + - + { configForm.value.outVariables = node.childProcessSetting.outVariables // 6. 发起人设置 configForm.value.startUserType = node.childProcessSetting.startUserSetting.type - configForm.value.startUserEmptyType = node.childProcessSetting.startUserSetting.emptyType ?? ChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER + configForm.value.startUserEmptyType = + node.childProcessSetting.startUserSetting.emptyType ?? + ChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER configForm.value.startUserFormField = node.childProcessSetting.startUserSetting.formField ?? '' // 7. 超时设置 configForm.value.timeoutEnable = node.childProcessSetting.timeoutSetting.enable ?? false diff --git a/web/src/components/SimpleProcessDesignerV2/src/nodes/EndEventNode.vue b/web/src/components/SimpleProcessDesignerV2/src/nodes/EndEventNode.vue index 0af0310..ef43e05 100644 --- a/web/src/components/SimpleProcessDesignerV2/src/nodes/EndEventNode.vue +++ b/web/src/components/SimpleProcessDesignerV2/src/nodes/EndEventNode.vue @@ -1,63 +1,67 @@ - + 结束 - - - - - - - {{ scope.row.assigneeUser?.deptName || scope.row.ownerUser?.deptName }} - - - - - - - - - - - - - {{ formatPast2(scope.row.durationInMillis) }} - - - - - + + + + + + + {{ scope.row.assigneeUser?.deptName || scope.row.ownerUser?.deptName }} + + + + + + + + + + + + + {{ formatPast2(scope.row.durationInMillis) }} + + + + + diff --git a/web/src/components/bpmnProcessDesigner/package/theme/index.scss b/web/src/components/bpmnProcessDesigner/package/theme/index.scss index 2404760..c320d08 100644 --- a/web/src/components/bpmnProcessDesigner/package/theme/index.scss +++ b/web/src/components/bpmnProcessDesigner/package/theme/index.scss @@ -2,14 +2,15 @@ @use './process-panel.scss'; $success-color: #4eb819; -$primary-color: #409EFF; -$danger-color: #F56C6C; +$primary-color: #409eff; +$danger-color: #f56c6c; $cancel-color: #909399; .process-viewer { position: relative; - border: 1px solid #EFEFEF; - background: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+') repeat!important; + border: 1px solid #efefef; + background: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+') + repeat !important; .success-arrow { fill: $success-color; @@ -23,7 +24,7 @@ $cancel-color: #909399; .success.djs-connection { .djs-visual path { - stroke: $success-color!important; + stroke: $success-color !important; //marker-end: url(#sequenceflow-end-white-success)!important; } } @@ -36,82 +37,84 @@ $cancel-color: #909399; .success.djs-shape { .djs-visual rect { - stroke: $success-color!important; - fill: $success-color!important; - fill-opacity: 0.15!important; + stroke: $success-color !important; + fill: $success-color !important; + fill-opacity: 0.15 !important; } .djs-visual polygon { - stroke: $success-color!important; + stroke: $success-color !important; } .djs-visual path:nth-child(2) { - stroke: $success-color!important; - fill: $success-color!important; + stroke: $success-color !important; + fill: $success-color !important; } .djs-visual circle { - stroke: $success-color!important; - fill: $success-color!important; - fill-opacity: 0.15!important; + stroke: $success-color !important; + fill: $success-color !important; + fill-opacity: 0.15 !important; } } .primary.djs-shape { .djs-visual rect { - stroke: $primary-color!important; - fill: $primary-color!important; - fill-opacity: 0.15!important; + stroke: $primary-color !important; + fill: $primary-color !important; + fill-opacity: 0.15 !important; } .djs-visual polygon { - stroke: $primary-color!important; + stroke: $primary-color !important; } .djs-visual circle { - stroke: $primary-color!important; - fill: $primary-color!important; - fill-opacity: 0.15!important; + stroke: $primary-color !important; + fill: $primary-color !important; + fill-opacity: 0.15 !important; } } .danger.djs-shape { .djs-visual rect { - stroke: $danger-color!important; - fill: $danger-color!important; - fill-opacity: 0.15!important; + stroke: $danger-color !important; + fill: $danger-color !important; + fill-opacity: 0.15 !important; } .djs-visual polygon { - stroke: $danger-color!important; + stroke: $danger-color !important; } .djs-visual circle { - stroke: $danger-color!important; - fill: $danger-color!important; - fill-opacity: 0.15!important; + stroke: $danger-color !important; + fill: $danger-color !important; + fill-opacity: 0.15 !important; } } .cancel.djs-shape { .djs-visual rect { - stroke: $cancel-color!important; - fill: $cancel-color!important; - fill-opacity: 0.15!important; + stroke: $cancel-color !important; + fill: $cancel-color !important; + fill-opacity: 0.15 !important; } .djs-visual polygon { - stroke: $cancel-color!important; + stroke: $cancel-color !important; } .djs-visual circle { - stroke: $cancel-color!important; - fill: $cancel-color!important; - fill-opacity: 0.15!important; + stroke: $cancel-color !important; + fill: $cancel-color !important; + fill-opacity: 0.15 !important; } } } -.process-viewer .djs-tooltip-container, .process-viewer .djs-overlay-container, .process-viewer .djs-palette { +.process-viewer .djs-tooltip-container, +.process-viewer .djs-overlay-container, +.process-viewer .djs-palette { display: none; } diff --git a/web/src/hooks/web/useWatermark.ts b/web/src/hooks/web/useWatermark.ts index 028926b..aa604e0 100644 --- a/web/src/hooks/web/useWatermark.ts +++ b/web/src/hooks/web/useWatermark.ts @@ -8,7 +8,7 @@ export function useWatermark(appendEl: HTMLElement | null = document.body) { const id = domSymbol.toString() const appStore = useAppStore() let watermarkStr = '' - + const clear = () => { const domId = document.getElementById(id) if (domId) { diff --git a/web/src/layout/components/Footer/src/Footer.vue b/web/src/layout/components/Footer/src/Footer.vue index 5ea6b5e..2f3dea8 100644 --- a/web/src/layout/components/Footer/src/Footer.vue +++ b/web/src/layout/components/Footer/src/Footer.vue @@ -22,6 +22,8 @@ const currentYear = computed(() => new Date().getFullYear()) :class="prefixCls" class="h-[var(--app-footer-height)] bg-[var(--app-content-bg-color)] text-center leading-[var(--app-footer-height)] text-[var(--el-text-color-placeholder)] dark:bg-[var(--el-bg-color)] overflow-hidden" > - Copyright ©{{ currentYear }} {{ title }} 京公网安备 11010702002311号 + Copyright ©{{ currentYear }} {{ title }} 京公网安备 11010702002311号 diff --git a/web/src/layout/components/Setting/src/Setting.vue b/web/src/layout/components/Setting/src/Setting.vue index 2973674..005e9fb 100644 --- a/web/src/layout/components/Setting/src/Setting.vue +++ b/web/src/layout/components/Setting/src/Setting.vue @@ -297,6 +297,6 @@ $prefix-cls: #{$namespace}-setting; .#{$prefix-cls} { border-radius: 6px 0 0 6px; - z-index: 1200;/*修正没有z-index会被表格层覆盖,值不要超过4000*/ + z-index: 1200; /*修正没有z-index会被表格层覆盖,值不要超过4000*/ } diff --git a/web/src/layout/components/TenantVisit/index.vue b/web/src/layout/components/TenantVisit/index.vue index 2d7dced..07adfa6 100644 --- a/web/src/layout/components/TenantVisit/index.vue +++ b/web/src/layout/components/TenantVisit/index.vue @@ -1,8 +1,14 @@ 1" filterable placeholder="请选择租户" class="!w-180px" v-model="value" - @change="handleChange" clearable> + v-if="tenants.length > 1" + filterable + placeholder="请选择租户" + class="!w-180px" + v-model="value" + @change="handleChange" + clearable + > diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts index 6bd01bc..4c64e9f 100644 --- a/web/src/locales/en.ts +++ b/web/src/locales/en.ts @@ -141,10 +141,10 @@ export default { qrcode: 'Scan the QR code to log in', btnRegister: 'Sign up', SmsSendMsg: 'code has been sent', - resetPassword: "Reset Password", - resetPasswordSuccess: "Reset Password Success", - invalidTenantName:"Invalid Tenant Name", - mobile:"Mobile", + resetPassword: 'Reset Password', + resetPasswordSuccess: 'Reset Password Success', + invalidTenantName: 'Invalid Tenant Name', + mobile: 'Mobile' }, captcha: { verification: 'Please complete security verification', @@ -424,7 +424,7 @@ export default { info: { title: 'Basic Information', basicInfo: 'Basic Information', - resetPwd: 'Reset Password', + resetPwd: 'Reset Password' }, rules: { nickname: 'Please Enter User Nickname', diff --git a/web/src/locales/zh-CN.ts b/web/src/locales/zh-CN.ts index e711f97..f74b278 100644 --- a/web/src/locales/zh-CN.ts +++ b/web/src/locales/zh-CN.ts @@ -416,7 +416,7 @@ export default { info: { title: '基本信息', basicInfo: '基本设置', - resetPwd: '密码设置', + resetPwd: '密码设置' }, rules: { nickname: '请输入用户昵称', diff --git a/web/src/main.ts b/web/src/main.ts index 3022fea..905c032 100644 --- a/web/src/main.ts +++ b/web/src/main.ts @@ -40,9 +40,9 @@ import './permission' import Logger from '@/utils/Logger' import VueDOMPurifyHTML from 'vue-dompurify-html' // 解决v-html 的安全隐患 import VConsole from 'vconsole' -if (import.meta.env.VITE_DEV === 'true') { +if (import.meta.env.VITE_DEV === 'true' && import.meta.env.VITE_IS_TAURI === 'false') { // eslint-disable-next-line no-unused-vars - const vConsole = new VConsole(); + const vConsole = new VConsole() } // 创建实例 const setupAll = async () => { diff --git a/web/src/permission.ts b/web/src/permission.ts index c915201..14b0adc 100644 --- a/web/src/permission.ts +++ b/web/src/permission.ts @@ -47,13 +47,7 @@ const parseURL = ( } // 路由不重定向白名单 -const whiteList = [ - '/login', - '/auth-redirect', - '/bind', - '/register', - '/oauthLogin/gitee' -] +const whiteList = ['/login', '/auth-redirect', '/bind', '/register', '/oauthLogin/gitee'] // 路由加载前 router.beforeEach(async (to, from, next) => { diff --git a/web/src/router/modules/remaining.ts b/web/src/router/modules/remaining.ts index 02ea675..bb5b2a8 100644 --- a/web/src/router/modules/remaining.ts +++ b/web/src/router/modules/remaining.ts @@ -363,7 +363,7 @@ const remainingRouter: AppRouteRecordRaw[] = [ } } ] - }, + } ] export default remainingRouter diff --git a/web/src/store/modules/app.ts b/web/src/store/modules/app.ts index 2ef653e..9e92356 100644 --- a/web/src/store/modules/app.ts +++ b/web/src/store/modules/app.ts @@ -39,6 +39,7 @@ interface AppState { theme: ThemeTypes fixedMenu: boolean isWorkWechat: boolean + isTauri: boolean } export const useAppStore = defineStore('app', { @@ -46,20 +47,23 @@ export const useAppStore = defineStore('app', { return { userInfo: 'userInfo', // 登录信息存储字段-建议每个项目换一个字段,避免与其他项目冲突 sizeMap: ['default', 'large', 'small'], + // 平台相关 + isWorkWechat: false, // 是否是工作微信 + isTauri: import.meta.env.VITE_IS_TAURI === 'true', // 是否是tauri mobile: false, // 是否是移动端 + title: import.meta.env.VITE_APP_TITLE, // 标题 pageLoading: false, // 路由跳转loading - breadcrumb: true, // 面包屑 breadcrumbIcon: true, // 面包屑图标 collapse: false, // 折叠菜单 uniqueOpened: true, // 是否只保持一个子菜单的展开 hamburger: true, // 折叠图标 screenfull: true, // 全屏图标 - search: true, // 搜索图标 - size: true, // 尺寸图标 - locale: true, // 多语言图标 - message: true, // 消息图标 + search: false, // 搜索图标 + size: false, // 尺寸图标 + locale: false, // 多语言图标 + message: false, // 消息图标 tagsView: true, // 标签页 tagsViewImmerse: false, // 标签页沉浸 tagsViewIcon: true, // 是否显示标签图标 @@ -68,13 +72,12 @@ export const useAppStore = defineStore('app', { footer: true, // 显示页脚 greyMode: false, // 是否开始灰色模式,用于特殊悼念日 fixedMenu: wsCache.get('fixedMenu') || false, // 是否固定菜单 - isWorkWechat: false, // 是否是工作微信 layout: wsCache.get(CACHE_KEY.LAYOUT) || 'classic', // layout布局 isDark: wsCache.get(CACHE_KEY.IS_DARK) || false, // 是否是暗黑模式 currentSize: wsCache.get('default') || 'default', // 组件尺寸 theme: wsCache.get(CACHE_KEY.THEME) || { // 主题色 - elColorPrimary: '#409eff', + elColorPrimary: '#ff9800', // 左侧菜单边框颜色 leftMenuBorderColor: 'inherit', // 左侧菜单背景颜色 diff --git a/web/src/store/modules/elLock.ts b/web/src/store/modules/elLock.ts index ea698d6..566525d 100644 --- a/web/src/store/modules/elLock.ts +++ b/web/src/store/modules/elLock.ts @@ -1,10 +1,7 @@ import { defineStore } from 'pinia' import { store } from '@/store' import { UserVO } from '@/api/system/user' -import { - getAllUser, - getAllBaseData -} from '@/api/lock' +import { getAllUser, getAllBaseData } from '@/api/lock' import { Lock } from '@/api/electron/lock' import { Point } from '@/api/isolation/point' import { Plan } from '@/api/isolation/plan' @@ -40,8 +37,8 @@ export const useElLockStore = defineStore('elLock', { planItems: [], planItemDetails: [], planLifeLocks: [], - plan2DetailTree: [],//plan item itemdetail lock lifelock - plan2ItemTree: [], //plan item + plan2DetailTree: [], //plan item itemdetail lock lifelock + plan2ItemTree: [] //plan item } }, getters: {}, @@ -62,19 +59,25 @@ export const useElLockStore = defineStore('elLock', { this.locks = res.lockList this.planItems = res.planItemList this.planItemDetails = res.planItemDetailList.map((detail: PlanItemDetail) => { - const item: PlanItem | undefined = this.planItems.find(item => item.id === detail.isolationPlanItemId) + const item: PlanItem | undefined = this.planItems.find( + (item) => item.id === detail.isolationPlanItemId + ) return { ...detail, - planId: item?.isolationPlanId, + planId: item?.isolationPlanId } }) this.planLifeLocks = res.planLifeLockList.map((lock: PlanLifeLock) => { - const detail: PlanItemDetail | undefined = this.planItemDetails.find(detail => detail.id === lock.isolationPlanItemDetailId) - const item: PlanItem | undefined = this.planItems.find(item => item.id === detail?.isolationPlanItemId) + const detail: PlanItemDetail | undefined = this.planItemDetails.find( + (detail) => detail.id === lock.isolationPlanItemDetailId + ) + const item: PlanItem | undefined = this.planItems.find( + (item) => item.id === detail?.isolationPlanItemId + ) return { ...lock, planId: item?.isolationPlanId, - itemId: item?.id, + itemId: item?.id } }) this.isolationPlans = res.planList @@ -92,26 +95,32 @@ export const useElLockStore = defineStore('elLock', { } }, getPlan2DetailTree() { - const planlist = cloneDeep(this.isolationPlans).filter(plan => plan.status == 0) + const planlist = cloneDeep(this.isolationPlans).filter((plan) => plan.status == 0) const itemlist = cloneDeep(this.planItems) const detaillist = cloneDeep(this.planItemDetails) const lifelocklist = cloneDeep(this.planLifeLocks) planlist.forEach((plan: any) => { - const items = itemlist.filter(item => item.isolationPlanId === plan.id) + const items = itemlist.filter((item) => item.isolationPlanId === plan.id) plan.planItem = items items.forEach((item: any) => { - const details = detaillist.filter(detail => detail.isolationPlanItemId === item.id).map(deteail => { - const lock = this.locks.find(lock => lock.id === deteail.lockId) - const isolationPoint = this.isolationPoints.find(point => point.id === deteail.isolationPointId) - return { - ...deteail, - lock, - isolationPoint - } - }) + const details = detaillist + .filter((detail) => detail.isolationPlanItemId === item.id) + .map((deteail) => { + const lock = this.locks.find((lock) => lock.id === deteail.lockId) + const isolationPoint = this.isolationPoints.find( + (point) => point.id === deteail.isolationPointId + ) + return { + ...deteail, + lock, + isolationPoint + } + }) item.planItemDetail = details details.forEach((detail: any) => { - const lifelock = lifelocklist.filter(lifelock => lifelock.isolationPlanItemDetailId === detail.id) + const lifelock = lifelocklist.filter( + (lifelock) => lifelock.isolationPlanItemDetailId === detail.id + ) detail.itemLifeLock = lifelock }) }) diff --git a/web/src/utils/bluetooth.ts b/web/src/utils/bluetooth.ts index 4e34b99..4921887 100644 --- a/web/src/utils/bluetooth.ts +++ b/web/src/utils/bluetooth.ts @@ -5,23 +5,23 @@ // 声明微信小程序API类型 declare const wx: { - onBLECharacteristicValueChange: (callback: (res: { value: ArrayBuffer }) => void) => void; + onBLECharacteristicValueChange: (callback: (res: { value: ArrayBuffer }) => void) => void writeBLECharacteristicValue: (options: { - deviceId: string; - serviceId: string; - characteristicId: string; - value: ArrayBuffer; - success?: (res: any) => void; - fail?: (error: any) => void; - }) => void; -}; + deviceId: string + serviceId: string + characteristicId: string + value: ArrayBuffer + success?: (res: any) => void + fail?: (error: any) => void + }) => void +} // 全局变量 -let deviceId = ''; -const serviceId = '0000FFF0-0000-1000-8000-00805F9B34FB'; -const characteristicId = '0000FFF2-0000-1000-8000-00805F9B34FB'; -let callback: any = ''; -let taskTime = 10; +let deviceId = '' +const serviceId = '0000FFF0-0000-1000-8000-00805F9B34FB' +const characteristicId = '0000FFF2-0000-1000-8000-00805F9B34FB' +let callback: any = '' +let taskTime = 10 /** * 创建指令 @@ -30,18 +30,18 @@ let taskTime = 10; * @returns 构建好的指令数组 */ function createInstruct(command: number, data: number[]): number[] { - const header = [0xaa, 0xbb]; - let instruction: number[] = []; - let payload: number[] = []; - - instruction = instruction.concat(header); - payload.push(command); - payload = payload.concat(data); - payload.splice(payload.length + 2); - payload = doCrc(payload); - instruction = instruction.concat(payload); - - return instruction; + const header = [0xaa, 0xbb] + let instruction: number[] = [] + let payload: number[] = [] + + instruction = instruction.concat(header) + payload.push(command) + payload = payload.concat(data) + payload.splice(payload.length + 2) + payload = doCrc(payload) + instruction = instruction.concat(payload) + + return instruction } /** @@ -50,8 +50,8 @@ function createInstruct(command: number, data: number[]): number[] { * @returns 添加CRC后的数据 */ function doCrc(data: number[]): number[] { - const crcResult = crc16ab(data, true, false); - return data.concat(crcResult); + const crcResult = crc16ab(data, true, false) + return data.concat(crcResult) } /** @@ -60,11 +60,11 @@ function doCrc(data: number[]): number[] { * @returns Uint8数组 */ function strToUint8(str: string): number[] { - const result: number[] = []; + const result: number[] = [] for (let i = 0; i < str.length; i++) { - result[i] = str.charCodeAt(i); + result[i] = str.charCodeAt(i) } - return result; + return result } /** @@ -75,24 +75,24 @@ function strToUint8(str: string): number[] { * @returns CRC校验结果 */ function crc16ab(data: any[], reverseResult: boolean, isHexString: boolean): number[] { - let crc = 0xffff; - const polynomial = 0x1021; - + let crc = 0xffff + const polynomial = 0x1021 + for (let i = 0; i < data.length; i++) { - const byte = isHexString ? parseInt(data[i], 16) : data[i]; + const byte = isHexString ? parseInt(data[i], 16) : data[i] for (let bit = 0; bit < 8; bit++) { - const bitValue = (byte >> (7 - bit) & 1) === 1; - const crcBit = (crc >> 15 & 1) === 1; - crc <<= 1; + const bitValue = ((byte >> (7 - bit)) & 1) === 1 + const crcBit = ((crc >> 15) & 1) === 1 + crc <<= 1 if (crcBit !== bitValue) { - crc ^= polynomial; + crc ^= polynomial } } } - - const high = (crc & 0xff00) >> 8; - const low = crc & 0xff; - return reverseResult ? [low, high] : [high, low]; + + const high = (crc & 0xff00) >> 8 + const low = crc & 0xff + return reverseResult ? [low, high] : [high, low] } /** @@ -102,11 +102,11 @@ function crc16ab(data: any[], reverseResult: boolean, isHexString: boolean): num * @returns 分块后的数组 */ function arrayChunks(array: any[], chunkSize: number): any[] { - const chunks: any[] = []; + const chunks: any[] = [] for (let i = 0; i < array.length; i += chunkSize) { - chunks.push(array.slice(i, i + chunkSize)); + chunks.push(array.slice(i, i + chunkSize)) } - return chunks; + return chunks } /** @@ -115,9 +115,9 @@ function arrayChunks(array: any[], chunkSize: number): any[] { * @returns 十六进制CRC值 */ function getCrcHex(data: any[]): string { - const crcArray = crc16ab(data, true, true); - const hexString = ab2hex(crcArray).join(''); - return hexString; + const crcArray = crc16ab(data, true, true) + const hexString = ab2hex(crcArray).join('') + return hexString } /** @@ -127,12 +127,12 @@ function getCrcHex(data: any[]): string { */ function ab2hex(arrayBuffer: ArrayBuffer | number[]): string[] { const hexArray = Array.prototype.map.call( - new Uint8Array(arrayBuffer), - function(byte: number): string { - return ('00' + byte.toString(16)).slice(-2); + new Uint8Array(arrayBuffer), + function (byte: number): string { + return ('00' + byte.toString(16)).slice(-2) } - ) as string[]; - return hexArray; + ) as string[] + return hexArray } /** @@ -141,11 +141,11 @@ function ab2hex(arrayBuffer: ArrayBuffer | number[]): string[] { * @returns 数组 */ function hex2ab(hexString: string[]): number[] { - const result: number[] = []; + const result: number[] = [] for (let i = 0; i < hexString.length; i++) { - result.push(parseInt(hexString[i], 16)); + result.push(parseInt(hexString[i], 16)) } - return result; + return result } /** @@ -153,13 +153,13 @@ function hex2ab(hexString: string[]): number[] { * @param instructData 指令数据 */ function dealInstruct(instructData: any): void { - const hexData = ab2hex(instructData); - + const hexData = ab2hex(instructData) + while (hexData.indexOf('aa') >= 0) { - const startIndex = hexData.indexOf('aa'); - const length = parseInt(hexData[startIndex + 2], 16); - const instructHex = hexData.slice(startIndex, length + 3); - callback(resolutionInstruct(instructHex)); + const startIndex = hexData.indexOf('aa') + const length = parseInt(hexData[startIndex + 2], 16) + const instructHex = hexData.slice(startIndex, length + 3) + callback(resolutionInstruct(instructHex)) } } @@ -169,17 +169,17 @@ function dealInstruct(instructData: any): void { * @returns 是否有效 */ function checkValidity(data: any[]): boolean { - if (data.length < 2) return false; - - const chunks = arrayChunks(data, data.length - 2); - const crcData = chunks.pop(); - if (!crcData) return false; - const receivedCrc = crcData[0] + crcData[1]; - const payload = chunks.pop(); - if (!payload) return false; - const calculatedCrc = getCrcHex(payload); - - return receivedCrc === calculatedCrc; + if (data.length < 2) return false + + const chunks = arrayChunks(data, data.length - 2) + const crcData = chunks.pop() + if (!crcData) return false + const receivedCrc = crcData[0] + crcData[1] + const payload = chunks.pop() + if (!payload) return false + const calculatedCrc = getCrcHex(payload) + + return receivedCrc === calculatedCrc } /** @@ -188,11 +188,11 @@ function checkValidity(data: any[]): boolean { * @param callbackParam 回调函数 */ function init(deviceIdParam: string, callbackParam: any): void { - deviceId = deviceIdParam; - callback = callbackParam; - wx.onBLECharacteristicValueChange(function(res: any) { - dealInstruct(res.value); - }); + deviceId = deviceIdParam + callback = callbackParam + wx.onBLECharacteristicValueChange(function (res: any) { + dealInstruct(res.value) + }) } /** @@ -201,51 +201,59 @@ function init(deviceIdParam: string, callbackParam: any): void { * @returns 解析结果 */ function resolutionInstruct(instructArray: string[]): any { - if (instructArray.shift() != 'aa') return setCallData('-1', '-1', '数据错误'); - if (instructArray.shift() != 'bb') return setCallData('-1', '-1', '数据错误'); - + if (instructArray.shift() != 'aa') return setCallData('-1', '-1', '数据错误') + if (instructArray.shift() != 'bb') return setCallData('-1', '-1', '数据错误') + if (checkValidity(instructArray)) { - const command = parseInt(instructArray[1], 16); - + const command = parseInt(instructArray[1], 16) + if (command == 0x8) { // 锁状态指令 - if (instructArray[2] == '01') return setCallData('08', '01', '手柄打开'); - else if (instructArray[2] == '00') return setCallData('08', '00', '手柄关闭'); + if (instructArray[2] == '01') return setCallData('08', '01', '手柄打开') + else if (instructArray[2] == '00') return setCallData('08', '00', '手柄关闭') } else if (command == 0x7) { // 开锁指令 - if (instructArray[2] == '01') return setCallData('07', '01', '开锁成功'); - else if (instructArray[2] == '02') return setCallData('07', '00', '钥匙开锁'); + if (instructArray[2] == '01') return setCallData('07', '01', '开锁成功') + else if (instructArray[2] == '02') return setCallData('07', '00', '钥匙开锁') } else if (command == 0x12) { // 设置锁ID指令 - if (instructArray[2] == '01') return setCallData('12', '01', '修改成功'); - else if (instructArray[2] == '02') return setCallData('12', '00', '修改失败'); + if (instructArray[2] == '01') return setCallData('12', '01', '修改成功') + else if (instructArray[2] == '02') return setCallData('12', '00', '修改失败') } else if (command == 0x11) { // 获取锁ID指令 if (instructArray[2] == '01') { - const idData = instructArray.slice(3, 7); - const idArray = hexToAb(idData); - const lockId = byteToInt(idArray); - return setCallData('11', lockId, '获取成功'); + const idData = instructArray.slice(3, 7) + const idArray = hexToAb(idData) + const lockId = byteToInt(idArray) + return setCallData('11', lockId, '获取成功') } } else if (command == 0x10) { // 发送开锁密钥指令 - if (instructArray[2] == '01') return setCallData('10', '01', '开锁成功'); + if (instructArray[2] == '01') return setCallData('10', '01', '开锁成功') } else if (command == 0x5) { // 设置蓝牙名称指令 - return instructArray[2] == '01' ? setCallData('05', '01', '修改成功') : setCallData('05', '00', '修改失败'); + return instructArray[2] == '01' + ? setCallData('05', '01', '修改成功') + : setCallData('05', '00', '修改失败') } else if (command == 0xa) { // 获取当前时间指令 - const timeData = instructArray.slice(2, 7); - setTaskTime(timeData, taskTime, () => {}); + const timeData = instructArray.slice(2, 7) + setTaskTime(timeData, taskTime, () => {}) } else if (command == 0x22) { // 设置任务时间指令 - return instructArray[2] == '01' ? setCallData('22', '01', '修改成功') : setCallData('22', '00', '修改失败'); + return instructArray[2] == '01' + ? setCallData('22', '01', '修改成功') + : setCallData('22', '00', '修改失败') } else if (command == 0x23) { // 设置任务权限指令 - return instructArray[2] == '01' ? setCallData('23', '01', '修改成功') : setCallData('23', '00', '修改失败'); + return instructArray[2] == '01' + ? setCallData('23', '01', '修改成功') + : setCallData('23', '00', '修改失败') } else if (command == 0x2a) { // 清除任务权限指令 - return instructArray[2] == '01' ? setCallData('2A', '01', '清除成功') : setCallData('2A', '00', '清除失败'); + return instructArray[2] == '01' + ? setCallData('2A', '01', '清除成功') + : setCallData('2A', '00', '清除失败') } } } @@ -259,11 +267,11 @@ function resolutionInstruct(instructArray: string[]): any { */ function setCallData(code: string, data: any, msg: string): any { const callbackData = { - 'code': code, - 'data': data, - 'msg': msg - }; - return callbackData; + code: code, + data: data, + msg: msg + } + return callbackData } /** @@ -272,23 +280,23 @@ function setCallData(code: string, data: any, msg: string): any { * @param callback 回调函数 */ function submitInstructions(instruction: number[], callback: any): void { - const buffer = new Uint8Array(instruction).buffer; - + const buffer = new Uint8Array(instruction).buffer + if (buffer.byteLength > 0) { wx.writeBLECharacteristicValue({ - 'deviceId': deviceId, - 'serviceId': serviceId, - 'characteristicId': characteristicId, - 'value': buffer, - 'success'(res: any) { - callback(res); + deviceId: deviceId, + serviceId: serviceId, + characteristicId: characteristicId, + value: buffer, + success(res: any) { + callback(res) }, - 'fail'(error: any) { - callback(error); + fail(error: any) { + callback(error) } - }); + }) } else { - callback('参数错误'); + callback('参数错误') } } @@ -298,7 +306,7 @@ function submitInstructions(instruction: number[], callback: any): void { * @returns 是否为有效的函数 */ function checkCallback(callback: any): boolean { - return typeof callback == 'function'; + return typeof callback == 'function' } /** @@ -307,13 +315,13 @@ function checkCallback(callback: any): boolean { * @returns 执行结果 */ function openLock(callback: any): any { - const isValidCallback = checkCallback(callback); + const isValidCallback = checkCallback(callback) if (isValidCallback) { - const data = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]; - const instruction = createInstruct(0x7, data); - return submitInstructions(instruction, callback); + const data = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff] + const instruction = createInstruct(0x7, data) + return submitInstructions(instruction, callback) } else { - return '参数错误'; + return '参数错误' } } @@ -323,13 +331,13 @@ function openLock(callback: any): any { * @returns 执行结果 */ function sendOpenKey(callback: any): any { - const isValidCallback = checkCallback(callback); + const isValidCallback = checkCallback(callback) if (isValidCallback) { - const data = [0x32, 0x0, 0xff, 0xff, 0xff, 0xff]; - const instruction = createInstruct(0x10, data); - return submitInstructions(instruction, callback); + const data = [0x32, 0x0, 0xff, 0xff, 0xff, 0xff] + const instruction = createInstruct(0x10, data) + return submitInstructions(instruction, callback) } else { - return '参数错误'; + return '参数错误' } } @@ -339,13 +347,13 @@ function sendOpenKey(callback: any): any { * @returns 执行结果 */ function lockStatus(callback: any): any { - const isValidCallback = checkCallback(callback); + const isValidCallback = checkCallback(callback) if (isValidCallback) { - const data: number[] = []; - const instruction = createInstruct(0x8, data); - return submitInstructions(instruction, callback); + const data: number[] = [] + const instruction = createInstruct(0x8, data) + return submitInstructions(instruction, callback) } else { - return '参数错误'; + return '参数错误' } } @@ -355,9 +363,9 @@ function lockStatus(callback: any): any { * @returns 执行结果 */ function sendHeart(callback: any): any { - const data: number[] = []; - const instruction = createInstruct(0xff, data); - return submitInstructions(instruction, callback); + const data: number[] = [] + const instruction = createInstruct(0xff, data) + return submitInstructions(instruction, callback) } /** @@ -367,13 +375,13 @@ function sendHeart(callback: any): any { * @returns 执行结果 */ function setBluetoothName(name: string, callback: any): any { - const isValidCallback = checkCallback(callback); + const isValidCallback = checkCallback(callback) if (isValidCallback) { - const nameData = strToUint8(name); - const instruction = createInstruct(0x5, nameData); - return submitInstructions(instruction, callback); + const nameData = strToUint8(name) + const instruction = createInstruct(0x5, nameData) + return submitInstructions(instruction, callback) } else { - return '参数错误'; + return '参数错误' } } @@ -383,13 +391,13 @@ function setBluetoothName(name: string, callback: any): any { * @returns 执行结果 */ function getLockId(callback: any): any { - const isValidCallback = checkCallback(callback); + const isValidCallback = checkCallback(callback) if (isValidCallback) { - const data = [0xff, 0xff, 0xff, 0xff]; - const instruction = createInstruct(0x11, data); - return submitInstructions(instruction, callback); + const data = [0xff, 0xff, 0xff, 0xff] + const instruction = createInstruct(0x11, data) + return submitInstructions(instruction, callback) } else { - return '参数错误'; + return '参数错误' } } @@ -400,15 +408,15 @@ function getLockId(callback: any): any { * @returns 执行结果 */ function setLockId(lockId: number, callback: any): any { - const isValidCallback = checkCallback(callback); + const isValidCallback = checkCallback(callback) if (isValidCallback) { - const padding = [0xff, 0xff, 0xff, 0xff]; - const idBytes = intToByte4(lockId); - const data = idBytes.concat(padding); - const instruction = createInstruct(0x12, data); - return submitInstructions(instruction, callback); + const padding = [0xff, 0xff, 0xff, 0xff] + const idBytes = intToByte4(lockId) + const data = idBytes.concat(padding) + const instruction = createInstruct(0x12, data) + return submitInstructions(instruction, callback) } else { - return '参数错误'; + return '参数错误' } } @@ -418,8 +426,8 @@ function setLockId(lockId: number, callback: any): any { * @param callback 回调函数 */ function startTaskTime(time: number, callback: any): void { - taskTime = time; - getCurrentTime(callback); + taskTime = time + getCurrentTime(callback) } /** @@ -428,13 +436,13 @@ function startTaskTime(time: number, callback: any): void { * @returns 执行结果 */ function getCurrentTime(callback: any): any { - const isValidCallback = checkCallback(callback); + const isValidCallback = checkCallback(callback) if (isValidCallback) { - const data: number[] = []; - const instruction = createInstruct(0xa, data); - return submitInstructions(instruction, callback); + const data: number[] = [] + const instruction = createInstruct(0xa, data) + return submitInstructions(instruction, callback) } else { - return '参数错误'; + return '参数错误' } } @@ -446,15 +454,15 @@ function getCurrentTime(callback: any): any { * @returns 执行结果 */ function setTaskTime(timeHex: string[], timeValue: number, callback: any): any { - const isValidCallback = checkCallback(callback); + const isValidCallback = checkCallback(callback) if (isValidCallback) { - const timeData = hex2ab(timeHex); - const timeBytes = intToByte4R(timeValue); - const data = timeData.concat(timeBytes).concat([0xff]); - const instruction = createInstruct(0x22, data); - return submitInstructions(instruction, callback); + const timeData = hex2ab(timeHex) + const timeBytes = intToByte4R(timeValue) + const data = timeData.concat(timeBytes).concat([0xff]) + const instruction = createInstruct(0x22, data) + return submitInstructions(instruction, callback) } else { - return '参数错误'; + return '参数错误' } } @@ -466,16 +474,16 @@ function setTaskTime(timeHex: string[], timeValue: number, callback: any): any { * @returns 执行结果 */ function setTaskPrem(taskId: number, permission: number, callback: any): any { - const isValidCallback = checkCallback(callback); + const isValidCallback = checkCallback(callback) if (isValidCallback) { - const padding = [0xff, 0xff, 0xff, 0xff]; - const idBytes = intToByte4(taskId); - const data = idBytes.concat(padding); - data.push(permission); - const instruction = createInstruct(0x23, data); - return submitInstructions(instruction, callback); + const padding = [0xff, 0xff, 0xff, 0xff] + const idBytes = intToByte4(taskId) + const data = idBytes.concat(padding) + data.push(permission) + const instruction = createInstruct(0x23, data) + return submitInstructions(instruction, callback) } else { - return '参数错误'; + return '参数错误' } } @@ -485,13 +493,13 @@ function setTaskPrem(taskId: number, permission: number, callback: any): any { * @returns 执行结果 */ function cleanTaskPrem(callback: any): any { - const isValidCallback = checkCallback(callback); + const isValidCallback = checkCallback(callback) if (isValidCallback) { - const data: number[] = []; - const instruction = createInstruct(0x2a, data); - return submitInstructions(instruction, callback); + const data: number[] = [] + const instruction = createInstruct(0x2a, data) + return submitInstructions(instruction, callback) } else { - return '参数错误'; + return '参数错误' } } @@ -501,12 +509,12 @@ function cleanTaskPrem(callback: any): any { * @returns 字节数组 */ function intToByte4(value: number): number[] { - const bytes: number[] = []; - bytes[3] = value & 0xff; - bytes[2] = (value >> 8) & 0xff; - bytes[1] = (value >> 16) & 0xff; - bytes[0] = (value >> 24) & 0xff; - return bytes; + const bytes: number[] = [] + bytes[3] = value & 0xff + bytes[2] = (value >> 8) & 0xff + bytes[1] = (value >> 16) & 0xff + bytes[0] = (value >> 24) & 0xff + return bytes } /** @@ -515,12 +523,12 @@ function intToByte4(value: number): number[] { * @returns 字节数组 */ function intToByte4R(value: number): number[] { - const bytes: number[] = []; - bytes[0] = value & 0xff; - bytes[1] = (value >> 8) & 0xff; - bytes[2] = (value >> 16) & 0xff; - bytes[3] = (value >> 24) & 0xff; - return bytes; + const bytes: number[] = [] + bytes[0] = value & 0xff + bytes[1] = (value >> 8) & 0xff + bytes[2] = (value >> 16) & 0xff + bytes[3] = (value >> 24) & 0xff + return bytes } /** @@ -529,11 +537,11 @@ function intToByte4R(value: number): number[] { * @returns 整数值 */ function byteToInt(bytes: number[]): number { - const byte0 = bytes[3] & 0xff; - const byte1 = bytes[2] & 0xff; - const byte2 = bytes[1] & 0xff; - const byte3 = bytes[0] & 0xff; - return (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0; + const byte0 = bytes[3] & 0xff + const byte1 = bytes[2] & 0xff + const byte2 = bytes[1] & 0xff + const byte3 = bytes[0] & 0xff + return (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0 } /** @@ -542,24 +550,24 @@ function byteToInt(bytes: number[]): number { * @returns 数字数组 */ function hexToAb(hexArray: string[]): number[] { - const result: number[] = []; + const result: number[] = [] for (let i = 0; i < hexArray.length; i++) { - result.push(parseInt(hexArray[i], 16)); + result.push(parseInt(hexArray[i], 16)) } - return result; + return result } // 模块导出 module.exports = { - 'init': init, - 'openLock': openLock, - 'lockStatus': lockStatus, - 'sendHeart': sendHeart, - 'setBluetoothName': setBluetoothName, - 'sendOpenKey': sendOpenKey, - 'getLockId': getLockId, - 'setLockId': setLockId, - 'startTaskTime': startTaskTime, - 'setTaskPrem': setTaskPrem, - 'cleanTaskPrem': cleanTaskPrem -}; + init: init, + openLock: openLock, + lockStatus: lockStatus, + sendHeart: sendHeart, + setBluetoothName: setBluetoothName, + sendOpenKey: sendOpenKey, + getLockId: getLockId, + setLockId: setLockId, + startTaskTime: startTaskTime, + setTaskPrem: setTaskPrem, + cleanTaskPrem: cleanTaskPrem +} diff --git a/web/src/utils/color.ts b/web/src/utils/color.ts index 943be97..74e0fba 100644 --- a/web/src/utils/color.ts +++ b/web/src/utils/color.ts @@ -173,7 +173,6 @@ export const PREDEFINE_COLORS = [ '#711f57' ] - /** * Mixes two colors. * diff --git a/web/src/utils/is.ts b/web/src/utils/is.ts index 24d7191..e09fa56 100644 --- a/web/src/utils/is.ts +++ b/web/src/utils/is.ts @@ -100,7 +100,7 @@ export const isClient = !isServer export const isUrl = (path: string): boolean => { // fix:修复hash路由无法跳转的问题 const reg = - /(((^https?:(?:\/\/)?)(?:[-:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%#\/.\w-_]*)?\??(?:[-\+=&%@.\w_]*)#?(?:[\w]*))?)$/ + /(((^https?:(?:\/\/)?)(?:[-:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%#\/.\w-_]*)?\??(?:[-\+=&%@.\w_]*)#?(?:[\w]*))?)$/ return reg.test(path) } diff --git a/web/src/utils/lock.old.js b/web/src/utils/lock.old.js index 5c32c3c..e06aeb7 100644 --- a/web/src/utils/lock.old.js +++ b/web/src/utils/lock.old.js @@ -1,334 +1,346 @@ -var deviceId = ''; -var serviceId = '0000FFF0-0000-1000-8000-00805F9B34FB'; -var characteristicId = '0000FFF2-0000-1000-8000-00805F9B34FB'; -var callback = ''; -var taskTime = 10; +var deviceId = '' +var serviceId = '0000FFF0-0000-1000-8000-00805F9B34FB' +var characteristicId = '0000FFF2-0000-1000-8000-00805F9B34FB' +var callback = '' +var taskTime = 10 function createInstruct(command, data) { - let header = [0xaa, 0xbb]; - let instruction = []; - let payload = []; - instruction = instruction.concat(header); - payload.push(command); - payload = payload.concat(data); - payload.push(payload.length + 2); - payload = doCrc(payload); - instruction = instruction.concat(payload); - return instruction; + let header = [0xaa, 0xbb] + let instruction = [] + let payload = [] + instruction = instruction.concat(header) + payload.push(command) + payload = payload.concat(data) + payload.push(payload.length + 2) + payload = doCrc(payload) + instruction = instruction.concat(payload) + return instruction } function doCrc(data) { - let crc = crc16ab(data, true, false); - data = data.concat(crc); - return data; + let crc = crc16ab(data, true, false) + data = data.concat(crc) + return data } function strToUint8(str) { - let result = []; - for (let i = 0; i < str.length; i++) { - result[i] = str.charCodeAt(i); - } - return result; + let result = [] + for (let i = 0; i < str.length; i++) { + result[i] = str.charCodeAt(i) + } + return result } function crc16ab(data, swapBytes, isHex) { - let crc = 0xffff; - let polynomial = 0x1021; - for (let i = 0; i < data.length; i++) { - let byte = isHex ? parseInt(data[i], 16) : data[i]; - for (let j = 0; j < 8; j++) { - let bit = (byte >> (7 - j) & 0x1) === 0x1; - let crcBit = (crc >> 15 & 0x1) === 0x1; - crc <<= 1; - if (crcBit ^ bit) crc ^= polynomial; - } + let crc = 0xffff + let polynomial = 0x1021 + for (let i = 0; i < data.length; i++) { + let byte = isHex ? parseInt(data[i], 16) : data[i] + for (let j = 0; j < 8; j++) { + let bit = ((byte >> (7 - j)) & 0x1) === 0x1 + let crcBit = ((crc >> 15) & 0x1) === 0x1 + crc <<= 1 + if (crcBit ^ bit) crc ^= polynomial } - let highByte = (crc & 0xff00) >> 8; - let lowByte = crc & 0xff; - return swapBytes ? [lowByte, highByte] : [highByte, lowByte]; + } + let highByte = (crc & 0xff00) >> 8 + let lowByte = crc & 0xff + return swapBytes ? [lowByte, highByte] : [highByte, lowByte] } function arrayChunks(array, size) { - let chunks = []; - for (let i = 0; i < array.length; i += size) { - chunks.push(array.slice(i, i + size)); - } - return chunks; + let chunks = [] + for (let i = 0; i < array.length; i += size) { + chunks.push(array.slice(i, i + size)) + } + return chunks } function getCrcHex(data) { - let crc = crc16ab(data, true, true); - let hex = ab2hex(crc).join(''); - return hex; + let crc = crc16ab(data, true, true) + let hex = ab2hex(crc).join('') + return hex } function ab2hex(buffer) { - let hexArray = Array.prototype.map.call(new Uint8Array(buffer), function(byte) { - return ('00' + byte.toString(16)).slice(-2); - }); - return hexArray; + let hexArray = Array.prototype.map.call(new Uint8Array(buffer), function (byte) { + return ('00' + byte.toString(16)).slice(-2) + }) + return hexArray } function hex2ab(hexArray) { - let result = Array.prototype.map.call(new Uint8Array(hexArray), function(byte) { - return parseInt(byte, 16); - }); - return result; + let result = Array.prototype.map.call(new Uint8Array(hexArray), function (byte) { + return parseInt(byte, 16) + }) + return result } function dealInstruct(data) { - let hexData = ab2hex(data); - while (hexData.indexOf('aa') >= 0) { - let headerIndex = hexData.indexOf('aa'); - let length = parseInt(hexData[headerIndex + 2], 16); - let instruction = hexData.slice(headerIndex, length + 3); - callback(resolutionInstruct(instruction)); - } + let hexData = ab2hex(data) + while (hexData.indexOf('aa') >= 0) { + let headerIndex = hexData.indexOf('aa') + let length = parseInt(hexData[headerIndex + 2], 16) + let instruction = hexData.slice(headerIndex, length + 3) + callback(resolutionInstruct(instruction)) + } } function checkValidity(data) { - if (data.length < 2) return false; - let chunks = arrayChunks(data, data.length - 2); - let crcBytes = chunks.pop(); - let crcHex = crcBytes[0] + crcBytes[1]; - let dataToCheck = chunks.pop(); - let calculatedCrc = getCrcHex(dataToCheck); - return crcHex === calculatedCrc; + if (data.length < 2) return false + let chunks = arrayChunks(data, data.length - 2) + let crcBytes = chunks.pop() + let crcHex = crcBytes[0] + crcBytes[1] + let dataToCheck = chunks.pop() + let calculatedCrc = getCrcHex(dataToCheck) + return crcHex === calculatedCrc } function init(device, cb) { - deviceId = device; - callback = cb; - wx.onBLECharacteristicValueChange(function(res) { - dealInstruct(res.value); - }); + deviceId = device + callback = cb + wx.onBLECharacteristicValueChange(function (res) { + dealInstruct(res.value) + }) } function resolutionInstruct(instruction) { - if (instruction.shift() !== 'aa') return setCallData('-1', '-1', 'Data error'); - if (instruction.pop() !== 'bb') return setCallData('-1', '-1', 'Data error'); - if (checkValidity(instruction)) { - let command = parseInt(instruction[1], 16); - if (command === 0x8) { - if (instruction[2] === '01') return setCallData('08', '01', 'Lock opened successfully'); - else if (instruction[2] === '00') return setCallData('08', '00', 'Lock failed to open'); - } else if (command === 0x7) { - if (instruction[2] === '01') return setCallData('07', '01', 'Handle opened'); - else if (instruction[2] === '00') return setCallData('07', '00', 'Handle closed'); - } else if (command === 0x12) { - if (instruction[2] === '01') return setCallData('12', '01', 'Modification successful'); - else if (instruction[2] === '00') return setCallData('12', '00', 'Modification failed'); - } else if (command === 0x11) { - if (instruction[2] === '01') { - let lockIdBytes = instruction.slice(3, 7); - lockIdBytes = hexToAb(lockIdBytes); - let lockId = byteToInt(lockIdBytes); - return setCallData('11', lockId, 'Retrieved successfully'); - } - } else if (command === 0x10) { - if (instruction[2] === '01') return setCallData('10', '01', 'Key unlock successful'); - } else if (command === 0x5) { - return instruction[2] === '01' ? setCallData('05', '01', 'Modification successful') : setCallData('05', '00', 'Modification failed'); - } else if (command === 0xa) { - let timeData = instruction.slice(2, 7); - setTaskTime(timeData, taskTime, () => {}); - } else if (command === 0x22) { - return instruction[2] === '01' ? setCallData('22', '01', 'Modification successful') : setCallData('22', '00', 'Modification failed'); - } else if (command === 0x23) { - return instruction[2] === '01' ? setCallData('23', '01', 'Modification successful') : setCallData('23', '00', 'Modification failed'); - } else if (command === 0x2a) { - return instruction[2] === '01' ? setCallData('2A', '01', 'Clear successful') : setCallData('2A', '00', 'Clear failed'); - } + if (instruction.shift() !== 'aa') return setCallData('-1', '-1', 'Data error') + if (instruction.pop() !== 'bb') return setCallData('-1', '-1', 'Data error') + if (checkValidity(instruction)) { + let command = parseInt(instruction[1], 16) + if (command === 0x8) { + if (instruction[2] === '01') return setCallData('08', '01', 'Lock opened successfully') + else if (instruction[2] === '00') return setCallData('08', '00', 'Lock failed to open') + } else if (command === 0x7) { + if (instruction[2] === '01') return setCallData('07', '01', 'Handle opened') + else if (instruction[2] === '00') return setCallData('07', '00', 'Handle closed') + } else if (command === 0x12) { + if (instruction[2] === '01') return setCallData('12', '01', 'Modification successful') + else if (instruction[2] === '00') return setCallData('12', '00', 'Modification failed') + } else if (command === 0x11) { + if (instruction[2] === '01') { + let lockIdBytes = instruction.slice(3, 7) + lockIdBytes = hexToAb(lockIdBytes) + let lockId = byteToInt(lockIdBytes) + return setCallData('11', lockId, 'Retrieved successfully') + } + } else if (command === 0x10) { + if (instruction[2] === '01') return setCallData('10', '01', 'Key unlock successful') + } else if (command === 0x5) { + return instruction[2] === '01' + ? setCallData('05', '01', 'Modification successful') + : setCallData('05', '00', 'Modification failed') + } else if (command === 0xa) { + let timeData = instruction.slice(2, 7) + setTaskTime(timeData, taskTime, () => {}) + } else if (command === 0x22) { + return instruction[2] === '01' + ? setCallData('22', '01', 'Modification successful') + : setCallData('22', '00', 'Modification failed') + } else if (command === 0x23) { + return instruction[2] === '01' + ? setCallData('23', '01', 'Modification successful') + : setCallData('23', '00', 'Modification failed') + } else if (command === 0x2a) { + return instruction[2] === '01' + ? setCallData('2A', '01', 'Clear successful') + : setCallData('2A', '00', 'Clear failed') } + } } function setCallData(code, data, msg) { - return { code: code, data: data, msg: msg }; + return { code: code, data: data, msg: msg } } function submitInstructions(instruction, callback) { - let buffer = new Uint8Array(instruction).buffer; - if (buffer !== '') { - wx.writeBLECharacteristicValue({ - deviceId: deviceId, - serviceId: serviceId, - characteristicId: characteristicId, - value: buffer, - success(res) { callback(res); }, - fail(err) { callback(err); } - }); - } else { - callback('Parameter error'); - } + let buffer = new Uint8Array(instruction).buffer + if (buffer !== '') { + wx.writeBLECharacteristicValue({ + deviceId: deviceId, + serviceId: serviceId, + characteristicId: characteristicId, + value: buffer, + success(res) { + callback(res) + }, + fail(err) { + callback(err) + } + }) + } else { + callback('Parameter error') + } } function checkCallback(cb) { - return typeof cb === 'function'; + return typeof cb === 'function' } function openLock(callback) { - if (checkCallback(callback)) { - let data = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]; - let instruction = createInstruct(0x7, data); - return submitInstructions(instruction, callback); - } else { - return 'Parameter error'; - } + if (checkCallback(callback)) { + let data = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff] + let instruction = createInstruct(0x7, data) + return submitInstructions(instruction, callback) + } else { + return 'Parameter error' + } } function sendOpenKey(callback) { - if (checkCallback(callback)) { - let data = [0x32, 0x0, 0xff, 0xff, 0xff, 0xff]; - let instruction = createInstruct(0x10, data); - return submitInstructions(instruction, callback); - } else { - return 'Parameter error'; - } + if (checkCallback(callback)) { + let data = [0x32, 0x0, 0xff, 0xff, 0xff, 0xff] + let instruction = createInstruct(0x10, data) + return submitInstructions(instruction, callback) + } else { + return 'Parameter error' + } } function lockStatus(callback) { - if (checkCallback(callback)) { - let data = []; - let instruction = createInstruct(0x8, data); - return submitInstructions(instruction, callback); - } else { - return 'Parameter error'; - } + if (checkCallback(callback)) { + let data = [] + let instruction = createInstruct(0x8, data) + return submitInstructions(instruction, callback) + } else { + return 'Parameter error' + } } function sendHeart(callback) { - let data = []; - let instruction = createInstruct(0xff, data); - return submitInstructions(instruction, callback); + let data = [] + let instruction = createInstruct(0xff, data) + return submitInstructions(instruction, callback) } function setBluetoothName(name, callback) { - if (checkCallback(callback)) { - let data = strToUint8(name); - let instruction = createInstruct(0x5, data); - return submitInstructions(instruction, callback); - } else { - return 'Parameter error'; - } + if (checkCallback(callback)) { + let data = strToUint8(name) + let instruction = createInstruct(0x5, data) + return submitInstructions(instruction, callback) + } else { + return 'Parameter error' + } } function getLockId(callback) { - if (checkCallback(callback)) { - let data = [0xff, 0xff, 0xff, 0xff]; - let instruction = createInstruct(0x11, data); - return submitInstructions(instruction, callback); - } else { - return 'Parameter error'; - } + if (checkCallback(callback)) { + let data = [0xff, 0xff, 0xff, 0xff] + let instruction = createInstruct(0x11, data) + return submitInstructions(instruction, callback) + } else { + return 'Parameter error' + } } function setLockId(lockId, callback) { - if (checkCallback(callback)) { - let data = [0xff, 0xff, 0xff, 0xff]; - let lockIdBytes = intToByte4(lockId); - lockIdBytes = lockIdBytes.concat(data); - let instruction = createInstruct(0x12, lockIdBytes); - return submitInstructions(instruction, callback); - } else { - return 'Parameter error'; - } + if (checkCallback(callback)) { + let data = [0xff, 0xff, 0xff, 0xff] + let lockIdBytes = intToByte4(lockId) + lockIdBytes = lockIdBytes.concat(data) + let instruction = createInstruct(0x12, lockIdBytes) + return submitInstructions(instruction, callback) + } else { + return 'Parameter error' + } } function startTaskTime(time, callback) { - taskTime = time; - getCurrentTime(callback); + taskTime = time + getCurrentTime(callback) } function getCurrentTime(callback) { - if (checkCallback(callback)) { - let data = []; - let instruction = createInstruct(0xa, data); - return submitInstructions(instruction, callback); - } else { - return 'Parameter error'; - } + if (checkCallback(callback)) { + let data = [] + let instruction = createInstruct(0xa, data) + return submitInstructions(instruction, callback) + } else { + return 'Parameter error' + } } function setTaskTime(timeData, time, callback) { - if (checkCallback(callback)) { - let timeBytes = hex2ab(timeData); - let timeValue = intToByte4R(time); - timeBytes = timeBytes.concat(timeValue); - timeBytes = timeBytes.concat([0xff]); - let instruction = createInstruct(0x22, timeBytes); - return submitInstructions(instruction, callback); - } else { - return 'Parameter error'; - } + if (checkCallback(callback)) { + let timeBytes = hex2ab(timeData) + let timeValue = intToByte4R(time) + timeBytes = timeBytes.concat(timeValue) + timeBytes = timeBytes.concat([0xff]) + let instruction = createInstruct(0x22, timeBytes) + return submitInstructions(instruction, callback) + } else { + return 'Parameter error' + } } function setTaskPrem(param1, param2, callback) { - if (checkCallback(callback)) { - let data = [0xff, 0xff, 0xff, 0xff]; - let param1Bytes = intToByte4(param1); - param1Bytes = param1Bytes.concat(data); - param1Bytes.push(param2); - let instruction = createInstruct(0x23, param1Bytes); - return submitInstructions(instruction, callback); - } else { - return 'Parameter error'; - } + if (checkCallback(callback)) { + let data = [0xff, 0xff, 0xff, 0xff] + let param1Bytes = intToByte4(param1) + param1Bytes = param1Bytes.concat(data) + param1Bytes.push(param2) + let instruction = createInstruct(0x23, param1Bytes) + return submitInstructions(instruction, callback) + } else { + return 'Parameter error' + } } function cleanTaskPrem(callback) { - if (checkCallback(callback)) { - let data = []; - let instruction = createInstruct(0x2a, data); - return submitInstructions(instruction, callback); - } else { - return 'Parameter error'; - } + if (checkCallback(callback)) { + let data = [] + let instruction = createInstruct(0x2a, data) + return submitInstructions(instruction, callback) + } else { + return 'Parameter error' + } } function intToByte4(number) { - let bytes = []; - bytes[3] = number & 0xff; - bytes[2] = (number >> 8) & 0xff; - bytes[1] = (number >> 16) & 0xff; - bytes[0] = (number >> 24) & 0xff; - return bytes; + let bytes = [] + bytes[3] = number & 0xff + bytes[2] = (number >> 8) & 0xff + bytes[1] = (number >> 16) & 0xff + bytes[0] = (number >> 24) & 0xff + return bytes } function intToByte4R(number) { - let bytes = []; - bytes[0] = number & 0xff; - bytes[1] = (number >> 8) & 0xff; - bytes[2] = (number >> 16) & 0xff; - bytes[3] = (number >> 24) & 0xff; - return bytes; + let bytes = [] + bytes[0] = number & 0xff + bytes[1] = (number >> 8) & 0xff + bytes[2] = (number >> 16) & 0xff + bytes[3] = (number >> 24) & 0xff + return bytes } function byteToInt(bytes) { - let b0 = bytes[3] & 0xff; - let b1 = bytes[2] & 0xff; - let b2 = bytes[1] & 0xff; - let b3 = bytes[0] & 0xff; - return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; + let b0 = bytes[3] & 0xff + let b1 = bytes[2] & 0xff + let b2 = bytes[1] & 0xff + let b3 = bytes[0] & 0xff + return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0 } function hexToAb(hexArray) { - let result = []; - for (let i = 0; i < hexArray.length; i++) { - result.push(parseInt(hexArray[i], 16)); - } - return result; + let result = [] + for (let i = 0; i < hexArray.length; i++) { + result.push(parseInt(hexArray[i], 16)) + } + return result } module.exports = { - init, - openLock, - lockStatus, - sendHeart, - setBluetoothName, - sendOpenKey, - getLockId, - setLockId, - startTaskTime, - setTaskPrem, - cleanTaskPrem -}; + init, + openLock, + lockStatus, + sendHeart, + setBluetoothName, + sendOpenKey, + getLockId, + setLockId, + startTaskTime, + setTaskPrem, + cleanTaskPrem +} diff --git a/web/src/utils/lock.ts b/web/src/utils/lock.ts index 1153f1d..2f2cb1c 100644 --- a/web/src/utils/lock.ts +++ b/web/src/utils/lock.ts @@ -12,289 +12,298 @@ import { ElMessage, ElMessageBox } from 'element-plus' import download from '@/utils/download' import { updateFile } from '@/api/infra/file' - type Location = { - latitude: number - longitude: number - accuracy: number + latitude: number + longitude: number + accuracy: number } export const getCurrentLocation = async (): Promise => { - if (appStore.isWorkWechat) { - const res = await ww.getLocation() - return { - latitude: res.latitude, - longitude: res.longitude, - accuracy: res.accuracy - } - } else if (window.navigator.geolocation) { - return new Promise((resolve, reject) => { - window.navigator.geolocation.getCurrentPosition((position) => { - resolve({ - latitude: position.coords.latitude, - longitude: position.coords.longitude, - accuracy: position.coords.accuracy - }) - }, (error) => { - ElMessage.error('获取位置失败') - resolve({ - latitude: 0, - longitude: 0, - accuracy: 0 - }) - }) - }) - } else { - return Promise.resolve({ + if (appStore.isWorkWechat) { + const res = await ww.getLocation() + return { + latitude: res.latitude, + longitude: res.longitude, + accuracy: res.accuracy + } + } else if (window.navigator.geolocation) { + return new Promise((resolve, reject) => { + window.navigator.geolocation.getCurrentPosition( + (position) => { + resolve({ + latitude: position.coords.latitude, + longitude: position.coords.longitude, + accuracy: position.coords.accuracy + }) + }, + (error) => { + ElMessage.error('获取位置失败') + resolve({ latitude: 0, longitude: 0, accuracy: 0 - }) - } + }) + } + ) + }) + } else { + return Promise.resolve({ + latitude: 0, + longitude: 0, + accuracy: 0 + }) + } } // 统一执行绑定逻辑:校验锁状态 -> 获取位置 -> 记录工单 -> 批量更新状态 async function performBind(detail: any, lockId: number, operatorId: number): Promise { - // 校验锁具状态 - const lock = await LockApi.getLock(lockId) - if (!lock) { - ElMessage.error('锁具不存在') - return - } - if (lock.lockStatus != 2) { - ElMessage.error('锁具状态异常 无法绑定') - return - } + // 校验锁具状态 + const lock = await LockApi.getLock(lockId) + if (!lock) { + ElMessage.error('锁具不存在') + return + } + if (lock.lockStatus != 2) { + ElMessage.error('锁具状态异常 无法绑定') + return + } - // 获取当前位置 - let location: Location - try { - location = await getCurrentLocation() - } catch (_) { - ElMessage.error('获取位置失败,无法绑定') - return - } + // 获取当前位置 + let location: Location + try { + location = await getCurrentLocation() + } catch (_) { + ElMessage.error('获取位置失败,无法绑定') + return + } - - - // // 批量更新业务状态 - // await Promise.all([ - // PointApi.updatePoint({ - // id: detail.isolationPointId, - // status: 1 // 已锁定 - // }), - // LockApi.updateLock({ - // id: lockId, - // lockStatus: 7 // 已绑定 - // }), - // PlanItemDetailApi.updatePlanItemDetail({ - // id: detail.id, - // lockId: lockId, - // lockStatus: 1 // 未上锁 - // }) - // ]) - await bindLockApi({ planItemDetailId: detail.id, lockId }) - // 创建工单记录 - await LockWorkRecordApi.createLockWorkRecord({ - operatorId: Number(operatorId), - lockId: lockId, - isolationPlanItemDetailId: detail.id, - recordType: 2, // 未绑定 - gpsCoordinates: `${location.latitude},${location.longitude}` - }) - ElMessage.success('绑定成功') + // // 批量更新业务状态 + // await Promise.all([ + // PointApi.updatePoint({ + // id: detail.isolationPointId, + // status: 1 // 已锁定 + // }), + // LockApi.updateLock({ + // id: lockId, + // lockStatus: 7 // 已绑定 + // }), + // PlanItemDetailApi.updatePlanItemDetail({ + // id: detail.id, + // lockId: lockId, + // lockStatus: 1 // 未上锁 + // }) + // ]) + await bindLockApi({ planItemDetailId: detail.id, lockId }) + // 创建工单记录 + await LockWorkRecordApi.createLockWorkRecord({ + operatorId: Number(operatorId), + lockId: lockId, + isolationPlanItemDetailId: detail.id, + recordType: 2, // 未绑定 + gpsCoordinates: `${location.latitude},${location.longitude}` + }) + ElMessage.success('绑定成功') } - export const bindLock = async (detail: any) => { - try { - if (appStore.isWorkWechat) { - const currentUserId = useUserStore().getUser.id - // 扫描二维码获取锁具ID - const scanRes = await (ww as any).scanQRCode({ - needResult: true, - scanType: ['qrCode'] - }) - const lockId = Number((scanRes && scanRes.resultStr) || NaN) - if (!lockId || Number.isNaN(lockId)) { - ElMessage.error('二维码内容无效') - return - } - await performBind(detail, lockId, currentUserId) - } else { - const lockOptions = elLockStore.locks.filter(i => i.lockEnableStatus == 1 && i.lockStatus == 2).map((lock) => ({ - label: lock.lockName, - number: lock.lockNumber, - value: lock.id - })) + try { + if (appStore.isWorkWechat) { + const currentUserId = useUserStore().getUser.id + // 扫描二维码获取锁具ID + const scanRes = await (ww as any).scanQRCode({ + needResult: true, + scanType: ['qrCode'] + }) + const lockId = Number((scanRes && scanRes.resultStr) || NaN) + if (!lockId || Number.isNaN(lockId)) { + ElMessage.error('二维码内容无效') + return + } + await performBind(detail, lockId, currentUserId) + } else { + const lockOptions = elLockStore.locks + .filter((i) => i.lockEnableStatus == 1 && i.lockStatus == 2) + .map((lock) => ({ + label: lock.lockName, + number: lock.lockNumber, + value: lock.id + })) - // 检查是否有可用的锁具 - if (lockOptions.length === 0) { - ElMessage.error('暂无可用的锁具') - return - } + // 检查是否有可用的锁具 + if (lockOptions.length === 0) { + ElMessage.error('暂无可用的锁具') + return + } - // 如果只有一个锁具,直接使用 - if (lockOptions.length === 1) { - const lockId = lockOptions[0]?.value - if (lockId) { - await performBind(detail, lockId, useUserStore().getUser.id) - } - return - } + // 如果只有一个锁具,直接使用 + if (lockOptions.length === 1) { + const lockId = lockOptions[0]?.value + if (lockId) { + await performBind(detail, lockId, useUserStore().getUser.id) + } + return + } - // 有多个锁具时,让用户选择 - const lockOptionsHtml = lockOptions.map((option, index) => - `${option.number}. ${option.label}` - ).join('') + // 有多个锁具时,让用户选择 + const lockOptionsHtml = lockOptions + .map( + (option, index) => + `${option.number}. ${option.label}` + ) + .join('') - let selectedLockId: string | null = null + let selectedLockId: string | null = null - try { - await ElMessageBox.confirm( - ` + try { + await ElMessageBox.confirm( + ` 请选择要绑定的锁具: -- 请选择锁具 -- ${lockOptionsHtml} `, - '选择锁具', - { - confirmButtonText: '确定', - cancelButtonText: '取消', - dangerouslyUseHTMLString: true, - beforeClose: (action, _instance, done) => { - if (action === 'confirm') { - const selector = document.getElementById('lockSelector') as HTMLSelectElement - const selectedValue = selector?.value - - if (!selectedValue) { - ElMessage.warning('请选择一个锁具') - return false - } - - selectedLockId = selectedValue - } - done() - } - } - ) + '选择锁具', + { + confirmButtonText: '确定', + cancelButtonText: '取消', + dangerouslyUseHTMLString: true, + beforeClose: (action, _instance, done) => { + if (action === 'confirm') { + const selector = document.getElementById('lockSelector') as HTMLSelectElement + const selectedValue = selector?.value - // 用户点击确定后执行绑定 - if (selectedLockId) { - await performBind(detail, Number(selectedLockId), useUserStore().getUser.id) + if (!selectedValue) { + ElMessage.warning('请选择一个锁具') + return false } - } catch (error) { - // 用户取消选择,不做任何操作 - console.log('用户取消选择锁具') + + selectedLockId = selectedValue + } + done() } + } + ) + + // 用户点击确定后执行绑定 + if (selectedLockId) { + await performBind(detail, Number(selectedLockId), useUserStore().getUser.id) } - } catch (error) { - ElMessage.error('绑定失败,请重试') + } catch (error) { + // 用户取消选择,不做任何操作 + console.log('用户取消选择锁具') throw error + } } + } catch (error) { + // ElMessage.error('绑定失败,请重试') + throw error + } } // 将 localId 转换为 File(先尝试 getLocalImgData,失败则用 Canvas 回退) async function convertLocalIdToFile(localId: string): Promise { - // 优先:getLocalImgData(iOS 常见) - if ((ww as any).getLocalImgData) { - try { - const localDataRes = await (ww as any).getLocalImgData({ localId }) - const base64 = String((localDataRes && localDataRes.localData) || '') - const dataUrl = base64.startsWith('data:') ? base64 : `data:image/jpeg;base64,${base64}` - return download.base64ToFile(dataUrl, 'photo') - } catch (_) { /* fallback to canvas */ } + // 优先:getLocalImgData(iOS 常见) + if ((ww as any).getLocalImgData) { + try { + const localDataRes = await (ww as any).getLocalImgData({ localId }) + const base64 = String((localDataRes && localDataRes.localData) || '') + const dataUrl = base64.startsWith('data:') ? base64 : `data:image/jpeg;base64,${base64}` + return download.base64ToFile(dataUrl, 'photo') + } catch (_) { + /* fallback to canvas */ } + } - // 回退:使用 Canvas 生成 base64 再转 File(Android 常见) - return await new Promise((resolve, reject) => { - const img = new Image() - img.crossOrigin = 'anonymous' - img.onload = () => { - try { - const canvas = document.createElement('canvas') - canvas.width = img.width - canvas.height = img.height - const ctx = canvas.getContext('2d') - if (!ctx) throw new Error('canvas context 获取失败') - ctx.drawImage(img, 0, 0, img.width, img.height) - const dataUrl = canvas.toDataURL('image/jpeg') - resolve(download.base64ToFile(dataUrl, 'photo')) - } catch (err) { - reject(err) - } - } - img.onerror = (err) => reject(err) - img.src = localId - }) + // 回退:使用 Canvas 生成 base64 再转 File(Android 常见) + return await new Promise((resolve, reject) => { + const img = new Image() + img.crossOrigin = 'anonymous' + img.onload = () => { + try { + const canvas = document.createElement('canvas') + canvas.width = img.width + canvas.height = img.height + const ctx = canvas.getContext('2d') + if (!ctx) throw new Error('canvas context 获取失败') + ctx.drawImage(img, 0, 0, img.width, img.height) + const dataUrl = canvas.toDataURL('image/jpeg') + resolve(download.base64ToFile(dataUrl, 'photo')) + } catch (err) { + reject(err) + } + } + img.onerror = (err) => reject(err) + img.src = localId + }) } // 创建文件选择器(非企业微信环境使用) async function selectFile(): Promise { - return new Promise((resolve, reject) => { - const input = document.createElement('input') - input.type = 'file' - input.accept = 'image/*' - input.style.display = 'none' + return new Promise((resolve, reject) => { + const input = document.createElement('input') + input.type = 'file' + input.accept = 'image/*' + input.style.display = 'none' - input.onchange = (event) => { - const target = event.target as HTMLInputElement - const file = target.files?.[0] - document.body.removeChild(input) + input.onchange = (event) => { + const target = event.target as HTMLInputElement + const file = target.files?.[0] + document.body.removeChild(input) - if (file) { - resolve(file) - } else { - reject(new Error('未选择文件')) - } - } + if (file) { + resolve(file) + } else { + reject(new Error('未选择文件')) + } + } - input.oncancel = () => { - document.body.removeChild(input) - reject(new Error('用户取消选择')) - } + input.oncancel = () => { + document.body.removeChild(input) + reject(new Error('用户取消选择')) + } - document.body.appendChild(input) - input.click() - }) + document.body.appendChild(input) + input.click() + }) } export const getPhoto = async () => { - try { - if (appStore.isWorkWechat) { - // 放宽企业微信JSSDK类型限制,兼容不同终端返回与参数 - const chooseRes = await (ww as any).chooseImage({ - count: 1, - sizeType: ['original'], - sourceType: ['camera'], - defaultCameraMode: 'normal', - isSaveToAlbum: false - } as any) as { - localIds?: string[] - localId?: string - tempFiles?: Array<{ file?: File }> - } + try { + if (appStore.isWorkWechat) { + // 放宽企业微信JSSDK类型限制,兼容不同终端返回与参数 + const chooseRes = (await (ww as any).chooseImage({ + count: 1, + sizeType: ['original'], + sourceType: ['camera'], + defaultCameraMode: 'normal', + isSaveToAlbum: false + } as any)) as { + localIds?: string[] + localId?: string + tempFiles?: Array<{ file?: File }> + } - // 企业微信返回可能是 tempFiles 或 localIds/localId - const directFile: File | undefined = (chooseRes as any)?.tempFiles?.[0]?.file - const localId: string | undefined = chooseRes?.localIds?.[0] || (chooseRes as any)?.localId + // 企业微信返回可能是 tempFiles 或 localIds/localId + const directFile: File | undefined = (chooseRes as any)?.tempFiles?.[0]?.file + const localId: string | undefined = chooseRes?.localIds?.[0] || (chooseRes as any)?.localId - const file: File | null = directFile - ? directFile - : (localId ? await convertLocalIdToFile(localId) : null) + const file: File | null = directFile + ? directFile + : localId + ? await convertLocalIdToFile(localId) + : null - if (!file) throw new Error('无法获取拍照文件') + if (!file) throw new Error('无法获取拍照文件') - const uploadRes = await updateFile({ file }) - return uploadRes && uploadRes.data - } else { - // 非企业微信环境:选择文件并上传 - const file = await selectFile() - const uploadRes = await updateFile({ file }) - return uploadRes && uploadRes.data - } - } catch (err) { - ElMessage.error('文件上传失败') - throw err - } + const uploadRes = await updateFile({ file }) + return uploadRes && uploadRes.data + } else { + // 非企业微信环境:选择文件并上传 + const file = await selectFile() + const uploadRes = await updateFile({ file }) + return uploadRes && uploadRes.data + } + } catch (err) { + ElMessage.error('文件上传失败') + throw err + } } diff --git a/web/src/utils/permission.ts b/web/src/utils/permission.ts index 4138173..74144d3 100644 --- a/web/src/utils/permission.ts +++ b/web/src/utils/permission.ts @@ -1,6 +1,5 @@ import { CACHE_KEY, useCache } from '@/hooks/web/useCache' -import {hasPermission} from "@/directives/permission/hasPermi"; - +import { hasPermission } from '@/directives/permission/hasPermi' const { t } = useI18n() // 国际化 diff --git a/web/src/utils/routerHelper.ts b/web/src/utils/routerHelper.ts index 7a87bd7..5becfac 100644 --- a/web/src/utils/routerHelper.ts +++ b/web/src/utils/routerHelper.ts @@ -101,7 +101,7 @@ export const generateRoute = (routes: AppCustomRouteRecordRaw[]): AppRouteRecord if (!route.children && route.parentId == 0 && route.component) { data.component = Layout data.meta = { - hidden: meta.hidden, + hidden: meta.hidden } data.name = toCamelCase(route.path, true) + 'Parent' data.redirect = '' diff --git a/web/src/utils/ww.ts b/web/src/utils/ww.ts index df9e88d..6a2080a 100644 --- a/web/src/utils/ww.ts +++ b/web/src/utils/ww.ts @@ -3,1853 +3,1996 @@ import { useAppStore } from '@/store/modules/app' const appStore = useAppStore() import { getAgentConfigSignature, getConfigSignature } from '@/api/lock' ww.register({ - corpId: 'ww6e1eee0a8ae45397', - agentId: 1000002, - jsApiList: [ - // 蓝牙基础API - 'openBluetoothAdapter', - 'getBluetoothAdapterState', - 'closeBluetoothAdapter', - - // 设备发现API - 'startBluetoothDevicesDiscovery', - 'stopBluetoothDevicesDiscovery', - 'getBluetoothDevices', - 'getConnectedBluetoothDevices', - - // BLE连接管理API - 'createBLEConnection', - 'closeBLEConnection', - 'onBLEConnectionStateChange', - - // BLE服务和特征值API - 'getBLEDeviceServices', - 'getBLEDeviceCharacteristics', - 'readBLECharacteristicValue', - 'writeBLECharacteristicValue', - 'notifyBLECharacteristicValueChange', - 'onBLECharacteristicValueChange', - - // 其他功能API - 'getLocation', // 地理位置 - 'scanQRCode', // 扫码 - 'chooseImage',//拍照 - 'getLocalImgData',//获取本地图片 - 'getContext' - ], - getConfigSignature: getConfigSignature, - getAgentConfigSignature: getAgentConfigSignature, + corpId: 'ww6e1eee0a8ae45397', + agentId: 1000002, + jsApiList: [ + // 蓝牙基础API + 'openBluetoothAdapter', + 'getBluetoothAdapterState', + 'closeBluetoothAdapter', + + // 设备发现API + 'startBluetoothDevicesDiscovery', + 'stopBluetoothDevicesDiscovery', + 'getBluetoothDevices', + 'getConnectedBluetoothDevices', + + // BLE连接管理API + 'createBLEConnection', + 'closeBLEConnection', + 'onBLEConnectionStateChange', + + // BLE服务和特征值API + 'getBLEDeviceServices', + 'getBLEDeviceCharacteristics', + 'readBLECharacteristicValue', + 'writeBLECharacteristicValue', + 'notifyBLECharacteristicValueChange', + 'onBLECharacteristicValueChange', + + // 其他功能API + 'getLocation', // 地理位置 + 'scanQRCode', // 扫码 + 'chooseImage', //拍照 + 'getLocalImgData', //获取本地图片 + 'getContext' + ], + getConfigSignature: getConfigSignature, + getAgentConfigSignature: getAgentConfigSignature }) ww.getContext().then(() => { - appStore.isWorkWechat = true + appStore.isWorkWechat = true }) type BluetoothDevice = { - deviceId: string - name: string - RSSI: number - advertisData: ArrayBuffer - advertisServiceUUIDs: string[] - localName: string - serviceData: any + deviceId: string + name: string + RSSI: number + advertisData: ArrayBuffer + advertisServiceUUIDs: string[] + localName: string + serviceData: any } type BluetoothService = { - uuid: string - isPrimary: boolean + uuid: string + isPrimary: boolean } type BluetoothCharacteristic = { - uuid: string - properties: { - read: boolean - write: boolean - notify: boolean - indicate: boolean - writeNoResponse?: boolean - } + uuid: string + properties: { + read: boolean + write: boolean + notify: boolean + indicate: boolean + writeNoResponse?: boolean + } } type Bluetooth = { - isSupport: boolean - isOpen: boolean - isDiscovering: boolean - isConnected: boolean - currentDevice: BluetoothDevice | null - connectedDevices: BluetoothDevice[] - discoveredDevices: BluetoothDevice[] - - // 基础功能 - checkSupport: () => Promise - getState: () => Promise - discover: (services?: string[], timeout?: number) => Promise - stopDiscovery: () => Promise - - // 设备管理 - getDiscoveredDevices: () => Promise - getConnectedDevices: () => Promise - connect: (deviceId: string) => Promise - disconnect: (deviceId: string) => Promise - - // 服务和特征值 - getServices: (deviceId: string) => Promise - getCharacteristics: (deviceId: string, serviceId: string) => Promise - - // 数据读写 - readCharacteristic: (deviceId: string, serviceId: string, characteristicId: string) => Promise - writeCharacteristic: (deviceId: string, serviceId: string, characteristicId: string, value: ArrayBuffer) => Promise - notifyCharacteristic: (deviceId: string, serviceId: string, characteristicId: string, state: boolean) => Promise - - // 事件监听 - onCharacteristicValueChange: (callback: (res: any) => void) => void - onConnectionStateChange: (callback: (res: any) => void) => void - - // 实用方法 - init: () => Promise - reset: () => Promise - clearDevices: () => void - - // 权限管理 - requestPermissions: () => Promise - checkPermissions: () => Promise - openAppSetting: () => Promise + isSupport: boolean + isOpen: boolean + isDiscovering: boolean + isConnected: boolean + currentDevice: BluetoothDevice | null + connectedDevices: BluetoothDevice[] + discoveredDevices: BluetoothDevice[] + + // 基础功能 + checkSupport: () => Promise + getState: () => Promise + discover: (services?: string[], timeout?: number) => Promise + stopDiscovery: () => Promise + + // 设备管理 + getDiscoveredDevices: () => Promise + getConnectedDevices: () => Promise + connect: (deviceId: string) => Promise + disconnect: (deviceId: string) => Promise + + // 服务和特征值 + getServices: (deviceId: string) => Promise + getCharacteristics: (deviceId: string, serviceId: string) => Promise + + // 数据读写 + readCharacteristic: ( + deviceId: string, + serviceId: string, + characteristicId: string + ) => Promise + writeCharacteristic: ( + deviceId: string, + serviceId: string, + characteristicId: string, + value: ArrayBuffer + ) => Promise + notifyCharacteristic: ( + deviceId: string, + serviceId: string, + characteristicId: string, + state: boolean + ) => Promise + + // 事件监听 + onCharacteristicValueChange: (callback: (res: any) => void) => void + onConnectionStateChange: (callback: (res: any) => void) => void + + // 实用方法 + init: () => Promise + reset: () => Promise + clearDevices: () => void + + // 权限管理 + requestPermissions: () => Promise + checkPermissions: () => Promise + openAppSetting: () => Promise } // 权限结果类型 type PermissionResult = { - camera: PermissionStatus - location: PermissionStatus - bluetooth: PermissionStatus - microphone?: PermissionStatus - storage?: PermissionStatus - contacts?: PermissionStatus + camera: PermissionStatus + location: PermissionStatus + bluetooth: PermissionStatus + microphone?: PermissionStatus + storage?: PermissionStatus + contacts?: PermissionStatus } type PermissionStatus = 'granted' | 'denied' | 'restricted' | 'notDetermined' | 'unavailable' class wwBluetooth implements Bluetooth { - isSupport: boolean - isOpen: boolean - isDiscovering: boolean - isConnected: boolean - currentDevice: BluetoothDevice | null - connectedDevices: BluetoothDevice[] - discoveredDevices: BluetoothDevice[] - - private discoveryTimeout?: NodeJS.Timeout - private characteristicCallbacks: Map void> - private connectionCallbacks: ((res: any) => void)[] - private heartbeatInterval: NodeJS.Timeout | null = null - public currentHeartbeatDeviceId: string | null = null - - constructor() { - this.isSupport = false - this.isConnected = false - this.isOpen = false - this.isDiscovering = false - this.currentDevice = null - this.connectedDevices = [] - this.discoveredDevices = [] - this.characteristicCallbacks = new Map() - this.connectionCallbacks = [] - } - async checkSupport() { - return new Promise((resolve, reject) => { - ww.openBluetoothAdapter( - { - success: (res) => { - this.isSupport = true - resolve(res) - }, - fail: (err) => { - this.isSupport = false - reject(err) - } - } - ) - }) - } - async getState() { - return new Promise((resolve, reject) => { - ww.getBluetoothAdapterState( - { - success: (res) => { - resolve(res) - }, - fail: (err) => { - reject(err) - } - } - ) - }) - } - async discover(services?: string[], timeout: number = 10000) { - return new Promise((resolve, reject) => { - this.isDiscovering = true - this.discoveredDevices = [] - - const options: any = { - success: (res: any) => { - if (timeout > 0) { - this.discoveryTimeout = setTimeout(() => { - this.stopDiscovery() - }, timeout) - } - resolve(res) - }, - fail: (err: any) => { - this.isDiscovering = false - reject(err) - } - } - - if (services && services.length > 0) { - options.services = services - } - - ww.startBluetoothDevicesDiscovery(options) - }) - } - async stopDiscovery() { - return new Promise((resolve, reject) => { - this.isDiscovering = false - if (this.discoveryTimeout) { - clearTimeout(this.discoveryTimeout) - this.discoveryTimeout = undefined + isSupport: boolean + isOpen: boolean + isDiscovering: boolean + isConnected: boolean + currentDevice: BluetoothDevice | null + connectedDevices: BluetoothDevice[] + discoveredDevices: BluetoothDevice[] + + private discoveryTimeout?: NodeJS.Timeout + private characteristicCallbacks: Map void> + private connectionCallbacks: ((res: any) => void)[] + private heartbeatInterval: NodeJS.Timeout | null = null + public currentHeartbeatDeviceId: string | null = null + + constructor() { + this.isSupport = false + this.isConnected = false + this.isOpen = false + this.isDiscovering = false + this.currentDevice = null + this.connectedDevices = [] + this.discoveredDevices = [] + this.characteristicCallbacks = new Map() + this.connectionCallbacks = [] + } + async checkSupport() { + return new Promise((resolve, reject) => { + ww.openBluetoothAdapter({ + success: (res) => { + this.isSupport = true + resolve(res) + }, + fail: (err) => { + this.isSupport = false + reject(err) + } + }) + }) + } + async getState() { + return new Promise((resolve, reject) => { + ww.getBluetoothAdapterState({ + success: (res) => { + resolve(res) + }, + fail: (err) => { + reject(err) + } + }) + }) + } + async discover(services?: string[], timeout: number = 10000) { + return new Promise((resolve, reject) => { + this.isDiscovering = true + this.discoveredDevices = [] + + const options: any = { + success: (res: any) => { + if (timeout > 0) { + this.discoveryTimeout = setTimeout(() => { + this.stopDiscovery() + }, timeout) + } + resolve(res) + }, + fail: (err: any) => { + this.isDiscovering = false + reject(err) + } + } + + if (services && services.length > 0) { + options.services = services + } + + ww.startBluetoothDevicesDiscovery(options) + }) + } + async stopDiscovery() { + return new Promise((resolve, reject) => { + this.isDiscovering = false + if (this.discoveryTimeout) { + clearTimeout(this.discoveryTimeout) + this.discoveryTimeout = undefined + } + + ww.stopBluetoothDevicesDiscovery({ + success: (res) => { + resolve(res) + }, + fail: (err) => { + reject(err) + } + }) + }) + } + async getDiscoveredDevices(): Promise { + return new Promise((resolve, reject) => { + ww.getBluetoothDevices({ + success: (res: any) => { + this.discoveredDevices = res.devices || [] + resolve(this.discoveredDevices) + }, + fail: (err) => { + reject(err) + } + }) + }) + } + async getConnectedDevices(): Promise { + return new Promise((resolve, reject) => { + ww.getConnectedBluetoothDevices({ + success: (res: any) => { + this.connectedDevices = res.devices || [] + resolve(this.connectedDevices) + }, + fail: (err) => { + reject(err) + }, + services: [] + }) + }) + } + async connect(deviceId: string) { + return new Promise((resolve, reject) => { + ww.createBLEConnection({ + deviceId: deviceId, + success: (res) => { + this.isConnected = true + const device = this.discoveredDevices.find((d) => d.deviceId === deviceId) + if (device && !this.connectedDevices.find((d) => d.deviceId === deviceId)) { + this.connectedDevices.push(device) + this.currentDevice = device + } + this.connectionCallbacks.forEach((callback) => callback({ deviceId, connected: true })) + resolve(res) + }, + fail: (err) => { + reject(err) + } + }) + }) + } + + async disconnect(deviceId: string) { + return new Promise((resolve, reject) => { + ww.closeBLEConnection({ + deviceId: deviceId, + success: (res) => { + this.connectedDevices = this.connectedDevices.filter((d) => d.deviceId !== deviceId) + if (this.currentDevice?.deviceId === deviceId) { + this.currentDevice = null + // 清理当前设备的监听器和心跳 + this.clearEventListeners() + this.stopHeartbeat() + } + this.isConnected = this.connectedDevices.length > 0 + this.connectionCallbacks.forEach((callback) => callback({ deviceId, connected: false })) + console.log(`[蓝牙调试] 设备 ${deviceId} 已断开连接`) + resolve(res) + }, + fail: (err) => { + reject(err) + } + }) + }) + } + + // 服务和特征值操作 + async getServices(deviceId: string): Promise { + return new Promise((resolve, reject) => { + ww.getBLEDeviceServices({ + deviceId: deviceId, + success: (res: any) => { + resolve(res.services || []) + }, + fail: (err) => { + reject(err) + } + }) + }) + } + + async getCharacteristics( + deviceId: string, + serviceId: string + ): Promise { + return new Promise((resolve, reject) => { + ww.getBLEDeviceCharacteristics({ + deviceId: deviceId, + serviceId: serviceId, + success: (res: any) => { + resolve(res.characteristics || []) + }, + fail: (err) => { + reject(err) + } + }) + }) + } + + // 数据读写操作 + async readCharacteristic(deviceId: string, serviceId: string, characteristicId: string) { + return new Promise((resolve, reject) => { + ww.readBLECharacteristicValue({ + deviceId: deviceId, + serviceId: serviceId, + characteristicId: characteristicId, + success: (res: any) => { + resolve(res) + }, + fail: (err) => { + reject(err) + } + }) + }) + } + + async writeCharacteristic( + deviceId: string, + serviceId: string, + characteristicId: string, + value: ArrayBuffer + ) { + return new Promise((resolve, reject) => { + // 检查API是否存在 + if (!ww.writeBLECharacteristicValue) { + reject(new Error('writeBLECharacteristicValue API不可用')) + return + } + + // 企业微信可能对数据长度有限制,通常是20字节 + if (value.byteLength > 20) { + console.warn(`[蓝牙调试] 数据长度 ${value.byteLength} 超过建议的20字节限制`) + } + + console.log(`[蓝牙调试] 调用writeBLECharacteristicValue:`, { + deviceId, + serviceId, + characteristicId, + valueByteLength: value.byteLength, + valueHex: Array.from(new Uint8Array(value)) + .map((b) => b.toString(16).padStart(2, '0')) + .join(' ') + }) + + // 企业微信蓝牙API的参数配置 + const params: any = { + deviceId: deviceId, + serviceId: serviceId, + characteristicId: characteristicId, + value: value, + success: (res: any) => { + console.log(`[蓝牙调试] writeBLECharacteristicValue success:`, res) + resolve(res) + }, + fail: (err: any) => { + console.error(`[蓝牙调试] writeBLECharacteristicValue fail:`, err) + + // 企业微信特有的错误处理 + if (err.errMsg) { + if (err.errMsg.includes('fail_system err')) { + // 系统错误,可能是特征值不支持或权限问题 + const newError = new Error( + '蓝牙写入失败:设备特征值不支持写入或权限不足。请检查设备是否支持该特征值的写入操作。' + ) + ;(newError as any).originalError = err + reject(newError) + } else if (err.errMsg.includes('fail_device not connected')) { + reject(new Error('设备已断开连接,请重新连接设备')) + } else if (err.errMsg.includes('fail_invalid characteristic')) { + reject(new Error('无效的特征值ID,请检查特征值是否存在')) + } else if (err.errMsg.includes('fail_write not permit')) { + reject(new Error('特征值不允许写入操作')) + } else { + reject(err) } + } else { + reject(err) + } + } + } + + // 企业微信可能需要指定写入类型 + // 如果支持writeNoResponse,优先使用(更可靠) + try { + // 先尝试标准写入 + ww.writeBLECharacteristicValue(params) + } catch (error) { + console.error(`[蓝牙调试] 标准写入失败,尝试其他方式:`, error) + reject(error) + } + }) + } + + async notifyCharacteristic( + deviceId: string, + serviceId: string, + characteristicId: string, + state: boolean + ) { + return new Promise((resolve, reject) => { + ww.notifyBLECharacteristicValueChange({ + deviceId: deviceId, + serviceId: serviceId, + characteristicId: characteristicId, + state: state, + success: (res: any) => { + resolve(res) + }, + fail: (err) => { + reject(err) + } + }) + }) + } + + // 事件监听 + onCharacteristicValueChange(callback: (res: any) => void) { + // 清理现有的监听器,避免重复绑定 + this.characteristicCallbacks.clear() + + const key = 'characteristic_change' + this.characteristicCallbacks.set(key, callback) + + // 企业微信环境中,避免重复绑定 + // 注意:这里可能需要在设备断开时清理 + ww.onBLECharacteristicValueChange(callback) + console.log(`[蓝牙调试] 特征值变化监听器已设置`) + } + + onConnectionStateChange(callback: (res: any) => void) { + // 避免重复添加相同的回调 + if (!this.connectionCallbacks.includes(callback)) { + this.connectionCallbacks.push(callback) + + // 包装回调函数,添加心跳管理逻辑 + const wrappedCallback = (res: any) => { + console.log(`[蓝牙调试] 连接状态变化: 设备${res.deviceId}, 连接状态: ${res.connected}`) + + // 特别关注断开连接的情况 + if (!res.connected) { + console.warn(`[蓝牙调试] 设备断开连接: ${res.deviceId}`) + + // 如果是当前心跳设备,停止心跳 + if (this.currentHeartbeatDeviceId === res.deviceId) { + console.log(`[蓝牙调试] 停止心跳,设备: ${res.deviceId}`) + this.stopHeartbeat() + } + + // 从连接设备列表中移除 + this.connectedDevices = this.connectedDevices.filter((d) => d.deviceId !== res.deviceId) + if (this.currentDevice?.deviceId === res.deviceId) { + this.currentDevice = null + } + this.isConnected = this.connectedDevices.length > 0 + } else { + console.log(`[蓝牙调试] 设备连接成功: ${res.deviceId}`) + } - ww.stopBluetoothDevicesDiscovery({ - success: (res) => { - resolve(res) - }, - fail: (err) => { - reject(err) - } - }) - }) - } - async getDiscoveredDevices(): Promise { - return new Promise((resolve, reject) => { - ww.getBluetoothDevices({ - success: (res: any) => { - this.discoveredDevices = res.devices || [] - resolve(this.discoveredDevices) - }, - fail: (err) => { - reject(err) - } - }) - }) - } - async getConnectedDevices(): Promise { - return new Promise((resolve, reject) => { - ww.getConnectedBluetoothDevices({ - success: (res: any) => { - this.connectedDevices = res.devices || [] - resolve(this.connectedDevices) - }, - fail: (err) => { - reject(err) - }, - services: [] - }) - }) - } - async connect(deviceId: string) { - return new Promise((resolve, reject) => { - ww.createBLEConnection({ - deviceId: deviceId, - success: (res) => { - this.isConnected = true - const device = this.discoveredDevices.find(d => d.deviceId === deviceId) - if (device && !this.connectedDevices.find(d => d.deviceId === deviceId)) { - this.connectedDevices.push(device) - this.currentDevice = device - } - this.connectionCallbacks.forEach(callback => callback({ deviceId, connected: true })) - resolve(res) - }, - fail: (err) => { - reject(err) - } - }) - }) - } + // 调用原始回调 + callback(res) + } - async disconnect(deviceId: string) { - return new Promise((resolve, reject) => { - ww.closeBLEConnection({ - deviceId: deviceId, - success: (res) => { - this.connectedDevices = this.connectedDevices.filter(d => d.deviceId !== deviceId) - if (this.currentDevice?.deviceId === deviceId) { - this.currentDevice = null - // 清理当前设备的监听器和心跳 - this.clearEventListeners() - this.stopHeartbeat() - } - this.isConnected = this.connectedDevices.length > 0 - this.connectionCallbacks.forEach(callback => callback({ deviceId, connected: false })) - console.log(`[蓝牙调试] 设备 ${deviceId} 已断开连接`) - resolve(res) - }, - fail: (err) => { - reject(err) - } - }) - }) + ww.onBLEConnectionStateChange && ww.onBLEConnectionStateChange(wrappedCallback) + console.log(`[蓝牙调试] 连接状态变化监听器已设置`) } - - // 服务和特征值操作 - async getServices(deviceId: string): Promise { - return new Promise((resolve, reject) => { - ww.getBLEDeviceServices({ - deviceId: deviceId, - success: (res: any) => { - resolve(res.services || []) - }, - fail: (err) => { - reject(err) - } - }) - }) + } + + // 清理监听器的方法 + clearEventListeners() { + this.characteristicCallbacks.clear() + this.connectionCallbacks.length = 0 + console.log(`[蓝牙调试] 事件监听器已清理`) + } + + /** + * 请求系统权限 + * @returns Promise + */ + async requestPermissions(): Promise { + const result: PermissionResult = { + camera: 'notDetermined', + location: 'notDetermined', + bluetooth: 'notDetermined' } - async getCharacteristics(deviceId: string, serviceId: string): Promise { - return new Promise((resolve, reject) => { - ww.getBLEDeviceCharacteristics({ - deviceId: deviceId, - serviceId: serviceId, - success: (res: any) => { - resolve(res.characteristics || []) - }, - fail: (err) => { - reject(err) - } + try { + // 请求相机权限 - 企业微信可能不支持authorize,使用其他方式 + try { + // 尝试通过其他方式请求相机权限 + result.camera = 'notDetermined' + console.log('[权限管理] 相机权限在企业微信中可能需要手动开启') + } catch (error) { + console.warn('相机权限检查失败:', error) + result.camera = 'denied' + } + + // 请求地理位置权限 + try { + if (ww.getLocation) { + await new Promise((resolve, reject) => { + ww.getLocation({ + type: 'gcj02' as any, // 类型断言 + success: () => { + result.location = 'granted' + resolve(true) + }, + fail: (err: any) => { + console.warn('地理位置权限请求失败:', err) + result.location = 'denied' + resolve(false) + } }) - }) - } - - // 数据读写操作 - async readCharacteristic(deviceId: string, serviceId: string, characteristicId: string) { - return new Promise((resolve, reject) => { - ww.readBLECharacteristicValue({ - deviceId: deviceId, - serviceId: serviceId, - characteristicId: characteristicId, - success: (res: any) => { - resolve(res) - }, - fail: (err) => { - reject(err) - } + }) + } else { + result.location = 'unavailable' + } + } catch (error) { + console.warn('地理位置权限检查失败:', error) + result.location = 'denied' + } + + // 请求蓝牙权限 + try { + if (ww.openBluetoothAdapter) { + await new Promise((resolve, reject) => { + ww.openBluetoothAdapter({ + success: () => { + result.bluetooth = 'granted' + resolve(true) + }, + fail: (err: any) => { + console.warn('蓝牙权限请求失败:', err) + result.bluetooth = 'denied' + resolve(false) + } }) - }) + }) + } else { + result.bluetooth = 'unavailable' + } + } catch (error) { + console.warn('蓝牙权限检查失败:', error) + result.bluetooth = 'denied' + } + + // 请求麦克风权限(如果支持) + try { + // 企业微信可能不支持authorize,使用其他方式 + result.microphone = 'notDetermined' + console.log('[权限管理] 麦克风权限在企业微信中可能需要手动开启') + } catch (error) { + console.warn('麦克风权限检查失败:', error) + result.microphone = 'denied' + } + + console.log('[权限管理] 权限请求结果:', result) + return result + } catch (error) { + console.error('[权限管理] 权限请求过程中出错:', error) + return result } - - async writeCharacteristic(deviceId: string, serviceId: string, characteristicId: string, value: ArrayBuffer) { - return new Promise((resolve, reject) => { - // 检查API是否存在 - if (!ww.writeBLECharacteristicValue) { - reject(new Error('writeBLECharacteristicValue API不可用')); - return; - } - - // 企业微信可能对数据长度有限制,通常是20字节 - if (value.byteLength > 20) { - console.warn(`[蓝牙调试] 数据长度 ${value.byteLength} 超过建议的20字节限制`); - } - - console.log(`[蓝牙调试] 调用writeBLECharacteristicValue:`, { - deviceId, - serviceId, - characteristicId, - valueByteLength: value.byteLength, - valueHex: Array.from(new Uint8Array(value)).map(b => b.toString(16).padStart(2, '0')).join(' ') - }); - - // 企业微信蓝牙API的参数配置 - const params: any = { - deviceId: deviceId, - serviceId: serviceId, - characteristicId: characteristicId, - value: value, - success: (res: any) => { - console.log(`[蓝牙调试] writeBLECharacteristicValue success:`, res); - resolve(res) - }, - fail: (err: any) => { - console.error(`[蓝牙调试] writeBLECharacteristicValue fail:`, err); - - // 企业微信特有的错误处理 - if (err.errMsg) { - if (err.errMsg.includes('fail_system err')) { - // 系统错误,可能是特征值不支持或权限问题 - const newError = new Error('蓝牙写入失败:设备特征值不支持写入或权限不足。请检查设备是否支持该特征值的写入操作。'); - (newError as any).originalError = err; - reject(newError); - } else if (err.errMsg.includes('fail_device not connected')) { - reject(new Error('设备已断开连接,请重新连接设备')); - } else if (err.errMsg.includes('fail_invalid characteristic')) { - reject(new Error('无效的特征值ID,请检查特征值是否存在')); - } else if (err.errMsg.includes('fail_write not permit')) { - reject(new Error('特征值不允许写入操作')); - } else { - reject(err); - } - } else { - reject(err); - } - } - }; - - // 企业微信可能需要指定写入类型 - // 如果支持writeNoResponse,优先使用(更可靠) - try { - // 先尝试标准写入 - ww.writeBLECharacteristicValue(params); - } catch (error) { - console.error(`[蓝牙调试] 标准写入失败,尝试其他方式:`, error); - reject(error); - } - }) + } + + /** + * 检查当前权限状态 + * @returns Promise + */ + async checkPermissions(): Promise { + const result: PermissionResult = { + camera: 'notDetermined', + location: 'notDetermined', + bluetooth: 'notDetermined' } - async notifyCharacteristic(deviceId: string, serviceId: string, characteristicId: string, state: boolean) { - return new Promise((resolve, reject) => { - ww.notifyBLECharacteristicValueChange({ - deviceId: deviceId, - serviceId: serviceId, - characteristicId: characteristicId, - state: state, - success: (res: any) => { - resolve(res) - }, - fail: (err) => { - reject(err) - } + try { + // 检查相机权限 - 企业微信可能不支持getSetting + try { + // 企业微信中相机权限检查方式不同 + result.camera = 'notDetermined' + console.log('[权限管理] 相机权限状态在企业微信中无法直接检查') + } catch (error) { + console.warn('相机权限检查失败:', error) + result.camera = 'notDetermined' + } + + // 检查地理位置权限 - 企业微信可能不支持getSetting + try { + // 企业微信中地理位置权限检查方式不同 + result.location = 'notDetermined' + console.log('[权限管理] 地理位置权限状态在企业微信中无法直接检查') + } catch (error) { + console.warn('地理位置权限检查失败:', error) + result.location = 'notDetermined' + } + + // 检查蓝牙权限 + try { + if (ww.getBluetoothAdapterState) { + const state = (await new Promise((resolve, reject) => { + ww.getBluetoothAdapterState({ + success: (res: any) => resolve(res), + fail: (err: any) => reject(err) }) - }) - } - - // 事件监听 - onCharacteristicValueChange(callback: (res: any) => void) { - // 清理现有的监听器,避免重复绑定 - this.characteristicCallbacks.clear() - - const key = 'characteristic_change' - this.characteristicCallbacks.set(key, callback) - - // 企业微信环境中,避免重复绑定 - // 注意:这里可能需要在设备断开时清理 - ww.onBLECharacteristicValueChange(callback) - console.log(`[蓝牙调试] 特征值变化监听器已设置`) - } - - onConnectionStateChange(callback: (res: any) => void) { - // 避免重复添加相同的回调 - if (!this.connectionCallbacks.includes(callback)) { - this.connectionCallbacks.push(callback) - - // 包装回调函数,添加心跳管理逻辑 - const wrappedCallback = (res: any) => { - console.log(`[蓝牙调试] 连接状态变化: 设备${res.deviceId}, 连接状态: ${res.connected}`) - - // 特别关注断开连接的情况 - if (!res.connected) { - console.warn(`[蓝牙调试] 设备断开连接: ${res.deviceId}`) - - // 如果是当前心跳设备,停止心跳 - if (this.currentHeartbeatDeviceId === res.deviceId) { - console.log(`[蓝牙调试] 停止心跳,设备: ${res.deviceId}`) - this.stopHeartbeat() - } - - // 从连接设备列表中移除 - this.connectedDevices = this.connectedDevices.filter(d => d.deviceId !== res.deviceId) - if (this.currentDevice?.deviceId === res.deviceId) { - this.currentDevice = null - } - this.isConnected = this.connectedDevices.length > 0 - } else { - console.log(`[蓝牙调试] 设备连接成功: ${res.deviceId}`) - } - - // 调用原始回调 - callback(res) - } - - ww.onBLEConnectionStateChange && ww.onBLEConnectionStateChange(wrappedCallback) - console.log(`[蓝牙调试] 连接状态变化监听器已设置`) + })) as any + + if (state.available === true) { + result.bluetooth = 'granted' + } else if (state.available === false) { + result.bluetooth = 'denied' + } else { + result.bluetooth = 'notDetermined' + } + } else { + result.bluetooth = 'unavailable' } + } catch (error) { + console.warn('蓝牙权限检查失败:', error) + result.bluetooth = 'notDetermined' + } + + // 检查麦克风权限 - 企业微信可能不支持getSetting + try { + // 企业微信中麦克风权限检查方式不同 + result.microphone = 'notDetermined' + console.log('[权限管理] 麦克风权限状态在企业微信中无法直接检查') + } catch (error) { + console.warn('麦克风权限检查失败:', error) + result.microphone = 'notDetermined' + } + + console.log('[权限管理] 当前权限状态:', result) + return result + } catch (error) { + console.error('[权限管理] 权限检查过程中出错:', error) + return result } - - // 清理监听器的方法 - clearEventListeners() { - this.characteristicCallbacks.clear() - this.connectionCallbacks.length = 0 - console.log(`[蓝牙调试] 事件监听器已清理`) - } - - /** - * 请求系统权限 - * @returns Promise - */ - async requestPermissions(): Promise { - const result: PermissionResult = { - camera: 'notDetermined', - location: 'notDetermined', - bluetooth: 'notDetermined' + } + + /** + * 打开应用设置页面 + * @returns Promise + */ + async openAppSetting(): Promise { + return new Promise((resolve, reject) => { + // 企业微信可能不支持openSetting,提供替代方案 + console.log('[权限管理] 企业微信中无法直接打开设置页面,建议用户手动开启权限') + + // 可以在这里添加用户提示逻辑 + // 例如显示一个对话框,说明如何手动开启权限 + + resolve({ message: '请在企业微信设置中手动开启相关权限' }) + }) + } + + /** + * 检查并请求必要权限 + * @param requiredPermissions 需要的权限列表 + * @returns Promise + */ + async ensurePermissions( + requiredPermissions: (keyof PermissionResult)[] = ['bluetooth', 'location'] + ): Promise { + try { + console.log('[权限管理] 开始检查必要权限:', requiredPermissions) + + // 先检查当前权限状态 + const currentPermissions = await this.checkPermissions() + + // 检查哪些权限需要请求 + const needRequest: (keyof PermissionResult)[] = [] + for (const permission of requiredPermissions) { + if ( + currentPermissions[permission] === 'denied' || + currentPermissions[permission] === 'notDetermined' + ) { + needRequest.push(permission) } + } - try { - // 请求相机权限 - 企业微信可能不支持authorize,使用其他方式 - try { - // 尝试通过其他方式请求相机权限 - result.camera = 'notDetermined' - console.log('[权限管理] 相机权限在企业微信中可能需要手动开启') - } catch (error) { - console.warn('相机权限检查失败:', error) - result.camera = 'denied' - } + if (needRequest.length === 0) { + console.log('[权限管理] 所有必要权限已获取') + return true + } - // 请求地理位置权限 - try { - if (ww.getLocation) { - await new Promise((resolve, reject) => { - ww.getLocation({ - type: 'gcj02' as any, // 类型断言 - success: () => { - result.location = 'granted' - resolve(true) - }, - fail: (err: any) => { - console.warn('地理位置权限请求失败:', err) - result.location = 'denied' - resolve(false) - } - }) - }) - } else { - result.location = 'unavailable' - } - } catch (error) { - console.warn('地理位置权限检查失败:', error) - result.location = 'denied' - } - - // 请求蓝牙权限 - try { - if (ww.openBluetoothAdapter) { - await new Promise((resolve, reject) => { - ww.openBluetoothAdapter({ - success: () => { - result.bluetooth = 'granted' - resolve(true) - }, - fail: (err: any) => { - console.warn('蓝牙权限请求失败:', err) - result.bluetooth = 'denied' - resolve(false) - } - }) - }) - } else { - result.bluetooth = 'unavailable' - } - } catch (error) { - console.warn('蓝牙权限检查失败:', error) - result.bluetooth = 'denied' - } + console.log('[权限管理] 需要请求的权限:', needRequest) - // 请求麦克风权限(如果支持) - try { - // 企业微信可能不支持authorize,使用其他方式 - result.microphone = 'notDetermined' - console.log('[权限管理] 麦克风权限在企业微信中可能需要手动开启') - } catch (error) { - console.warn('麦克风权限检查失败:', error) - result.microphone = 'denied' - } + // 请求权限 + const requestResult = await this.requestPermissions() - console.log('[权限管理] 权限请求结果:', result) - return result + // 再次检查权限状态 + const finalPermissions = await this.checkPermissions() - } catch (error) { - console.error('[权限管理] 权限请求过程中出错:', error) - return result - } - } + // 验证所有必要权限是否已获取 + const allGranted = requiredPermissions.every( + (permission) => finalPermissions[permission] === 'granted' + ) - /** - * 检查当前权限状态 - * @returns Promise - */ - async checkPermissions(): Promise { - const result: PermissionResult = { - camera: 'notDetermined', - location: 'notDetermined', - bluetooth: 'notDetermined' - } + if (allGranted) { + console.log('[权限管理] 所有必要权限已成功获取') + return true + } else { + console.warn('[权限管理] 部分权限获取失败:', finalPermissions) - try { - // 检查相机权限 - 企业微信可能不支持getSetting - try { - // 企业微信中相机权限检查方式不同 - result.camera = 'notDetermined' - console.log('[权限管理] 相机权限状态在企业微信中无法直接检查') - } catch (error) { - console.warn('相机权限检查失败:', error) - result.camera = 'notDetermined' - } - - // 检查地理位置权限 - 企业微信可能不支持getSetting - try { - // 企业微信中地理位置权限检查方式不同 - result.location = 'notDetermined' - console.log('[权限管理] 地理位置权限状态在企业微信中无法直接检查') - } catch (error) { - console.warn('地理位置权限检查失败:', error) - result.location = 'notDetermined' - } - - // 检查蓝牙权限 - try { - if (ww.getBluetoothAdapterState) { - const state = await new Promise((resolve, reject) => { - ww.getBluetoothAdapterState({ - success: (res: any) => resolve(res), - fail: (err: any) => reject(err) - }) - }) as any - - if (state.available === true) { - result.bluetooth = 'granted' - } else if (state.available === false) { - result.bluetooth = 'denied' - } else { - result.bluetooth = 'notDetermined' - } - } else { - result.bluetooth = 'unavailable' - } - } catch (error) { - console.warn('蓝牙权限检查失败:', error) - result.bluetooth = 'notDetermined' - } + // 提示用户手动开启权限 + const failedPermissions = requiredPermissions.filter( + (permission) => finalPermissions[permission] !== 'granted' + ) - // 检查麦克风权限 - 企业微信可能不支持getSetting - try { - // 企业微信中麦克风权限检查方式不同 - result.microphone = 'notDetermined' - console.log('[权限管理] 麦克风权限状态在企业微信中无法直接检查') - } catch (error) { - console.warn('麦克风权限检查失败:', error) - result.microphone = 'notDetermined' - } + console.warn('[权限管理] 以下权限获取失败,建议用户手动开启:', failedPermissions) - console.log('[权限管理] 当前权限状态:', result) - return result + // 可以在这里添加用户提示逻辑 + // 例如显示一个对话框,引导用户去设置页面开启权限 - } catch (error) { - console.error('[权限管理] 权限检查过程中出错:', error) - return result - } + return false + } + } catch (error) { + console.error('[权限管理] 权限确保过程中出错:', error) + return false } - - /** - * 打开应用设置页面 - * @returns Promise - */ - async openAppSetting(): Promise { - return new Promise((resolve, reject) => { - // 企业微信可能不支持openSetting,提供替代方案 - console.log('[权限管理] 企业微信中无法直接打开设置页面,建议用户手动开启权限') - - // 可以在这里添加用户提示逻辑 - // 例如显示一个对话框,说明如何手动开启权限 - - resolve({ message: '请在企业微信设置中手动开启相关权限' }) - }) + } + + // 实用方法 + async init(): Promise { + try { + // 先确保必要权限 + const hasPermissions = await this.ensurePermissions(['bluetooth']) + if (!hasPermissions) { + console.warn('[蓝牙初始化] 蓝牙权限不足,可能影响功能使用') + } + + await this.checkSupport() + await this.getState() + this.isOpen = true + } catch (error) { + console.error('蓝牙初始化失败:', error) + throw error } - - /** - * 检查并请求必要权限 - * @param requiredPermissions 需要的权限列表 - * @returns Promise - */ - async ensurePermissions(requiredPermissions: (keyof PermissionResult)[] = ['bluetooth', 'location']): Promise { - try { - console.log('[权限管理] 开始检查必要权限:', requiredPermissions) - - // 先检查当前权限状态 - const currentPermissions = await this.checkPermissions() - - // 检查哪些权限需要请求 - const needRequest: (keyof PermissionResult)[] = [] - for (const permission of requiredPermissions) { - if (currentPermissions[permission] === 'denied' || currentPermissions[permission] === 'notDetermined') { - needRequest.push(permission) - } - } - - if (needRequest.length === 0) { - console.log('[权限管理] 所有必要权限已获取') - return true - } - - console.log('[权限管理] 需要请求的权限:', needRequest) - - // 请求权限 - const requestResult = await this.requestPermissions() - - // 再次检查权限状态 - const finalPermissions = await this.checkPermissions() - - // 验证所有必要权限是否已获取 - const allGranted = requiredPermissions.every(permission => - finalPermissions[permission] === 'granted' - ) - - if (allGranted) { - console.log('[权限管理] 所有必要权限已成功获取') - return true - } else { - console.warn('[权限管理] 部分权限获取失败:', finalPermissions) - - // 提示用户手动开启权限 - const failedPermissions = requiredPermissions.filter(permission => - finalPermissions[permission] !== 'granted' - ) - - console.warn('[权限管理] 以下权限获取失败,建议用户手动开启:', failedPermissions) - - // 可以在这里添加用户提示逻辑 - // 例如显示一个对话框,引导用户去设置页面开启权限 - - return false - } - - } catch (error) { - console.error('[权限管理] 权限确保过程中出错:', error) - return false - } + } + + async reset(): Promise { + try { + if (this.isDiscovering) { + await this.stopDiscovery() + } + + // 断开所有连接 + const disconnectPromises = this.connectedDevices.map((device) => + this.disconnect(device.deviceId).catch((err) => console.warn('断开连接失败:', err)) + ) + await Promise.all(disconnectPromises) + + this.clearDevices() + + // 重新初始化 + await this.init() + } catch (error) { + console.error('蓝牙重置失败:', error) + throw error } - - // 实用方法 - async init(): Promise { - try { - // 先确保必要权限 - const hasPermissions = await this.ensurePermissions(['bluetooth']) - if (!hasPermissions) { - console.warn('[蓝牙初始化] 蓝牙权限不足,可能影响功能使用') - } - - await this.checkSupport() - await this.getState() - this.isOpen = true - } catch (error) { - console.error('蓝牙初始化失败:', error) - throw error - } + } + + clearDevices() { + this.discoveredDevices = [] + this.connectedDevices = [] + this.currentDevice = null + this.isConnected = false + this.characteristicCallbacks.clear() + this.connectionCallbacks.length = 0 + } + + // 便捷方法 + async findDeviceByName(name: string): Promise { + await this.getDiscoveredDevices() + return ( + this.discoveredDevices.find((device) => device.name === name || device.localName === name) || + null + ) + } + + async connectByName(name: string): Promise { + const device = await this.findDeviceByName(name) + if (!device) { + throw new Error(`未找到名为 "${name}" 的设备`) + } + return this.connect(device.deviceId) + } + + isDeviceConnected(deviceId: string): boolean { + return this.connectedDevices.some((device) => device.deviceId === deviceId) + } + + getDeviceSignalStrength(deviceId: string): number | null { + const device = this.discoveredDevices.find((d) => d.deviceId === deviceId) + return device ? device.RSSI : null + } + + // 电子锁快捷操作指令 + private lockServiceId = '0000FFF0-0000-1000-8000-00805F9B34FB' + private lockWriteCharacteristicId = '0000FFF2-0000-1000-8000-00805F9B34FB' // 写特征值 + private lockReadCharacteristicId = '0000FFF1-0000-1000-8000-00805F9B34FB' // 读特征值(用于notify) + private lockCallback: any = null + + /** + * 初始化锁通信 + * @param deviceId 设备ID + * @param callback 数据回调函数 + */ + async initLockCommunication(deviceId: string, callback?: (data: any) => void): Promise { + if (!this.isDeviceConnected(deviceId)) { + throw new Error('设备未连接') } - async reset(): Promise { - try { - if (this.isDiscovering) { - await this.stopDiscovery() - } - - // 断开所有连接 - const disconnectPromises = this.connectedDevices.map(device => - this.disconnect(device.deviceId).catch(err => console.warn('断开连接失败:', err)) - ) - await Promise.all(disconnectPromises) - - this.clearDevices() + this.lockCallback = callback + + try { + // 1. 获取服务对应的所有特征值 + const characteristics = await this.getCharacteristics(deviceId, this.lockServiceId) + + // 打印所有特征值及其属性,用于调试 + console.log( + `[蓝牙调试] 服务 ${this.lockServiceId} 下的所有特征值:`, + JSON.parse(JSON.stringify(characteristics)) + ) + + // 2. 验证写特征值是否存在且可写 + const writeCharacteristic = characteristics.find( + (c) => c.uuid.toUpperCase() === this.lockWriteCharacteristicId.toUpperCase() + ) + + if (!writeCharacteristic) { + console.error(`[蓝牙调试] 未找到写特征值: ${this.lockWriteCharacteristicId}`) + throw new Error(`设备服务中未找到写特征值`) + } + + if ( + !writeCharacteristic.properties.write && + !writeCharacteristic.properties.writeNoResponse + ) { + console.error(`[蓝牙调试] 写特征值不支持写入操作:`, writeCharacteristic.properties) + throw new Error(`写特征值不支持写入操作`) + } + + console.log( + `[蓝牙调试] 写特征值验证通过: ${this.lockWriteCharacteristicId}`, + writeCharacteristic.properties + ) + + // 添加延迟以确保连接稳定 + await new Promise((resolve) => setTimeout(resolve, 500)) + + // 3. 检查是否存在预期的读特征值 + const readCharacteristic = characteristics.find( + (c) => c.uuid.toUpperCase() === this.lockReadCharacteristicId.toUpperCase() + ) + + if (readCharacteristic && readCharacteristic.properties.notify) { + console.log(`[蓝牙调试] 使用预期的读特征值: ${this.lockReadCharacteristicId}`) + + // 先设置监听回调 + this.onCharacteristicValueChange((res: any) => { + // 确保监听的是正确的特征值 + if ( + res.deviceId === deviceId && + res.serviceId.toUpperCase() === this.lockServiceId.toUpperCase() && + res.characteristicId.toUpperCase() === this.lockReadCharacteristicId.toUpperCase() + ) { + console.log('[蓝牙调试] 收到锁响应数据') + this.handleLockResponse(res.value) + } + }) - // 重新初始化 - await this.init() - } catch (error) { - console.error('蓝牙重置失败:', error) - throw error + // 开启读特征值的通知监听 + await this.notifyCharacteristic( + deviceId, + this.lockServiceId, + this.lockReadCharacteristicId, + true + ) + console.log(`[蓝牙调试] 通知监听设置成功,等待连接稳定...`) + + // 等待更长时间确保蓝牙连接稳定,这很关键! + await new Promise((resolve) => setTimeout(resolve, 2000)) + console.log(`[蓝牙调试] 连接稳定化完成,可以开始发送指令`) + + // 启动心跳机制保持连接 + this.startHeartbeat(deviceId) + } else { + // fallback: 查找任何支持 notify 的特征值 + const notifyCharacteristic = characteristics.find((c) => c.properties.notify) + + if (!notifyCharacteristic) { + console.error( + '[蓝牙调试] 未找到任何支持 "notify" 的特征值。请检查设备固件或确认通信协议。' + ) + throw new Error(`该设备的服务中未找到支持通知(notify)的特征值`) } - } - - clearDevices() { - this.discoveredDevices = [] - this.connectedDevices = [] - this.currentDevice = null - this.isConnected = false - this.characteristicCallbacks.clear() - this.connectionCallbacks.length = 0 - } - // 便捷方法 - async findDeviceByName(name: string): Promise { - await this.getDiscoveredDevices() - return this.discoveredDevices.find(device => - device.name === name || device.localName === name - ) || null + const notifyCharacteristicId = notifyCharacteristic.uuid + console.warn(`[蓝牙调试] 未找到预期的读特征值,使用备选特征值: ${notifyCharacteristicId}`) + + // 开启特征值监听 + await this.notifyCharacteristic(deviceId, this.lockServiceId, notifyCharacteristicId, true) + + // 监听特征值变化 + this.onCharacteristicValueChange((res: any) => { + // 确保监听的是正确的特征值 + if ( + res.deviceId === deviceId && + res.serviceId.toUpperCase() === this.lockServiceId.toUpperCase() && + res.characteristicId.toUpperCase() === notifyCharacteristicId.toUpperCase() + ) { + this.handleLockResponse(res.value) + } + }) + } + } catch (err: any) { + // 重新抛出错误,以便上层可以捕获 + if (err.errMsg && err.errMsg.includes('notsupport')) { + throw new Error('当前环境不支持蓝牙通知功能,无法接收设备数据。') + } + throw err } - - async connectByName(name: string): Promise { - const device = await this.findDeviceByName(name) - if (!device) { - throw new Error(`未找到名为 "${name}" 的设备`) + } + + /** + * 创建锁指令 + */ + private createLockInstruct(command: number, data: number[]): number[] { + const header = [0xaa, 0xbb] + let instruction: number[] = [] + let payload: number[] = [] + + instruction = instruction.concat(header) + payload.push(command) + payload = payload.concat(data) + payload.unshift(payload.length + 2) + payload = this.doCrc(payload) + instruction = instruction.concat(payload) + + return instruction + } + + /** + * 执行CRC校验 + */ + private doCrc(data: number[]): number[] { + const crcResult = this.crc16ab(data, true, false) + return data.concat(crcResult) + } + + /** + * CRC16校验算法 + */ + private crc16ab(data: number[], reverseResult: boolean, isHexString: boolean): number[] { + let crc = 0xffff + const polynomial = 0x1021 + + for (let i = 0; i < data.length; i++) { + const byte = isHexString ? parseInt(data[i].toString(), 16) : data[i] + for (let bit = 0; bit < 8; bit++) { + const bitValue = ((byte >> (7 - bit)) & 1) === 1 + const crcBit = ((crc >> 15) & 1) === 1 + crc <<= 1 + if (crcBit !== bitValue) { + crc ^= polynomial } - return this.connect(device.deviceId) + } } - isDeviceConnected(deviceId: string): boolean { - return this.connectedDevices.some(device => device.deviceId === deviceId) + const high = (crc & 0xff00) >> 8 + const low = crc & 0xff + return reverseResult ? [low, high] : [high, low] + } + + /** + * 提交锁指令 + */ + private async submitLockInstruction(deviceId: string, instruction: number[]): Promise { + const hexInstruction = instruction + .map((x) => `0x${x.toString(16).padStart(2, '0').toUpperCase()}`) + .join(' ') + console.log(`[蓝牙调试] 准备发送指令:`, hexInstruction) + + // 详细解析指令结构 + if (instruction.length >= 4) { + console.log(`[蓝牙调试] 指令结构解析:`) + console.log( + ` - 帧头: ${instruction[0].toString(16).padStart(2, '0')} ${instruction[1].toString(16).padStart(2, '0')} (应为 AA BB)` + ) + console.log( + ` - 长度: ${instruction[2].toString(16).padStart(2, '0')} (${instruction[2]} 字节)` + ) + console.log( + ` - 命令: ${instruction[3].toString(16).padStart(2, '0')} (0x${instruction[3].toString(16).toUpperCase()})` + ) + if (instruction.length > 4) { + console.log( + ` - 数据: ${instruction + .slice(4, -2) + .map((x) => x.toString(16).padStart(2, '0')) + .join(' ')}` + ) + console.log( + ` - CRC: ${instruction + .slice(-2) + .map((x) => x.toString(16).padStart(2, '0')) + .join(' ')}` + ) + } } - getDeviceSignalStrength(deviceId: string): number | null { - const device = this.discoveredDevices.find(d => d.deviceId === deviceId) - return device ? device.RSSI : null + // 验证设备是否仍然连接 + if (!this.isDeviceConnected(deviceId)) { + throw new Error('设备已断开连接,无法发送指令') } - // 电子锁快捷操作指令 - private lockServiceId = '0000FFF0-0000-1000-8000-00805F9B34FB' - private lockWriteCharacteristicId = '0000FFF2-0000-1000-8000-00805F9B34FB' // 写特征值 - private lockReadCharacteristicId = '0000FFF1-0000-1000-8000-00805F9B34FB' // 读特征值(用于notify) - private lockCallback: any = null - - /** - * 初始化锁通信 - * @param deviceId 设备ID - * @param callback 数据回调函数 - */ - async initLockCommunication(deviceId: string, callback?: (data: any) => void): Promise { - if (!this.isDeviceConnected(deviceId)) { - throw new Error('设备未连接'); - } - - this.lockCallback = callback; - - try { - // 1. 获取服务对应的所有特征值 - const characteristics = await this.getCharacteristics(deviceId, this.lockServiceId); - - // 打印所有特征值及其属性,用于调试 - console.log(`[蓝牙调试] 服务 ${this.lockServiceId} 下的所有特征值:`, JSON.parse(JSON.stringify(characteristics))); - - // 2. 验证写特征值是否存在且可写 - const writeCharacteristic = characteristics.find(c => - c.uuid.toUpperCase() === this.lockWriteCharacteristicId.toUpperCase() - ); - - if (!writeCharacteristic) { - console.error(`[蓝牙调试] 未找到写特征值: ${this.lockWriteCharacteristicId}`); - throw new Error(`设备服务中未找到写特征值`); - } - - if (!writeCharacteristic.properties.write && !writeCharacteristic.properties.writeNoResponse) { - console.error(`[蓝牙调试] 写特征值不支持写入操作:`, writeCharacteristic.properties); - throw new Error(`写特征值不支持写入操作`); - } - - console.log(`[蓝牙调试] 写特征值验证通过: ${this.lockWriteCharacteristicId}`, writeCharacteristic.properties); - - // 添加延迟以确保连接稳定 - await new Promise(resolve => setTimeout(resolve, 500)); - - // 3. 检查是否存在预期的读特征值 - const readCharacteristic = characteristics.find(c => - c.uuid.toUpperCase() === this.lockReadCharacteristicId.toUpperCase() - ); - - if (readCharacteristic && readCharacteristic.properties.notify) { - console.log(`[蓝牙调试] 使用预期的读特征值: ${this.lockReadCharacteristicId}`); - - // 先设置监听回调 - this.onCharacteristicValueChange((res: any) => { - // 确保监听的是正确的特征值 - if (res.deviceId === deviceId && - res.serviceId.toUpperCase() === this.lockServiceId.toUpperCase() && - res.characteristicId.toUpperCase() === this.lockReadCharacteristicId.toUpperCase()) { - console.log('[蓝牙调试] 收到锁响应数据'); - this.handleLockResponse(res.value); - } - }); - - // 开启读特征值的通知监听 - await this.notifyCharacteristic(deviceId, this.lockServiceId, this.lockReadCharacteristicId, true); - console.log(`[蓝牙调试] 通知监听设置成功,等待连接稳定...`); - - // 等待更长时间确保蓝牙连接稳定,这很关键! - await new Promise(resolve => setTimeout(resolve, 2000)); - console.log(`[蓝牙调试] 连接稳定化完成,可以开始发送指令`); - - // 启动心跳机制保持连接 - this.startHeartbeat(deviceId); - - } else { - // fallback: 查找任何支持 notify 的特征值 - const notifyCharacteristic = characteristics.find(c => c.properties.notify); - - if (!notifyCharacteristic) { - console.error('[蓝牙调试] 未找到任何支持 "notify" 的特征值。请检查设备固件或确认通信协议。'); - throw new Error(`该设备的服务中未找到支持通知(notify)的特征值`); - } - - const notifyCharacteristicId = notifyCharacteristic.uuid; - console.warn(`[蓝牙调试] 未找到预期的读特征值,使用备选特征值: ${notifyCharacteristicId}`); - - // 开启特征值监听 - await this.notifyCharacteristic(deviceId, this.lockServiceId, notifyCharacteristicId, true); - - // 监听特征值变化 - this.onCharacteristicValueChange((res: any) => { - // 确保监听的是正确的特征值 - if (res.deviceId === deviceId && - res.serviceId.toUpperCase() === this.lockServiceId.toUpperCase() && - res.characteristicId.toUpperCase() === notifyCharacteristicId.toUpperCase()) { - this.handleLockResponse(res.value); - } - }); - } - - } catch (err: any) { - // 重新抛出错误,以便上层可以捕获 - if (err.errMsg && err.errMsg.includes('notsupport')) { - throw new Error('当前环境不支持蓝牙通知功能,无法接收设备数据。'); - } - throw err; - } + // 在企业微信环境中,再次确认连接状态 + try { + const connectedDevices = await this.getConnectedDevices() + const isConnected = connectedDevices.some((device) => device.deviceId === deviceId) + if (!isConnected) { + throw new Error('设备连接状态异常,请重新连接设备') + } + console.log(`[蓝牙调试] 设备连接状态确认正常`) + } catch (error) { + console.warn(`[蓝牙调试] 无法验证连接状态:`, error) + // 不阻塞流程,继续尝试发送 } - /** - * 创建锁指令 - */ - private createLockInstruct(command: number, data: number[]): number[] { - const header = [0xaa, 0xbb] - let instruction: number[] = [] - let payload: number[] = [] - - instruction = instruction.concat(header) - payload.push(command) - payload = payload.concat(data) - payload.unshift(payload.length + 2) - payload = this.doCrc(payload) - instruction = instruction.concat(payload) - - return instruction + // 验证指令数组 + if (!instruction || instruction.length === 0) { + throw new Error('指令数据无效:指令数组为空') } - /** - * 执行CRC校验 - */ - private doCrc(data: number[]): number[] { - const crcResult = this.crc16ab(data, true, false) - return data.concat(crcResult) + // 验证指令内容 + for (let i = 0; i < instruction.length; i++) { + if (instruction[i] < 0 || instruction[i] > 255) { + throw new Error(`指令数据无效:第${i}个字节值${instruction[i]}超出范围[0-255]`) + } } - /** - * CRC16校验算法 - */ - private crc16ab(data: number[], reverseResult: boolean, isHexString: boolean): number[] { - let crc = 0xffff - const polynomial = 0x1021 - - for (let i = 0; i < data.length; i++) { - const byte = isHexString ? parseInt(data[i].toString(), 16) : data[i] - for (let bit = 0; bit < 8; bit++) { - const bitValue = (byte >> (7 - bit) & 1) === 1 - const crcBit = (crc >> 15 & 1) === 1 - crc <<= 1 - if (crcBit !== bitValue) { - crc ^= polynomial - } - } - } - - const high = (crc & 0xff00) >> 8 - const low = crc & 0xff - return reverseResult ? [low, high] : [high, low] + // 企业微信环境下,确保ArrayBuffer创建正确 + let buffer: ArrayBuffer + try { + buffer = new Uint8Array(instruction).buffer + } catch (error) { + console.error(`[蓝牙调试] 创建ArrayBuffer失败:`, error) + throw new Error('指令数据格式转换失败') } - /** - * 提交锁指令 - */ - private async submitLockInstruction(deviceId: string, instruction: number[]): Promise { - const hexInstruction = instruction.map(x => `0x${x.toString(16).padStart(2, '0').toUpperCase()}`).join(' ') - console.log(`[蓝牙调试] 准备发送指令:`, hexInstruction); - - // 详细解析指令结构 - if (instruction.length >= 4) { - console.log(`[蓝牙调试] 指令结构解析:`); - console.log(` - 帧头: ${instruction[0].toString(16).padStart(2, '0')} ${instruction[1].toString(16).padStart(2, '0')} (应为 AA BB)`); - console.log(` - 长度: ${instruction[2].toString(16).padStart(2, '0')} (${instruction[2]} 字节)`); - console.log(` - 命令: ${instruction[3].toString(16).padStart(2, '0')} (0x${instruction[3].toString(16).toUpperCase()})`); - if (instruction.length > 4) { - console.log(` - 数据: ${instruction.slice(4, -2).map(x => x.toString(16).padStart(2, '0')).join(' ')}`); - console.log(` - CRC: ${instruction.slice(-2).map(x => x.toString(16).padStart(2, '0')).join(' ')}`); - } - } - - // 验证设备是否仍然连接 - if (!this.isDeviceConnected(deviceId)) { - throw new Error('设备已断开连接,无法发送指令'); - } - - // 在企业微信环境中,再次确认连接状态 - try { - const connectedDevices = await this.getConnectedDevices(); - const isConnected = connectedDevices.some(device => device.deviceId === deviceId); - if (!isConnected) { - throw new Error('设备连接状态异常,请重新连接设备'); - } - console.log(`[蓝牙调试] 设备连接状态确认正常`); - } catch (error) { - console.warn(`[蓝牙调试] 无法验证连接状态:`, error); - // 不阻塞流程,继续尝试发送 - } - - // 验证指令数组 - if (!instruction || instruction.length === 0) { - throw new Error('指令数据无效:指令数组为空'); - } - - // 验证指令内容 - for (let i = 0; i < instruction.length; i++) { - if (instruction[i] < 0 || instruction[i] > 255) { - throw new Error(`指令数据无效:第${i}个字节值${instruction[i]}超出范围[0-255]`); - } - } - - // 企业微信环境下,确保ArrayBuffer创建正确 - let buffer: ArrayBuffer; - try { - buffer = new Uint8Array(instruction).buffer; - } catch (error) { - console.error(`[蓝牙调试] 创建ArrayBuffer失败:`, error); - throw new Error('指令数据格式转换失败'); - } - - if (buffer.byteLength === 0) { - throw new Error('指令数据无效:转换后的buffer为空'); - } - - // 企业微信可能对单次发送的数据大小有限制 - if (buffer.byteLength > 20) { - console.warn(`[蓝牙调试] 指令长度 ${buffer.byteLength} 超过建议的20字节,可能导致发送失败`); - } - - console.log(`[蓝牙调试] 指令字节长度: ${buffer.byteLength}`); - console.log(`[蓝牙调试] 发送到设备: ${deviceId}`); - console.log(`[蓝牙调试] 服务ID: ${this.lockServiceId}`); - console.log(`[蓝牙调试] 写特征值ID: ${this.lockWriteCharacteristicId}`); - - try { - const result = await this.writeCharacteristic(deviceId, this.lockServiceId, this.lockWriteCharacteristicId, buffer); - console.log(`[蓝牙调试] 指令发送成功`); - return result; - } catch (error: any) { - console.error(`[蓝牙调试] 指令发送失败:`, error); - - // 针对不同的错误类型提供更详细的错误信息 - if (error.message) { - // 如果是我们自定义的错误消息,直接抛出 - if (error.message.includes('蓝牙写入失败') || - error.message.includes('设备已断开') || - error.message.includes('无效的特征值') || - error.message.includes('不允许写入')) { - throw error; - } - } - - if (error.errMsg) { - if (error.errMsg.includes('10008') || error.errCode === 10008) { - // 10008 错误通常是连接问题 - throw new Error('蓝牙连接不稳定(错误码10008),建议:1.重新连接设备 2.确保设备距离足够近 3.检查设备是否被其他应用占用'); - } else if (error.errMsg.includes('10009')) { - throw new Error('蓝牙服务不存在或已断开'); - } else if (error.errMsg.includes('10010')) { - throw new Error('特征值不存在或不支持写入'); - } else if (error.errMsg.includes('system err')) { - throw new Error('系统蓝牙错误,可能的原因:1.设备固件不兼容 2.特征值权限不足 3.企业微信蓝牙权限问题'); - } - } - - throw error; - } + if (buffer.byteLength === 0) { + throw new Error('指令数据无效:转换后的buffer为空') } - /** - * 处理锁响应数据 - */ - private handleLockResponse(data: ArrayBuffer): void { - // eslint-disable-next-line - let hexData = [...this.arrayBufferToHex(data)] // 创建可修改的副本 - console.log(`[蓝牙调试] 收到原始数据:`, hexData.join(' ')) - this.logLockOperation('接收数据', `原始数据: ${hexData.join(' ')}`, 'info') - - let processCount = 0 - const maxProcessCount = 10 // 防止无限循环 - - while (hexData.indexOf('aa') >= 0 && processCount < maxProcessCount) { - const startIndex = hexData.indexOf('aa') - - // 检查数据长度是否足够 - if (startIndex + 2 >= hexData.length) { - console.warn(`[蓝牙调试] 数据长度不足,无法解析`) - break - } - - const length = parseInt(hexData[startIndex + 2], 16) - - // 检查长度是否合理 - if (isNaN(length) || length <= 0 || length > 100) { - console.warn(`[蓝牙调试] 无效的数据长度: ${length}`) - // 跳过这个'aa'继续寻找下一个 - hexData.splice(startIndex, 1) - processCount++ - continue - } - - const endIndex = startIndex + length + 3 - - // 检查是否有足够的数据 - if (endIndex > hexData.length) { - console.warn(`[蓝牙调试] 数据包不完整,需要 ${endIndex} 但只有 ${hexData.length} 字节`) - break - } - - const instructHex = hexData.slice(startIndex, endIndex) - console.log(`[蓝牙调试] 解析数据包:`, instructHex.join(' ')) - - const result = this.parseLockResponse([...instructHex]) // 创建副本避免修改原数组 - - if (this.lockCallback) { - this.lockCallback(result) - } - - // 移除已处理的数据,防止重复处理 - hexData.splice(startIndex, endIndex - startIndex) - processCount++ - } - - if (processCount >= maxProcessCount) { - console.error(`[蓝牙调试] 数据处理达到最大循环次数,可能存在问题`) - } + // 企业微信可能对单次发送的数据大小有限制 + if (buffer.byteLength > 20) { + console.warn(`[蓝牙调试] 指令长度 ${buffer.byteLength} 超过建议的20字节,可能导致发送失败`) } - /** - * 解析锁响应 - */ - private parseLockResponse(instructArray: string[]): any { - if (instructArray.shift() !== 'aa') { - this.logLockOperation('解析响应', '数据头错误 - 缺少aa标识', 'error') - return this.createCallData('-1', '-1', '数据错误') + console.log(`[蓝牙调试] 指令字节长度: ${buffer.byteLength}`) + console.log(`[蓝牙调试] 发送到设备: ${deviceId}`) + console.log(`[蓝牙调试] 服务ID: ${this.lockServiceId}`) + console.log(`[蓝牙调试] 写特征值ID: ${this.lockWriteCharacteristicId}`) + + try { + const result = await this.writeCharacteristic( + deviceId, + this.lockServiceId, + this.lockWriteCharacteristicId, + buffer + ) + console.log(`[蓝牙调试] 指令发送成功`) + return result + } catch (error: any) { + console.error(`[蓝牙调试] 指令发送失败:`, error) + + // 针对不同的错误类型提供更详细的错误信息 + if (error.message) { + // 如果是我们自定义的错误消息,直接抛出 + if ( + error.message.includes('蓝牙写入失败') || + error.message.includes('设备已断开') || + error.message.includes('无效的特征值') || + error.message.includes('不允许写入') + ) { + throw error } - if (instructArray.shift() !== 'bb') { - this.logLockOperation('解析响应', '数据头错误 - 缺少bb标识', 'error') - return this.createCallData('-1', '-1', '数据错误') + } + + if (error.errMsg) { + if (error.errMsg.includes('10008') || error.errCode === 10008) { + // 10008 错误通常是连接问题 + throw new Error( + '蓝牙连接不稳定(错误码10008),建议:1.重新连接设备 2.确保设备距离足够近 3.检查设备是否被其他应用占用' + ) + } else if (error.errMsg.includes('10009')) { + throw new Error('蓝牙服务不存在或已断开') + } else if (error.errMsg.includes('10010')) { + throw new Error('特征值不存在或不支持写入') + } else if (error.errMsg.includes('system err')) { + throw new Error( + '系统蓝牙错误,可能的原因:1.设备固件不兼容 2.特征值权限不足 3.企业微信蓝牙权限问题' + ) } + } - const command = parseInt(instructArray[1], 16) - this.logLockOperation('解析响应', `收到指令响应: 0x${command.toString(16)}`, 'info', { command, rawData: instructArray }) - - switch (command) { - case 0x8: // 锁状态指令 - const statusResult = instructArray[2] === '01' - ? this.createCallData('08', '01', '手柄打开') - : this.createCallData('08', '00', '手柄关闭') - this.logLockOperation('获取状态', `锁状态: ${statusResult.msg}`, 'success', { - status: statusResult.data, - rawResponse: instructArray - }) - return statusResult - - case 0x7: // 开锁指令 - let openResult - if (instructArray[2] === '01') { - openResult = this.createCallData('07', '01', '开锁成功') - this.logLockOperation('开锁操作', '开锁成功', 'success', { - result: '01', - rawResponse: instructArray - }) - } else if (instructArray[2] === '02') { - // 按照微信小程序逻辑,02表示修改清除/开锁失败 - openResult = this.createCallData('07', '00', '开锁失败') - this.logLockOperation('开锁操作', '开锁失败(修改清除)', 'warn', { - result: '02', - rawResponse: instructArray - }) - } else { - openResult = this.createCallData('07', '00', '开锁失败') - this.logLockOperation('开锁操作', `开锁失败(响应码: ${instructArray[2]})`, 'warn', { - result: instructArray[2], - rawResponse: instructArray - }) - } - return openResult - - case 0x12: // 设置锁ID指令 - const setIdResult = instructArray[2] === '01' - ? this.createCallData('12', '01', '修改成功') - : this.createCallData('12', '00', '修改失败') - this.logLockOperation('设置锁ID', setIdResult.msg, setIdResult.data === '01' ? 'success' : 'error', { - result: setIdResult.data, - rawResponse: instructArray - }) - return setIdResult - - case 0x11: // 获取锁ID指令 - if (instructArray[2] === '01') { - const idData = instructArray.slice(3, 7) - const idArray = this.hexToArray(idData) - const lockId = this.bytesToInt(idArray) - const getIdResult = this.createCallData('11', lockId, '获取成功') - this.logLockOperation('获取锁ID', `锁ID: ${lockId}`, 'success', { - lockId, - hexData: idData, - rawResponse: instructArray - }) - return getIdResult - } else { - this.logLockOperation('获取锁ID', '获取锁ID失败', 'error', { rawResponse: instructArray }) - return this.createCallData('11', '00', '获取失败') - } - - case 0x10: // 发送开锁密钥指令 - const keyResult = instructArray[2] === '01' - ? this.createCallData('10', '01', '钥匙开锁') - : this.createCallData('10', '00', '开锁失败') - this.logLockOperation('密钥测试', keyResult.msg, keyResult.data === '01' ? 'success' : 'warn', { - result: keyResult.data, - rawResponse: instructArray - }) - return keyResult - - case 0x5: // 设置蓝牙名称指令 - const nameResult = instructArray[2] === '01' - ? this.createCallData('05', '01', '修改成功') - : this.createCallData('05', '00', '修改失败') - this.logLockOperation('设置蓝牙名称', nameResult.msg, nameResult.data === '01' ? 'success' : 'error') - return nameResult - - case 0x22: // 设置任务时间指令 - const timeSetResult = instructArray[2] === '01' - ? this.createCallData('22', '01', '修改成功') - : this.createCallData('22', '00', '修改失败') - this.logLockOperation('设置任务时间', timeSetResult.msg, timeSetResult.data === '01' ? 'success' : 'error') - return timeSetResult - - case 0x23: // 设置任务权限指令 - const permissionResult = instructArray[2] === '01' - ? this.createCallData('23', '01', '修改成功') - : this.createCallData('23', '00', '修改失败') - this.logLockOperation('设置任务权限', permissionResult.msg, permissionResult.data === '01' ? 'success' : 'error') - return permissionResult - - case 0x0a: // 获取当前时间指令 - // 时间数据在instructArray[2]到instructArray[6] - const timeData = instructArray.slice(2, 7) - // 这里可以处理时间数据,暂时返回成功 - const timeResult = this.createCallData('0a', '01', '时间获取成功') - this.logLockOperation('获取当前时间', '设备时间获取成功', 'success', { timeData }) - return timeResult - - case 0x2a: // 清除任务权限指令 - const clearResult = instructArray[2] === '01' - ? this.createCallData('2A', '01', '清除成功') - : this.createCallData('2A', '00', '清除失败') - this.logLockOperation('清除任务权限', clearResult.msg, clearResult.data === '01' ? 'success' : 'error') - return clearResult - - case 0xff: // 心跳响应 - // 心跳响应不记录日志,避免刷屏 - return this.createCallData('ff', '01', '心跳正常') - - case 0x16: // 可能是设备状态或版本信息响应 - // 根据设备固件,0x16可能是设备信息或状态响应 - const deviceInfoResult = this.createCallData('16', '01', '设备信息响应') - this.logLockOperation('设备信息', '收到设备信息或状态响应 (0x16)', 'info', { - rawResponse: instructArray, - possibleInfo: '设备版本或状态信息' - }) - return deviceInfoResult - } - - this.logLockOperation('解析响应', `未知指令: 0x${command.toString(16)}`, 'warn', { command, rawData: instructArray }) - return this.createCallData('-1', '-1', '未知指令') - } - - /** - * 创建回调数据 - */ - private createCallData(code: string, data: any, msg: string): any { - return { code, data, msg } - } - - /** - * ArrayBuffer转十六进制数组 - */ - private arrayBufferToHex(buffer: ArrayBuffer): string[] { - return Array.from(new Uint8Array(buffer), byte => - ('00' + byte.toString(16)).slice(-2) - ) - } - - /** - * 十六进制数组转数字数组 - */ - private hexToArray(hexArray: string[]): number[] { - return hexArray.map(hex => parseInt(hex, 16)) + throw error } - - /** - * 字节数组转整数 - */ - private bytesToInt(bytes: number[]): number { - const byte0 = bytes[3] & 0xff - const byte1 = bytes[2] & 0xff - const byte2 = bytes[1] & 0xff - const byte3 = bytes[0] & 0xff - return (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0 + } + + /** + * 处理锁响应数据 + */ + private handleLockResponse(data: ArrayBuffer): void { + // eslint-disable-next-line + let hexData = [...this.arrayBufferToHex(data)] // 创建可修改的副本 + console.log(`[蓝牙调试] 收到原始数据:`, hexData.join(' ')) + this.logLockOperation('接收数据', `原始数据: ${hexData.join(' ')}`, 'info') + + let processCount = 0 + const maxProcessCount = 10 // 防止无限循环 + + while (hexData.indexOf('aa') >= 0 && processCount < maxProcessCount) { + const startIndex = hexData.indexOf('aa') + + // 检查数据长度是否足够 + if (startIndex + 2 >= hexData.length) { + console.warn(`[蓝牙调试] 数据长度不足,无法解析`) + break + } + + const length = parseInt(hexData[startIndex + 2], 16) + + // 检查长度是否合理 + if (isNaN(length) || length <= 0 || length > 100) { + console.warn(`[蓝牙调试] 无效的数据长度: ${length}`) + // 跳过这个'aa'继续寻找下一个 + hexData.splice(startIndex, 1) + processCount++ + continue + } + + const endIndex = startIndex + length + 3 + + // 检查是否有足够的数据 + if (endIndex > hexData.length) { + console.warn(`[蓝牙调试] 数据包不完整,需要 ${endIndex} 但只有 ${hexData.length} 字节`) + break + } + + const instructHex = hexData.slice(startIndex, endIndex) + console.log(`[蓝牙调试] 解析数据包:`, instructHex.join(' ')) + + const result = this.parseLockResponse([...instructHex]) // 创建副本避免修改原数组 + + if (this.lockCallback) { + this.lockCallback(result) + } + + // 移除已处理的数据,防止重复处理 + hexData.splice(startIndex, endIndex - startIndex) + processCount++ } - /** - * 整数转4字节数组(大端序) - */ - private intToBytes(value: number): number[] { - const bytes: number[] = [] - bytes[3] = value & 0xff - bytes[2] = (value >> 8) & 0xff - bytes[1] = (value >> 16) & 0xff - bytes[0] = (value >> 24) & 0xff - return bytes + if (processCount >= maxProcessCount) { + console.error(`[蓝牙调试] 数据处理达到最大循环次数,可能存在问题`) } - - /** - * 整数转4字节数组(小端序) - */ - private intToByte4R(value: number): number[] { - const bytes: number[] = [] - bytes[0] = value & 0xff - bytes[1] = (value >> 8) & 0xff - bytes[2] = (value >> 16) & 0xff - bytes[3] = (value >> 24) & 0xff - return bytes + } + + /** + * 解析锁响应 + */ + private parseLockResponse(instructArray: string[]): any { + if (instructArray.shift() !== 'aa') { + this.logLockOperation('解析响应', '数据头错误 - 缺少aa标识', 'error') + return this.createCallData('-1', '-1', '数据错误') } - - /** - * 字符串转Uint8数组 - */ - private stringToBytes(str: string): number[] { - const result: number[] = [] - for (let i = 0; i < str.length; i++) { - result[i] = str.charCodeAt(i) - } - return result + if (instructArray.shift() !== 'bb') { + this.logLockOperation('解析响应', '数据头错误 - 缺少bb标识', 'error') + return this.createCallData('-1', '-1', '数据错误') } - /** - * 记录锁操作日志 - * @param operation 操作类型 - * @param message 消息内容 - * @param level 日志级别 - * @param data 额外数据 - */ - private logLockOperation(operation: string, message: string, level: 'info' | 'warn' | 'error' | 'success' = 'info', data?: any): void { - const timestamp = new Date().toLocaleString('zh-CN') - // const logEntry = { - // timestamp, - // operation, - // message, - // level, - // data: data || null, - // deviceId: this.currentHeartbeatDeviceId - // } - - // 控制台输出 - const consoleMessage = `[${timestamp}] [锁操作-${operation}] ${message}` - - switch (level) { - case 'info': - console.info(consoleMessage, data) - break - case 'warn': - console.warn(consoleMessage, data) - break - case 'error': - console.error(consoleMessage, data) - break - case 'success': - console.log(`✅ ${consoleMessage}`, data) - break - } - - // 可以在这里添加发送到后端的日志记录 - // await this.sendLogToServer(logEntry) - } - - // 锁操作快捷方法 - - /** - * 开锁指令 - * @param deviceId 设备ID - * @returns Promise - */ - async openLock(deviceId: string): Promise { - this.logLockOperation('开锁操作', `发送开锁指令到设备: ${deviceId}`, 'info') - // 按照微信小程序的参数:[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] - const data = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] - const instruction = this.createLockInstruct(0x07, data) - - this.logLockOperation('开锁操作', '构建开锁指令', 'info', { - command: '0x07', - data: data.map(x => `0x${x.toString(16).toUpperCase()}`), - fullInstruction: instruction.map(x => `0x${x.toString(16).padStart(2, '0').toUpperCase()}`) + const command = parseInt(instructArray[1], 16) + this.logLockOperation('解析响应', `收到指令响应: 0x${command.toString(16)}`, 'info', { + command, + rawData: instructArray + }) + + switch (command) { + case 0x8: // 锁状态指令 + const statusResult = + instructArray[2] === '01' + ? this.createCallData('08', '01', '手柄打开') + : this.createCallData('08', '00', '手柄关闭') + this.logLockOperation('获取状态', `锁状态: ${statusResult.msg}`, 'success', { + status: statusResult.data, + rawResponse: instructArray }) - - try { - const result = await this.submitLockInstruction(deviceId, instruction) - this.logLockOperation('开锁操作', '开锁指令发送成功', 'success', { - instruction: instruction.map(x => `0x${x.toString(16).padStart(2, '0').toUpperCase()}`) - }) - return result - } catch (error: any) { - this.logLockOperation('开锁操作', `开锁指令发送失败: ${error.message}`, 'error', { error }) - throw error + return statusResult + + case 0x7: // 开锁指令 + let openResult + if (instructArray[2] === '01') { + openResult = this.createCallData('07', '01', '开锁成功') + this.logLockOperation('开锁操作', '开锁成功', 'success', { + result: '01', + rawResponse: instructArray + }) + } else if (instructArray[2] === '02') { + // 按照微信小程序逻辑,02表示修改清除/开锁失败 + openResult = this.createCallData('07', '00', '开锁失败') + this.logLockOperation('开锁操作', '开锁失败(修改清除)', 'warn', { + result: '02', + rawResponse: instructArray + }) + } else { + openResult = this.createCallData('07', '00', '开锁失败') + this.logLockOperation('开锁操作', `开锁失败(响应码: ${instructArray[2]})`, 'warn', { + result: instructArray[2], + rawResponse: instructArray + }) } - } - - /** - * 获取锁状态 - * @param deviceId 设备ID - * @returns Promise - */ - async getLockStatus(deviceId: string): Promise { - this.logLockOperation('获取状态', `发送状态查询指令到设备: ${deviceId}`, 'info') - const data: number[] = [] - const instruction = this.createLockInstruct(0x8, data) - try { - const result = await this.submitLockInstruction(deviceId, instruction) - this.logLockOperation('获取状态', '状态查询指令发送成功', 'success', { instruction: instruction.map(x => x.toString(16)) }) - return result - } catch (error: any) { - this.logLockOperation('获取状态', `状态查询指令发送失败: ${error.message}`, 'error', { error }) - throw error + return openResult + + case 0x12: // 设置锁ID指令 + const setIdResult = + instructArray[2] === '01' + ? this.createCallData('12', '01', '修改成功') + : this.createCallData('12', '00', '修改失败') + this.logLockOperation( + '设置锁ID', + setIdResult.msg, + setIdResult.data === '01' ? 'success' : 'error', + { + result: setIdResult.data, + rawResponse: instructArray + } + ) + return setIdResult + + case 0x11: // 获取锁ID指令 + if (instructArray[2] === '01') { + const idData = instructArray.slice(3, 7) + const idArray = this.hexToArray(idData) + const lockId = this.bytesToInt(idArray) + const getIdResult = this.createCallData('11', lockId, '获取成功') + this.logLockOperation('获取锁ID', `锁ID: ${lockId}`, 'success', { + lockId, + hexData: idData, + rawResponse: instructArray + }) + return getIdResult + } else { + this.logLockOperation('获取锁ID', '获取锁ID失败', 'error', { rawResponse: instructArray }) + return this.createCallData('11', '00', '获取失败') } - } - - /** - * 发送心跳 - * @param deviceId 设备ID - * @returns Promise - */ - async sendHeartbeat(deviceId: string): Promise { - const data: number[] = [] - const instruction = this.createLockInstruct(0xff, data) - // 心跳发送不记录日志,避免刷屏 - try { - const result = await this.submitLockInstruction(deviceId, instruction) - return result - } catch (error: any) { - // 心跳失败也不记录日志,保持安静 - throw error - } + case 0x10: // 发送开锁密钥指令 + const keyResult = + instructArray[2] === '01' + ? this.createCallData('10', '01', '钥匙开锁') + : this.createCallData('10', '00', '开锁失败') + this.logLockOperation( + '密钥测试', + keyResult.msg, + keyResult.data === '01' ? 'success' : 'warn', + { + result: keyResult.data, + rawResponse: instructArray + } + ) + return keyResult + + case 0x5: // 设置蓝牙名称指令 + const nameResult = + instructArray[2] === '01' + ? this.createCallData('05', '01', '修改成功') + : this.createCallData('05', '00', '修改失败') + this.logLockOperation( + '设置蓝牙名称', + nameResult.msg, + nameResult.data === '01' ? 'success' : 'error' + ) + return nameResult + + case 0x22: // 设置任务时间指令 + const timeSetResult = + instructArray[2] === '01' + ? this.createCallData('22', '01', '修改成功') + : this.createCallData('22', '00', '修改失败') + this.logLockOperation( + '设置任务时间', + timeSetResult.msg, + timeSetResult.data === '01' ? 'success' : 'error' + ) + return timeSetResult + + case 0x23: // 设置任务权限指令 + const permissionResult = + instructArray[2] === '01' + ? this.createCallData('23', '01', '修改成功') + : this.createCallData('23', '00', '修改失败') + this.logLockOperation( + '设置任务权限', + permissionResult.msg, + permissionResult.data === '01' ? 'success' : 'error' + ) + return permissionResult + + case 0x0a: // 获取当前时间指令 + // 时间数据在instructArray[2]到instructArray[6] + const timeData = instructArray.slice(2, 7) + // 这里可以处理时间数据,暂时返回成功 + const timeResult = this.createCallData('0a', '01', '时间获取成功') + this.logLockOperation('获取当前时间', '设备时间获取成功', 'success', { timeData }) + return timeResult + + case 0x2a: // 清除任务权限指令 + const clearResult = + instructArray[2] === '01' + ? this.createCallData('2A', '01', '清除成功') + : this.createCallData('2A', '00', '清除失败') + this.logLockOperation( + '清除任务权限', + clearResult.msg, + clearResult.data === '01' ? 'success' : 'error' + ) + return clearResult + + case 0xff: // 心跳响应 + // 心跳响应不记录日志,避免刷屏 + return this.createCallData('ff', '01', '心跳正常') + + case 0x16: // 可能是设备状态或版本信息响应 + // 根据设备固件,0x16可能是设备信息或状态响应 + const deviceInfoResult = this.createCallData('16', '01', '设备信息响应') + this.logLockOperation('设备信息', '收到设备信息或状态响应 (0x16)', 'info', { + rawResponse: instructArray, + possibleInfo: '设备版本或状态信息' + }) + return deviceInfoResult } - /** - * 启动心跳机制 - * @param deviceId 设备ID - * @param interval 心跳间隔,默认2000ms(仿照微信小程序) - */ - startHeartbeat(deviceId: string, interval: number = 2000): void { - // 清理现有的心跳 - this.stopHeartbeat() - - this.currentHeartbeatDeviceId = deviceId - // console.log(`[蓝牙调试] 启动心跳机制,设备: ${deviceId}, 间隔: ${interval}ms`) - - // 按照微信小程序的逻辑,心跳持续运行,不因其他指令而停止 - this.heartbeatInterval = setInterval(async () => { - if (this.currentHeartbeatDeviceId && this.isDeviceConnected(this.currentHeartbeatDeviceId)) { - try { - // 静默发送心跳,不记录日志 - await this.sendHeartbeat(this.currentHeartbeatDeviceId) - } catch (error: any) { - // 心跳失败不记录日志,保持安静 - // 心跳失败可能表示连接有问题,但按微信小程序逻辑不立即停止 - } - } else { - // console.log(`[蓝牙调试] 设备已断开,停止心跳`) - this.stopHeartbeat() - } - }, interval) + this.logLockOperation('解析响应', `未知指令: 0x${command.toString(16)}`, 'warn', { + command, + rawData: instructArray + }) + return this.createCallData('-1', '-1', '未知指令') + } + + /** + * 创建回调数据 + */ + private createCallData(code: string, data: any, msg: string): any { + return { code, data, msg } + } + + /** + * ArrayBuffer转十六进制数组 + */ + private arrayBufferToHex(buffer: ArrayBuffer): string[] { + return Array.from(new Uint8Array(buffer), (byte) => ('00' + byte.toString(16)).slice(-2)) + } + + /** + * 十六进制数组转数字数组 + */ + private hexToArray(hexArray: string[]): number[] { + return hexArray.map((hex) => parseInt(hex, 16)) + } + + /** + * 字节数组转整数 + */ + private bytesToInt(bytes: number[]): number { + const byte0 = bytes[3] & 0xff + const byte1 = bytes[2] & 0xff + const byte2 = bytes[1] & 0xff + const byte3 = bytes[0] & 0xff + return (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0 + } + + /** + * 整数转4字节数组(大端序) + */ + private intToBytes(value: number): number[] { + const bytes: number[] = [] + bytes[3] = value & 0xff + bytes[2] = (value >> 8) & 0xff + bytes[1] = (value >> 16) & 0xff + bytes[0] = (value >> 24) & 0xff + return bytes + } + + /** + * 整数转4字节数组(小端序) + */ + private intToByte4R(value: number): number[] { + const bytes: number[] = [] + bytes[0] = value & 0xff + bytes[1] = (value >> 8) & 0xff + bytes[2] = (value >> 16) & 0xff + bytes[3] = (value >> 24) & 0xff + return bytes + } + + /** + * 字符串转Uint8数组 + */ + private stringToBytes(str: string): number[] { + const result: number[] = [] + for (let i = 0; i < str.length; i++) { + result[i] = str.charCodeAt(i) } - - /** - * 停止心跳机制 - */ - stopHeartbeat(): void { - if (this.heartbeatInterval) { - clearInterval(this.heartbeatInterval) - this.heartbeatInterval = null - // console.log(`[蓝牙调试] 心跳机制已停止`) - } - this.currentHeartbeatDeviceId = null + return result + } + + /** + * 记录锁操作日志 + * @param operation 操作类型 + * @param message 消息内容 + * @param level 日志级别 + * @param data 额外数据 + */ + private logLockOperation( + operation: string, + message: string, + level: 'info' | 'warn' | 'error' | 'success' = 'info', + data?: any + ): void { + const timestamp = new Date().toLocaleString('zh-CN') + // const logEntry = { + // timestamp, + // operation, + // message, + // level, + // data: data || null, + // deviceId: this.currentHeartbeatDeviceId + // } + + // 控制台输出 + const consoleMessage = `[${timestamp}] [锁操作-${operation}] ${message}` + + switch (level) { + case 'info': + console.info(consoleMessage, data) + break + case 'warn': + console.warn(consoleMessage, data) + break + case 'error': + console.error(consoleMessage, data) + break + case 'success': + console.log(`✅ ${consoleMessage}`, data) + break } - /** - * 设置蓝牙名称 - * @param deviceId 设备ID - * @param name 蓝牙名称 - * @returns Promise - */ - async setLockBluetoothName(deviceId: string, name: string): Promise { - const nameData = this.stringToBytes(name) - const instruction = this.createLockInstruct(0x5, nameData) - return this.submitLockInstruction(deviceId, instruction) + // 可以在这里添加发送到后端的日志记录 + // await this.sendLogToServer(logEntry) + } + + // 锁操作快捷方法 + + /** + * 开锁指令 + * @param deviceId 设备ID + * @returns Promise + */ + async openLock(deviceId: string): Promise { + this.logLockOperation('开锁操作', `发送开锁指令到设备: ${deviceId}`, 'info') + // 按照微信小程序的参数:[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] + const data = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff] + const instruction = this.createLockInstruct(0x07, data) + + this.logLockOperation('开锁操作', '构建开锁指令', 'info', { + command: '0x07', + data: data.map((x) => `0x${x.toString(16).toUpperCase()}`), + fullInstruction: instruction.map((x) => `0x${x.toString(16).padStart(2, '0').toUpperCase()}`) + }) + + try { + const result = await this.submitLockInstruction(deviceId, instruction) + this.logLockOperation('开锁操作', '开锁指令发送成功', 'success', { + instruction: instruction.map((x) => `0x${x.toString(16).padStart(2, '0').toUpperCase()}`) + }) + return result + } catch (error: any) { + this.logLockOperation('开锁操作', `开锁指令发送失败: ${error.message}`, 'error', { error }) + throw error } - - /** - * 获取锁ID - * @param deviceId 设备ID - * @returns Promise - */ - async getLockId(deviceId: string): Promise { - this.logLockOperation('获取锁ID', `发送锁ID查询指令到设备: ${deviceId}`, 'info') - const data = [0xff, 0xff, 0xff, 0xff] - const instruction = this.createLockInstruct(0x11, data) - try { - const result = await this.submitLockInstruction(deviceId, instruction) - this.logLockOperation('获取锁ID', '锁ID查询指令发送成功', 'success', { instruction: instruction.map(x => x.toString(16)) }) - return result - } catch (error: any) { - this.logLockOperation('获取锁ID', `锁ID查询指令发送失败: ${error.message}`, 'error', { error }) - throw error - } + } + + /** + * 获取锁状态 + * @param deviceId 设备ID + * @returns Promise + */ + async getLockStatus(deviceId: string): Promise { + this.logLockOperation('获取状态', `发送状态查询指令到设备: ${deviceId}`, 'info') + const data: number[] = [] + const instruction = this.createLockInstruct(0x8, data) + try { + const result = await this.submitLockInstruction(deviceId, instruction) + this.logLockOperation('获取状态', '状态查询指令发送成功', 'success', { + instruction: instruction.map((x) => x.toString(16)) + }) + return result + } catch (error: any) { + this.logLockOperation('获取状态', `状态查询指令发送失败: ${error.message}`, 'error', { + error + }) + throw error } - - /** - * 设置锁ID - * @param deviceId 设备ID - * @param lockId 锁ID - * @returns Promise - */ - async setLockId(deviceId: string, lockId: number): Promise { - this.logLockOperation('设置锁ID', `发送锁ID设置指令到设备: ${deviceId}, 新锁ID: ${lockId}`, 'info') - const padding = [0xff, 0xff, 0xff, 0xff] - const idBytes = this.intToBytes(lockId) - const data = idBytes.concat(padding) - const instruction = this.createLockInstruct(0x12, data) - try { - const result = await this.submitLockInstruction(deviceId, instruction) - this.logLockOperation('设置锁ID', `锁ID设置指令发送成功, 目标ID: ${lockId}`, 'success', { - lockId, - instruction: instruction.map(x => x.toString(16)), - idBytes - }) - return result - } catch (error: any) { - this.logLockOperation('设置锁ID', `锁ID设置指令发送失败: ${error.message}`, 'error', { lockId, error }) - throw error - } + } + + /** + * 发送心跳 + * @param deviceId 设备ID + * @returns Promise + */ + async sendHeartbeat(deviceId: string): Promise { + const data: number[] = [] + const instruction = this.createLockInstruct(0xff, data) + + // 心跳发送不记录日志,避免刷屏 + try { + const result = await this.submitLockInstruction(deviceId, instruction) + return result + } catch (error: any) { + // 心跳失败也不记录日志,保持安静 + throw error } - - /** - * 发送开锁密钥(电子钥匙) - * @param deviceId 设备ID - * @returns Promise - */ - async sendOpenKey(deviceId: string): Promise { - this.logLockOperation('开锁密钥', `发送开锁密钥到设备: ${deviceId}`, 'info') - - // 检查连接状态 - if (!this.isDeviceConnected(deviceId)) { - throw new Error('设备未连接,无法发送开锁密钥指令') - } - - // 根据官方SDK文档:开电子钥匙锁芯,返回码10 - const data = [0x32, 0x00, 0xFF, 0xFF, 0xFF, 0xFF] - const instruction = this.createLockInstruct(0x10, data) - - this.logLockOperation('开锁密钥', '构建开锁密钥指令', 'info', { - command: '0x10', - data: data.map(x => `0x${x.toString(16).toUpperCase()}`), - fullInstruction: instruction.map(x => `0x${x.toString(16).padStart(2, '0').toUpperCase()}`) - }) - + } + + /** + * 启动心跳机制 + * @param deviceId 设备ID + * @param interval 心跳间隔,默认2000ms(仿照微信小程序) + */ + startHeartbeat(deviceId: string, interval: number = 2000): void { + // 清理现有的心跳 + this.stopHeartbeat() + + this.currentHeartbeatDeviceId = deviceId + // console.log(`[蓝牙调试] 启动心跳机制,设备: ${deviceId}, 间隔: ${interval}ms`) + + // 按照微信小程序的逻辑,心跳持续运行,不因其他指令而停止 + this.heartbeatInterval = setInterval(async () => { + if (this.currentHeartbeatDeviceId && this.isDeviceConnected(this.currentHeartbeatDeviceId)) { try { - // 发送前详细验证连接状态 - console.log(`[蓝牙调试] 发送开锁密钥前验证连接状态...`) - const connectionBefore = await this.validateDeviceConnection(deviceId) - console.log(`[蓝牙调试] 发送前连接验证结果: ${connectionBefore}`) - - if (!connectionBefore) { - throw new Error('设备连接不稳定,无法发送开锁密钥指令') - } - - const result = await this.submitLockInstruction(deviceId, instruction) - - // 发送后延迟检查连接状态(给设备一点时间响应) - await new Promise(resolve => setTimeout(resolve, 500)) - const connectionAfter = await this.validateDeviceConnection(deviceId) - console.log(`[蓝牙调试] 发送开锁密钥后连接验证结果: ${connectionAfter}`) - - if (!connectionAfter) { - console.warn(`[蓝牙调试] 警告:发送开锁密钥后设备连接已断开`) - this.logLockOperation('开锁密钥', '指令发送成功但设备连接已断开', 'warn') - } else { - this.logLockOperation('开锁密钥', '开锁密钥指令发送成功,连接保持正常', 'success', { - instruction: instruction.map(x => `0x${x.toString(16).padStart(2, '0').toUpperCase()}`) - }) - } - - return result + // 静默发送心跳,不记录日志 + await this.sendHeartbeat(this.currentHeartbeatDeviceId) } catch (error: any) { - console.error(`[蓝牙调试] 开锁密钥发送过程中出错:`, error) - - // 检查是否是连接相关的错误 - if (error.message && error.message.includes('断开') || - error.errMsg && (error.errMsg.includes('10008') || error.errMsg.includes('10009'))) { - this.logLockOperation('开锁密钥', `连接中断导致发送失败: ${error.message}`, 'error', { - error, - connectionStatus: this.isDeviceConnected(deviceId) - }) - } else { - this.logLockOperation('开锁密钥', `开锁密钥指令发送失败: ${error.message}`, 'error', { error }) - } - throw error + // 心跳失败不记录日志,保持安静 + // 心跳失败可能表示连接有问题,但按微信小程序逻辑不立即停止 } + } else { + // console.log(`[蓝牙调试] 设备已断开,停止心跳`) + this.stopHeartbeat() + } + }, interval) + } + + /** + * 停止心跳机制 + */ + stopHeartbeat(): void { + if (this.heartbeatInterval) { + clearInterval(this.heartbeatInterval) + this.heartbeatInterval = null + // console.log(`[蓝牙调试] 心跳机制已停止`) } - - /** - * 设置任务权限 - * @param deviceId 设备ID - * @param taskId 任务ID - * @param permission 权限值 - * @returns Promise - */ - async setTaskPermission(deviceId: string, taskId: number, permission: number): Promise { - const padding = [0xff, 0xff, 0xff, 0xff] - const idBytes = this.intToBytes(taskId) - const data = idBytes.concat(padding) - data.push(permission) - const instruction = this.createLockInstruct(0x23, data) - return this.submitLockInstruction(deviceId, instruction) + this.currentHeartbeatDeviceId = null + } + + /** + * 设置蓝牙名称 + * @param deviceId 设备ID + * @param name 蓝牙名称 + * @returns Promise + */ + async setLockBluetoothName(deviceId: string, name: string): Promise { + const nameData = this.stringToBytes(name) + const instruction = this.createLockInstruct(0x5, nameData) + return this.submitLockInstruction(deviceId, instruction) + } + + /** + * 获取锁ID + * @param deviceId 设备ID + * @returns Promise + */ + async getLockId(deviceId: string): Promise { + this.logLockOperation('获取锁ID', `发送锁ID查询指令到设备: ${deviceId}`, 'info') + const data = [0xff, 0xff, 0xff, 0xff] + const instruction = this.createLockInstruct(0x11, data) + try { + const result = await this.submitLockInstruction(deviceId, instruction) + this.logLockOperation('获取锁ID', '锁ID查询指令发送成功', 'success', { + instruction: instruction.map((x) => x.toString(16)) + }) + return result + } catch (error: any) { + this.logLockOperation('获取锁ID', `锁ID查询指令发送失败: ${error.message}`, 'error', { + error + }) + throw error } - - /** - * 获取当前时间 - * @param deviceId 设备ID - * @returns Promise - */ - async getCurrentTime(deviceId: string): Promise { - const data: number[] = [] - const instruction = this.createLockInstruct(0x0a, data) - return this.submitLockInstruction(deviceId, instruction) + } + + /** + * 设置锁ID + * @param deviceId 设备ID + * @param lockId 锁ID + * @returns Promise + */ + async setLockId(deviceId: string, lockId: number): Promise { + this.logLockOperation( + '设置锁ID', + `发送锁ID设置指令到设备: ${deviceId}, 新锁ID: ${lockId}`, + 'info' + ) + const padding = [0xff, 0xff, 0xff, 0xff] + const idBytes = this.intToBytes(lockId) + const data = idBytes.concat(padding) + const instruction = this.createLockInstruct(0x12, data) + try { + const result = await this.submitLockInstruction(deviceId, instruction) + this.logLockOperation('设置锁ID', `锁ID设置指令发送成功, 目标ID: ${lockId}`, 'success', { + lockId, + instruction: instruction.map((x) => x.toString(16)), + idBytes + }) + return result + } catch (error: any) { + this.logLockOperation('设置锁ID', `锁ID设置指令发送失败: ${error.message}`, 'error', { + lockId, + error + }) + throw error } - - /** - * 设置任务时间 - * @param deviceId 设备ID - * @param timeHex 时间十六进制数组 - * @param duration 持续时间 - * @returns Promise - */ - async setTaskTime(deviceId: string, timeHex: string[], duration: number): Promise { - const timeData = this.hexToArray(timeHex) - const durationBytes = this.intToByte4R(duration) - const data = timeData.concat(durationBytes).concat([0xff]) - const instruction = this.createLockInstruct(0x22, data) - return this.submitLockInstruction(deviceId, instruction) + } + + /** + * 发送开锁密钥(电子钥匙) + * @param deviceId 设备ID + * @returns Promise + */ + async sendOpenKey(deviceId: string): Promise { + this.logLockOperation('开锁密钥', `发送开锁密钥到设备: ${deviceId}`, 'info') + + // 检查连接状态 + if (!this.isDeviceConnected(deviceId)) { + throw new Error('设备未连接,无法发送开锁密钥指令') } - /** - * 开始任务时间 - * @param deviceId 设备ID - * @returns Promise - */ - async startTaskTime(deviceId: string): Promise { - // 先获取当前时间,然后设置任务时间 - await this.getCurrentTime(deviceId) - // 注意:这里需要等待getCurrentTime的响应,然后调用setTaskTime - // 由于异步特性,实际的时间设置逻辑需要在回调中处理 - return Promise.resolve() + // 根据官方SDK文档:开电子钥匙锁芯,返回码10 + const data = [0x32, 0x00, 0xff, 0xff, 0xff, 0xff] + const instruction = this.createLockInstruct(0x10, data) + + this.logLockOperation('开锁密钥', '构建开锁密钥指令', 'info', { + command: '0x10', + data: data.map((x) => `0x${x.toString(16).toUpperCase()}`), + fullInstruction: instruction.map((x) => `0x${x.toString(16).padStart(2, '0').toUpperCase()}`) + }) + + try { + // 发送前详细验证连接状态 + console.log(`[蓝牙调试] 发送开锁密钥前验证连接状态...`) + const connectionBefore = await this.validateDeviceConnection(deviceId) + console.log(`[蓝牙调试] 发送前连接验证结果: ${connectionBefore}`) + + if (!connectionBefore) { + throw new Error('设备连接不稳定,无法发送开锁密钥指令') + } + + const result = await this.submitLockInstruction(deviceId, instruction) + + // 发送后延迟检查连接状态(给设备一点时间响应) + await new Promise((resolve) => setTimeout(resolve, 500)) + const connectionAfter = await this.validateDeviceConnection(deviceId) + console.log(`[蓝牙调试] 发送开锁密钥后连接验证结果: ${connectionAfter}`) + + if (!connectionAfter) { + console.warn(`[蓝牙调试] 警告:发送开锁密钥后设备连接已断开`) + this.logLockOperation('开锁密钥', '指令发送成功但设备连接已断开', 'warn') + } else { + this.logLockOperation('开锁密钥', '开锁密钥指令发送成功,连接保持正常', 'success', { + instruction: instruction.map((x) => `0x${x.toString(16).padStart(2, '0').toUpperCase()}`) + }) + } + + return result + } catch (error: any) { + console.error(`[蓝牙调试] 开锁密钥发送过程中出错:`, error) + + // 检查是否是连接相关的错误 + if ( + (error.message && error.message.includes('断开')) || + (error.errMsg && (error.errMsg.includes('10008') || error.errMsg.includes('10009'))) + ) { + this.logLockOperation('开锁密钥', `连接中断导致发送失败: ${error.message}`, 'error', { + error, + connectionStatus: this.isDeviceConnected(deviceId) + }) + } else { + this.logLockOperation('开锁密钥', `开锁密钥指令发送失败: ${error.message}`, 'error', { + error + }) + } + throw error } - - /** - * 清除任务权限 - * @param deviceId 设备ID - * @returns Promise - */ - async clearTaskPermission(deviceId: string): Promise { - const data: number[] = [] - const instruction = this.createLockInstruct(0x2a, data) - return this.submitLockInstruction(deviceId, instruction) + } + + /** + * 设置任务权限 + * @param deviceId 设备ID + * @param taskId 任务ID + * @param permission 权限值 + * @returns Promise + */ + async setTaskPermission(deviceId: string, taskId: number, permission: number): Promise { + const padding = [0xff, 0xff, 0xff, 0xff] + const idBytes = this.intToBytes(taskId) + const data = idBytes.concat(padding) + data.push(permission) + const instruction = this.createLockInstruct(0x23, data) + return this.submitLockInstruction(deviceId, instruction) + } + + /** + * 获取当前时间 + * @param deviceId 设备ID + * @returns Promise + */ + async getCurrentTime(deviceId: string): Promise { + const data: number[] = [] + const instruction = this.createLockInstruct(0x0a, data) + return this.submitLockInstruction(deviceId, instruction) + } + + /** + * 设置任务时间 + * @param deviceId 设备ID + * @param timeHex 时间十六进制数组 + * @param duration 持续时间 + * @returns Promise + */ + async setTaskTime(deviceId: string, timeHex: string[], duration: number): Promise { + const timeData = this.hexToArray(timeHex) + const durationBytes = this.intToByte4R(duration) + const data = timeData.concat(durationBytes).concat([0xff]) + const instruction = this.createLockInstruct(0x22, data) + return this.submitLockInstruction(deviceId, instruction) + } + + /** + * 开始任务时间 + * @param deviceId 设备ID + * @returns Promise + */ + async startTaskTime(deviceId: string): Promise { + // 先获取当前时间,然后设置任务时间 + await this.getCurrentTime(deviceId) + // 注意:这里需要等待getCurrentTime的响应,然后调用setTaskTime + // 由于异步特性,实际的时间设置逻辑需要在回调中处理 + return Promise.resolve() + } + + /** + * 清除任务权限 + * @param deviceId 设备ID + * @returns Promise + */ + async clearTaskPermission(deviceId: string): Promise { + const data: number[] = [] + const instruction = this.createLockInstruct(0x2a, data) + return this.submitLockInstruction(deviceId, instruction) + } + + /** + * 验证设备连接状态(用于指令发送前后的检查) + * @param deviceId 设备ID + * @returns Promise + */ + async validateDeviceConnection(deviceId: string): Promise { + try { + // 1. 检查内部连接状态 + const internalConnected = this.isDeviceConnected(deviceId) + console.log(`[蓝牙调试] 内部连接状态: ${internalConnected}`) + + // 2. 通过系统API获取连接状态 + const connectedDevices = await this.getConnectedDevices() + const systemConnected = connectedDevices.some((device) => device.deviceId === deviceId) + console.log(`[蓝牙调试] 系统连接状态: ${systemConnected}`) + + // 3. 尝试读取特征值来验证连接(如果支持) + try { + const characteristics = await this.getCharacteristics(deviceId, this.lockServiceId) + const hasValidCharacteristics = characteristics && characteristics.length > 0 + console.log(`[蓝牙调试] 特征值可访问性: ${hasValidCharacteristics}`) + + return internalConnected && systemConnected && hasValidCharacteristics + } catch (charError) { + console.warn(`[蓝牙调试] 无法访问特征值,连接可能不稳定:`, charError) + return false + } + } catch (error) { + console.error(`[蓝牙调试] 连接验证失败:`, error) + return false } - - /** - * 验证设备连接状态(用于指令发送前后的检查) - * @param deviceId 设备ID - * @returns Promise - */ - async validateDeviceConnection(deviceId: string): Promise { - try { - // 1. 检查内部连接状态 - const internalConnected = this.isDeviceConnected(deviceId) - console.log(`[蓝牙调试] 内部连接状态: ${internalConnected}`) - - // 2. 通过系统API获取连接状态 - const connectedDevices = await this.getConnectedDevices() - const systemConnected = connectedDevices.some(device => device.deviceId === deviceId) - console.log(`[蓝牙调试] 系统连接状态: ${systemConnected}`) - - // 3. 尝试读取特征值来验证连接(如果支持) - try { - const characteristics = await this.getCharacteristics(deviceId, this.lockServiceId) - const hasValidCharacteristics = characteristics && characteristics.length > 0 - console.log(`[蓝牙调试] 特征值可访问性: ${hasValidCharacteristics}`) - - return internalConnected && systemConnected && hasValidCharacteristics - } catch (charError) { - console.warn(`[蓝牙调试] 无法访问特征值,连接可能不稳定:`, charError) - return false - } - - } catch (error) { - console.error(`[蓝牙调试] 连接验证失败:`, error) - return false - } + } + + /** + * 验证蓝牙特征值配置 + * @param deviceId 设备ID + * @returns Promise + */ + async validateLockCharacteristics(deviceId: string): Promise { + try { + console.log(`[蓝牙调试] 开始验证设备特征值配置...`) + + const characteristics = await this.getCharacteristics(deviceId, this.lockServiceId) + console.log( + `[蓝牙调试] 发现特征值:`, + characteristics.map((c) => ({ + uuid: c.uuid, + properties: c.properties + })) + ) + + // 检查写特征值 + const writeChar = characteristics.find( + (c) => c.uuid.toUpperCase() === this.lockWriteCharacteristicId.toUpperCase() + ) + + // 检查读特征值 + const readChar = characteristics.find( + (c) => c.uuid.toUpperCase() === this.lockReadCharacteristicId.toUpperCase() + ) + + console.log( + `[蓝牙调试] 写特征值检查:`, + writeChar + ? `找到 ${writeChar.uuid} (write: ${writeChar.properties.write}, writeNoResponse: ${writeChar.properties.writeNoResponse || false})` + : '未找到' + ) + + console.log( + `[蓝牙调试] 读特征值检查:`, + readChar ? `找到 ${readChar.uuid} (notify: ${readChar.properties.notify})` : '未找到' + ) + + return !!( + writeChar && + readChar && + (writeChar.properties.write || writeChar.properties.writeNoResponse || false) && + readChar.properties.notify + ) + } catch (error) { + console.error(`[蓝牙调试] 特征值验证失败:`, error) + return false } - - /** - * 验证蓝牙特征值配置 - * @param deviceId 设备ID - * @returns Promise - */ - async validateLockCharacteristics(deviceId: string): Promise { - try { - console.log(`[蓝牙调试] 开始验证设备特征值配置...`); - - const characteristics = await this.getCharacteristics(deviceId, this.lockServiceId); - console.log(`[蓝牙调试] 发现特征值:`, characteristics.map(c => ({ - uuid: c.uuid, - properties: c.properties - }))); - - // 检查写特征值 - const writeChar = characteristics.find(c => - c.uuid.toUpperCase() === this.lockWriteCharacteristicId.toUpperCase() - ); - - // 检查读特征值 - const readChar = characteristics.find(c => - c.uuid.toUpperCase() === this.lockReadCharacteristicId.toUpperCase() - ); - - console.log(`[蓝牙调试] 写特征值检查:`, writeChar ? - `找到 ${writeChar.uuid} (write: ${writeChar.properties.write}, writeNoResponse: ${writeChar.properties.writeNoResponse || false})` : - '未找到' - ); - - console.log(`[蓝牙调试] 读特征值检查:`, readChar ? - `找到 ${readChar.uuid} (notify: ${readChar.properties.notify})` : - '未找到' - ); - - return !!(writeChar && readChar && - (writeChar.properties.write || (writeChar.properties.writeNoResponse || false)) && - readChar.properties.notify); - - } catch (error) { - console.error(`[蓝牙调试] 特征值验证失败:`, error); - return false; - } + } + + /** + * 完整的锁连接和初始化流程 + * @param deviceName 设备名称或设备ID + * @param callback 数据回调 + * @returns Promise + */ + async connectAndInitLock(deviceName: string, callback?: (data: any) => void): Promise { + let deviceId: string + + // 如果是设备名称,先查找设备 + if (!deviceName.includes(':') && !deviceName.includes('-')) { + const device = await this.findDeviceByName(deviceName) + if (!device) { + throw new Error(`未找到设备: ${deviceName}`) + } + deviceId = device.deviceId + } else { + deviceId = deviceName } - /** - * 完整的锁连接和初始化流程 - * @param deviceName 设备名称或设备ID - * @param callback 数据回调 - * @returns Promise - */ - async connectAndInitLock(deviceName: string, callback?: (data: any) => void): Promise { - let deviceId: string - - // 如果是设备名称,先查找设备 - if (!deviceName.includes(':') && !deviceName.includes('-')) { - const device = await this.findDeviceByName(deviceName) - if (!device) { - throw new Error(`未找到设备: ${deviceName}`) - } - deviceId = device.deviceId - } else { - deviceId = deviceName - } + // 连接设备 + await this.connect(deviceId) - // 连接设备 - await this.connect(deviceId) + // 初始化锁通信 + await this.initLockCommunication(deviceId, callback) - // 初始化锁通信 - await this.initLockCommunication(deviceId, callback) - - return deviceId - } + return deviceId + } } - // 创建蓝牙实例 const bluetooth = new wwBluetooth() // 扩展ww对象,添加蓝牙功能 const wwWithBluetooth = { - ...ww, - bluetooth, - - // 直接在ww上添加蓝牙方法以保持向后兼容 - getBluetoothAdapterState: bluetooth.getState.bind(bluetooth), - openBluetoothAdapter: bluetooth.checkSupport.bind(bluetooth), - startBluetoothDevicesDiscovery: bluetooth.discover.bind(bluetooth), - stopBluetoothDevicesDiscovery: bluetooth.stopDiscovery.bind(bluetooth), - getBluetoothDevices: bluetooth.getDiscoveredDevices.bind(bluetooth), - getConnectedBluetoothDevices: bluetooth.getConnectedDevices.bind(bluetooth), - createBLEConnection: bluetooth.connect.bind(bluetooth), - closeBLEConnection: bluetooth.disconnect.bind(bluetooth), - - // 新增的蓝牙方法 - getBLEDeviceServices: bluetooth.getServices.bind(bluetooth), - getBLEDeviceCharacteristics: bluetooth.getCharacteristics.bind(bluetooth), - readBLECharacteristicValue: bluetooth.readCharacteristic.bind(bluetooth), - writeBLECharacteristicValue: bluetooth.writeCharacteristic.bind(bluetooth), - notifyBLECharacteristicValueChange: bluetooth.notifyCharacteristic.bind(bluetooth), - onBLECharacteristicValueChange: bluetooth.onCharacteristicValueChange.bind(bluetooth), - onBLEConnectionStateChange: bluetooth.onConnectionStateChange.bind(bluetooth), - - // 锁操作快捷方法 - initLockCommunication: bluetooth.initLockCommunication.bind(bluetooth), - openLock: bluetooth.openLock.bind(bluetooth), - getLockStatus: bluetooth.getLockStatus.bind(bluetooth), - sendHeartbeat: bluetooth.sendHeartbeat.bind(bluetooth), - startHeartbeat: bluetooth.startHeartbeat.bind(bluetooth), - stopHeartbeat: bluetooth.stopHeartbeat.bind(bluetooth), - setLockBluetoothName: bluetooth.setLockBluetoothName.bind(bluetooth), - getLockId: bluetooth.getLockId.bind(bluetooth), - setLockId: bluetooth.setLockId.bind(bluetooth), - sendOpenKey: bluetooth.sendOpenKey.bind(bluetooth), - getCurrentTime: bluetooth.getCurrentTime.bind(bluetooth), - setTaskTime: bluetooth.setTaskTime.bind(bluetooth), - startTaskTime: bluetooth.startTaskTime.bind(bluetooth), - setTaskPermission: bluetooth.setTaskPermission.bind(bluetooth), - clearTaskPermission: bluetooth.clearTaskPermission.bind(bluetooth), - connectAndInitLock: bluetooth.connectAndInitLock.bind(bluetooth), - - // 权限管理方法 - requestPermissions: bluetooth.requestPermissions.bind(bluetooth), - checkPermissions: bluetooth.checkPermissions.bind(bluetooth), - openAppSetting: bluetooth.openAppSetting.bind(bluetooth), - ensurePermissions: bluetooth.ensurePermissions.bind(bluetooth), - - // 暴露心跳状态 - get currentHeartbeatDeviceId() { - return bluetooth.currentHeartbeatDeviceId - } + ...ww, + bluetooth, + + // 直接在ww上添加蓝牙方法以保持向后兼容 + getBluetoothAdapterState: bluetooth.getState.bind(bluetooth), + openBluetoothAdapter: bluetooth.checkSupport.bind(bluetooth), + startBluetoothDevicesDiscovery: bluetooth.discover.bind(bluetooth), + stopBluetoothDevicesDiscovery: bluetooth.stopDiscovery.bind(bluetooth), + getBluetoothDevices: bluetooth.getDiscoveredDevices.bind(bluetooth), + getConnectedBluetoothDevices: bluetooth.getConnectedDevices.bind(bluetooth), + createBLEConnection: bluetooth.connect.bind(bluetooth), + closeBLEConnection: bluetooth.disconnect.bind(bluetooth), + + // 新增的蓝牙方法 + getBLEDeviceServices: bluetooth.getServices.bind(bluetooth), + getBLEDeviceCharacteristics: bluetooth.getCharacteristics.bind(bluetooth), + readBLECharacteristicValue: bluetooth.readCharacteristic.bind(bluetooth), + writeBLECharacteristicValue: bluetooth.writeCharacteristic.bind(bluetooth), + notifyBLECharacteristicValueChange: bluetooth.notifyCharacteristic.bind(bluetooth), + onBLECharacteristicValueChange: bluetooth.onCharacteristicValueChange.bind(bluetooth), + onBLEConnectionStateChange: bluetooth.onConnectionStateChange.bind(bluetooth), + + // 锁操作快捷方法 + initLockCommunication: bluetooth.initLockCommunication.bind(bluetooth), + openLock: bluetooth.openLock.bind(bluetooth), + getLockStatus: bluetooth.getLockStatus.bind(bluetooth), + sendHeartbeat: bluetooth.sendHeartbeat.bind(bluetooth), + startHeartbeat: bluetooth.startHeartbeat.bind(bluetooth), + stopHeartbeat: bluetooth.stopHeartbeat.bind(bluetooth), + setLockBluetoothName: bluetooth.setLockBluetoothName.bind(bluetooth), + getLockId: bluetooth.getLockId.bind(bluetooth), + setLockId: bluetooth.setLockId.bind(bluetooth), + sendOpenKey: bluetooth.sendOpenKey.bind(bluetooth), + getCurrentTime: bluetooth.getCurrentTime.bind(bluetooth), + setTaskTime: bluetooth.setTaskTime.bind(bluetooth), + startTaskTime: bluetooth.startTaskTime.bind(bluetooth), + setTaskPermission: bluetooth.setTaskPermission.bind(bluetooth), + clearTaskPermission: bluetooth.clearTaskPermission.bind(bluetooth), + connectAndInitLock: bluetooth.connectAndInitLock.bind(bluetooth), + + // 权限管理方法 + requestPermissions: bluetooth.requestPermissions.bind(bluetooth), + checkPermissions: bluetooth.checkPermissions.bind(bluetooth), + openAppSetting: bluetooth.openAppSetting.bind(bluetooth), + ensurePermissions: bluetooth.ensurePermissions.bind(bluetooth), + + // 暴露心跳状态 + get currentHeartbeatDeviceId() { + return bluetooth.currentHeartbeatDeviceId + } } export default wwWithBluetooth export { bluetooth } diff --git a/web/src/views/Login/Login.vue b/web/src/views/Login/Login.vue index 3117ba8..237b4ef 100644 --- a/web/src/views/Login/Login.vue +++ b/web/src/views/Login/Login.vue @@ -1,9 +1,13 @@ - + + :class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px lt-xl:hidden overflow-x-hidden overflow-y-auto`" + > @@ -11,7 +15,11 @@ - + {{ t('login.welcome') }} @@ -21,10 +29,13 @@ + class="relative flex-1 p-30px dark:bg-[var(--login-bg-color)] lt-sm:p-10px overflow-x-hidden overflow-y-auto" + > - + {{ underlineToHump(appStore.getTitle) }} @@ -37,7 +48,8 @@ + class="m-auto h-[calc(100%-60px)] w-[100%] flex items-center at-2xl:max-w-500px at-lg:max-w-500px at-md:max-w-500px at-xl:max-w-500px" + > @@ -64,7 +76,14 @@ import { useAppStore } from '@/store/modules/app' import { ThemeSwitch } from '@/layout/components/ThemeSwitch' import { LocaleDropdown } from '@/layout/components/LocaleDropdown' -import { LoginForm, MobileForm, QrCodeForm, RegisterForm, SSOLoginVue, ForgetPasswordForm } from './components' +import { + LoginForm, + MobileForm, + QrCodeForm, + RegisterForm, + SSOLoginVue, + ForgetPasswordForm +} from './components' defineOptions({ name: 'Login' }) diff --git a/web/src/views/Login/components/LoginForm.vue b/web/src/views/Login/components/LoginForm.vue index 2e01df8..bd8f70a 100644 --- a/web/src/views/Login/components/LoginForm.vue +++ b/web/src/views/Login/components/LoginForm.vue @@ -48,10 +48,7 @@ /> - + @@ -119,7 +116,7 @@ - + diff --git a/web/src/views/lock/grouplock/components/PCGroupLock.vue b/web/src/views/lock/grouplock/components/PCGroupLock.vue index a09450d..8ad9e0a 100644 --- a/web/src/views/lock/grouplock/components/PCGroupLock.vue +++ b/web/src/views/lock/grouplock/components/PCGroupLock.vue @@ -2,8 +2,19 @@ - - + + 刷新 @@ -20,8 +31,15 @@ 任务: {{ isolationPlan.length }} - + @@ -36,15 +54,18 @@ - + - + - {{users.find((user) => user.id === scope.row.operatorId)?.nickname}} + {{ users.find((user) => user.id === scope.row.operatorId)?.nickname }} {{ getLifeLockStatus(scope.row.lifeLock, 1) }} @@ -52,7 +73,7 @@ - {{users.find((user) => user.id === scope.row.operatorHelperId)?.nickname}} + {{ users.find((user) => user.id === scope.row.operatorHelperId)?.nickname }} {{ getLifeLockStatus(scope.row.lifeLock, 2) }} - @@ -61,7 +82,7 @@ - {{users.find((user) => user.id === scope.row.verifierId)?.nickname}} + {{ users.find((user) => user.id === scope.row.verifierId)?.nickname }} {{ getLifeLockStatus(scope.row.lifeLock, 3) }} @@ -69,7 +90,7 @@ - {{users.find((user) => user.id === scope.row.verifierHelperId)?.nickname}} + {{ users.find((user) => user.id === scope.row.verifierHelperId)?.nickname }} {{ getLifeLockStatus(scope.row.lifeLock, 4) }} - @@ -80,8 +101,13 @@ - - {{users.find((u) => u.id === aff.userId)?.nickname}} + + {{ users.find((u) => u.id === aff.userId)?.nickname }} @@ -91,7 +117,7 @@ - - + @@ -99,28 +125,48 @@ - + - + 绑定 - + 上锁 - + 验证 - + 解锁验证 - + 解锁 @@ -275,7 +321,10 @@ const getInviteCode = (item) => { diff --git a/web/src/views/lock/grouplock/components/PlanCard.vue b/web/src/views/lock/grouplock/components/PlanCard.vue index c86e14f..82a6e22 100644 --- a/web/src/views/lock/grouplock/components/PlanCard.vue +++ b/web/src/views/lock/grouplock/components/PlanCard.vue @@ -17,12 +17,16 @@ - - 责任人员 + 责任人员 - + {{ role.label }}: {{ getUserName(isolationPlan[0], role.key) }} @@ -35,18 +39,26 @@ - - 隔离点详情 + 隔离点详情 - + {{ getPointName(pointId) }} - + - + @@ -58,66 +70,97 @@ - - {{ getLockName(item.lockId) }} + {{ getLockName(item.lockId) }} - + - - 受影响人员 + 受影响人员 - + {{ users.find((u) => u.id === affected.userId)?.nickname }} - + - + - - 绑定 + 绑定 - + - - 上锁 + 上锁 - + - - 验证 + 验证 - + - - 解锁验证 + 解锁验证 - + - - 解锁 + 解锁 @@ -262,8 +305,7 @@ const isVerifier = (item) => { const getUserName = (item, key) => users.value.find((user) => user.id === item?.[key])?.nickname || '未指定' -const getPointName = (pointId) => - elLockStore.isolationPoints.find((p) => p.id == pointId)?.ipName +const getPointName = (pointId) => elLockStore.isolationPoints.find((p) => p.id == pointId)?.ipName const getLockName = (lockId) => lockId ? locks.value.find((l) => l.id === lockId)?.lockName || `锁 ${lockId}` : '待绑定' @@ -329,49 +371,50 @@ watch( diff --git a/web/src/views/lock/grouplock/grouplock.vue b/web/src/views/lock/grouplock/grouplock.vue index 2dc50d2..289445b 100644 --- a/web/src/views/lock/grouplock/grouplock.vue +++ b/web/src/views/lock/grouplock/grouplock.vue @@ -1,12 +1,13 @@ - + - - @@ -81,7 +83,6 @@ onActivated(() => { refreshData() }) - // 数据处理方法 const getItemDetailList = (isolationPlan) => { let list = [] @@ -115,6 +116,14 @@ const refreshData = async () => { } const handleLockOperation = async (item) => { + let confirm = await ElMessageBox.confirm('确定要上锁吗?', '上锁确认', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }) + if (confirm !== 'confirm') { + return + } // 检查锁具状态 let lock = await LockApi.getLock(item.lockId) if (lock.lockStatus != 7) { @@ -134,19 +143,27 @@ const handleLockOperation = async (item) => { gpsCoordinates: `${location.latitude},${location.longitude}` }).then((res) => { // 修改锁具状态 修改任务子项详情状态 修改隔离点状态 - let promiseList = [ - lockAction({ planItemDetailId: item.id, operateRecordId: res }) - ] - Promise.all(promiseList).then((res) => { - ElMessage.success('上锁成功') - refreshData() - }).catch((err) => { - ElMessage.error('上锁失败') - refreshData() - }) + let promiseList = [lockAction({ planItemDetailId: item.id, operateRecordId: res })] + Promise.all(promiseList) + .then((res) => { + ElMessage.success('上锁成功') + refreshData() + }) + .catch((err) => { + ElMessage.error('上锁失败') + refreshData() + }) }) } const handleUnlockOperation = async (detail) => { + let confirm = await ElMessageBox.confirm('确定要解锁吗?', '解锁确认', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }) + if (confirm !== 'confirm') { + return + } // 检查锁具状态 let lock = await LockApi.getLock(detail.lockId) if (lock.lockStatus != 3) { @@ -180,30 +197,51 @@ const handleUnlockOperation = async (detail) => { let promiseList = [ unLockAction({ planItemDetailId: detail.id, planId: detail.planId, lifelockId: lifelock.id }) ] - Promise.all(promiseList).then((res) => { - ElMessage.success('解锁成功') - refreshData() - }).catch((err) => { - ElMessage.error('解锁失败') - refreshData() - }) + Promise.all(promiseList) + .then((res) => { + ElMessage.success('解锁成功') + refreshData() + }) + .catch((err) => { + ElMessage.error('解锁失败') + refreshData() + }) }) } -const handleBindLock = (item) => { - bindLock(item).then(() => { - ElMessage.success('绑定成功') - refreshData() - }).catch((err) => { - ElMessage.error('绑定失败') - refreshData() +const handleBindLock = async (item) => { + let confirm = await ElMessageBox.confirm('确定要绑定吗?', '绑定确认', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' }) + if (confirm !== 'confirm') { + return + } + return bindLock(item) + .then(() => { + // ElMessage.success('绑定成功') + refreshData() + }) + .catch((err) => { + ElMessage.error('绑定失败') + refreshData() + }) } const handleVerifyLock = async (item) => { // 获取地理位置 获取上锁照片 + let confirm = await ElMessageBox.confirm('确定要验证上锁吗?', '验证确认', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }) + if (confirm !== 'confirm') { + return + } + let photo try { let location = await getCurrentLocation() - let photo = await getPhoto() + photo = await getPhoto() } catch (err) { ElMessage.error('获取照片失败,无法验证') refreshData() @@ -218,59 +256,63 @@ const handleVerifyLock = async (item) => { gpsCoordinates: `${location.latitude},${location.longitude}` }).then((res) => { // 修改修改任务子项详情状态 发送已验证通知 - let promiseList = [ - verifyLockAction({ planItemDetailId: item.id, verifyRecordId: res }) - ] - Promise.all(promiseList).then((res) => { - ElMessage.success('验证成功') - refreshData() - }).catch((err) => { - ElMessage.error('验证失败') - refreshData() - }) + let promiseList = [verifyLockAction({ planItemDetailId: item.id, verifyRecordId: res })] + Promise.all(promiseList) + .then((res) => { + ElMessage.success('验证成功') + refreshData() + }) + .catch((err) => { + ElMessage.error('验证失败') + refreshData() + }) }) } const handleUnlockVerify = async (item) => { - // 获取地理位置 - ElMessageBox.confirm('确定要验证解锁吗?', '解锁确认', { + let confirm = await ElMessageBox.confirm('确定要验证解锁吗?', '解锁确认', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' - }).then(async (res) => { - if (res === 'confirm') { - let location = await getCurrentLocation() - const lifelock = elLockStore.planLifeLocks.find( - (lifelock) => - lifelock.isolationPlanItemDetailId == item.id && - lifelock.userId == currentUserId.value && - lifelock.lockStatus == 1 && - lifelock.lockType == 3 - ) - if (!lifelock) { - ElMessage.error('未找到生命锁') + }) + if (confirm !== 'confirm') { + return + } + // 获取地理位置 + + let location = await getCurrentLocation() + const lifelock = elLockStore.planLifeLocks.find( + (lifelock) => + lifelock.isolationPlanItemDetailId == item.id && + lifelock.userId == currentUserId.value && + lifelock.lockStatus == 1 && + lifelock.lockType == 3 + ) + if (!lifelock) { + ElMessage.error('未找到生命锁') + refreshData() + return + } + LockWorkRecordApi.createLockWorkRecord({ + operatorId: currentUserId.value, + lockId: item.lockId, + isolationPlanItemDetailId: item.id, + recordType: 18, //解锁验证 + gpsCoordinates: `${location.latitude},${location.longitude}` + }).then((res) => { + const promiseList = [ + verifyUnlockAction({ planItemDetailId: item.id, lifelockId: lifelock.id }) + ] + Promise.all(promiseList) + .then((res) => { + ElMessage.success('验证成功') + refreshData() + }) + .catch((err) => { + ElMessage.error('验证失败') refreshData() - return - } - LockWorkRecordApi.createLockWorkRecord({ - operatorId: currentUserId.value, - lockId: item.lockId, - isolationPlanItemDetailId: item.id, - recordType: 18, //解锁验证 - gpsCoordinates: `${location.latitude},${location.longitude}` - }).then((res) => { - const promiseList = [ - verifyUnlockAction({ planItemDetailId: item.id, lifelockId: lifelock.id }) - ] - Promise.all(promiseList).then((res) => { - ElMessage.success('验证成功') - refreshData() - }).catch((err) => { - ElMessage.error('验证失败') - refreshData() - }) }) - } }) + } // 处理锁响应数据 @@ -279,5 +321,15 @@ const handleLockResponse = (data) => { } diff --git a/web/src/views/lock/guide/lockguide/LockGuideFormNew.vue b/web/src/views/lock/guide/lockguide/LockGuideFormNew.vue index f6fa516..550770e 100644 --- a/web/src/views/lock/guide/lockguide/LockGuideFormNew.vue +++ b/web/src/views/lock/guide/lockguide/LockGuideFormNew.vue @@ -1,45 +1,120 @@ - + - + - + - + - - + + - - + + - - + + - - + + - - + + diff --git a/web/src/views/lock/guide/lockguide/index.vue b/web/src/views/lock/guide/lockguide/index.vue index b19a1de..3a7aee0 100644 --- a/web/src/views/lock/guide/lockguide/index.vue +++ b/web/src/views/lock/guide/lockguide/index.vue @@ -74,7 +74,12 @@ - + - + @@ -15,9 +21,20 @@ - - + + @@ -43,13 +60,18 @@ {{ scope.row.isolationPoint.ipName }} - + - + @@ -59,7 +81,10 @@ {{ elLockStore.users.find((user) => user.id === item.userId)?.nickname }} - + @@ -73,53 +98,112 @@ {{ getGuideName(scope.row.guideId) }} - + - - + + {{ elLockStore.users.find((user) => user.id === scope.row.operatorId)?.nickname - }} + }} - + - - + + {{ elLockStore.users.find((user) => user.id === scope.row.operatorHelperId)?.nickname - }} + }} - - + + {{ elLockStore.users.find((user) => user.id === scope.row.verifierId)?.nickname - }} + }} - + - - + + {{ elLockStore.users.find((user) => user.id === scope.row.verifierHelperId)?.nickname - }} + }} @@ -145,8 +229,15 @@ - + 任务名称 @@ -165,9 +256,20 @@ 添加检修任务指导书 - - + + @@ -178,32 +280,55 @@ - + - + {{ getGuideName(item.guideId) }} - + 删除 - + 集中挂牌人 - - + + - {{ elLockStore.users.find((user) => user.id === item.operatorId)?.nickname || '未选择' }} + {{ + elLockStore.users.find((user) => user.id === item.operatorId)?.nickname || + '未选择' + }} @@ -212,12 +337,26 @@ 集中挂牌协助人 - - + + - {{ elLockStore.users.find((user) => user.id === item.operatorHelperId)?.nickname || '未选择' }} + {{ + elLockStore.users.find((user) => user.id === item.operatorHelperId) + ?.nickname || '未选择' + }} @@ -226,12 +365,26 @@ 验证人 - - + + - {{ elLockStore.users.find((user) => user.id === item.verifierId)?.nickname || '未选择' }} + {{ + elLockStore.users.find((user) => user.id === item.verifierId)?.nickname || + '未选择' + }} @@ -240,12 +393,26 @@ 验证协助人 - - + + - {{ elLockStore.users.find((user) => user.id === item.verifierHelperId)?.nickname || '未选择' }} + {{ + elLockStore.users.find((user) => user.id === item.verifierHelperId) + ?.nickname || '未选择' + }} @@ -262,7 +429,11 @@ 隔离点 - + {{ point?.ipName + '-' + point?.ipLocation + '(' + point?.ipNumber + ')' }} @@ -277,9 +448,16 @@ - + 详细任务 - + 电子锁: @@ -299,21 +477,36 @@ 隔离点: {{ detail.isolationPoint.ipName }} - + 任务状态: - + 受影响人: - - {{ elLockStore.users.find((user) => user.id === lifeLock.userId)?.nickname }} - + + {{ + elLockStore.users.find((user) => user.id === lifeLock.userId)?.nickname + }} + @@ -326,14 +519,27 @@ - + + - 确 定 + 确 定 取 消 - 关 闭 + 关 闭 @@ -690,7 +896,7 @@ const deletePlanItem = (index: number) => { padding: 16px; background: #f8f9fa; border-radius: 8px; - + .mobile-form-title { font-size: 16px; font-weight: 600; @@ -704,7 +910,7 @@ const deletePlanItem = (index: number) => { .mobile-input, .mobile-select { width: 100%; - + :deep(.el-input__wrapper) { border-radius: 6px; } diff --git a/web/src/views/lock/isolation/point/PointForm.vue b/web/src/views/lock/isolation/point/PointForm.vue index 0062c6e..5942e4f 100644 --- a/web/src/views/lock/isolation/point/PointForm.vue +++ b/web/src/views/lock/isolation/point/PointForm.vue @@ -1,11 +1,20 @@ - + + v-for="item in getIntDictOptions(DICT_TYPE.LOCK_ISOLATION_TYPE)" + :key="item.value" + :label="item.label" + :value="item.value" + /> @@ -20,14 +29,22 @@ v-for="item in getIntDictOptions(DICT_TYPE.LOCK_ISOLATION_TYPE)" :key="item.valu + v-for="item in getIntDictOptions(DICT_TYPE.LOCK_ISOLATION_POINT_STATUS)" + :key="item.value" + :label="item.label" + :value="item.value" + /> + v-model="formData.guideLockNums" + :precision="0" + :step="1" + :min="1" + :max="1" + placeholder="请输入电子锁数量" + /> diff --git a/web/src/views/lock/lifelock/lifelock.vue b/web/src/views/lock/lifelock/lifelock.vue index b30711d..33834ce 100644 --- a/web/src/views/lock/lifelock/lifelock.vue +++ b/web/src/views/lock/lifelock/lifelock.vue @@ -47,8 +47,12 @@ - + @@ -105,13 +109,23 @@ - + @@ -230,11 +248,17 @@ - + 解锁 - + 重新上锁 @@ -273,13 +297,23 @@
请选择要绑定的锁具: