From b553c4e519884ae24dd0c9af97377c0355d22214 Mon Sep 17 00:00:00 2001 From: 12451 <12451@example.com> Date: Sat, 4 Jul 2026 04:03:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20API=20=E7=BB=9F=E4=B8=80=20+=20?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E8=AF=A6=E6=83=85=E8=A1=A5=E5=85=A8=20+=20H5?= =?UTF-8?q?=20=E5=AF=BC=E8=88=AA=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - API 路由统一: 移除 /api/h5/,统一 /api/pms/ 入口 - 二进制改名: api-server -> pms-api(与服务名一致) - nginx: 合并 /api/ + /pms/api/ 为 /api/pms/ - 新增 docs/API.md 接口文档 - 后端: CabinetDetail 增加 rssi/pow_fail/address/soc 等字段 - Redis 管道批量读取仓体实时数据(SOC/电压/电流/功率) - DB: 所有 TIMESTAMP 列改为 DATETIME(修复类型不匹配) - 用户管理: 新增创建时间/修改时间列,状态改用 Switch - 角色管理/用户管理: 操作按钮改为图标 - PC 设备详情: 基本信息补全 RSSI/ICCID/电源/地址 - PC 卡片: 通道统计改用 Lucide 图标,颜色统一 - PC 二维码: canvas 渲染,含编号文字,支持右键下载 - H5: 按 uni-app 规范修正导航(navigateTo 替代 reLaunch) - H5: 详情页加返回导航条 - H5: 扫码支持摄像头(jsQR)+ ?id= 参数 - H5: 底部 tabBar 在所有页面常显 - H5: 首页统计改为 6 项(设备/通道/空闲/充电/充满/故障) - H5: 下拉刷新 / 上拉加载 - 产品首页: 添加业务联系 - 文档: 清理过时文件,补充缺失规范章节 --- AGENTS.md | 672 ++++++---- deploy/deploy-prod.sh | 2 +- deploy/deploy-test.sh | 2 +- deploy/pms-api-test.service | 2 +- docs/1TN协议内容_完整.txt | 384 ------ docs/API.md | 351 +++++ docs/build_contract.js | 272 ---- docs/convert.js | 22 - docs/package-lock.json | 548 -------- docs/package.json | 7 - docs/toPdf.js | 26 - docs/开发文档.md | 25 +- docs/开发计划.md | 347 ----- software/api-server/Cargo.lock | 56 +- software/api-server/Cargo.toml | 2 +- software/api-server/src/db/migrate.rs | 6 +- software/api-server/src/routes/auth.rs | 11 +- software/api-server/src/routes/cabinets.rs | 105 +- .../api-server/src/routes/charge_records.rs | 28 +- .../api-server/src/routes/energy_stats.rs | 69 +- software/api-server/src/routes/h5.rs | 435 ++++--- software/api-server/src/routes/mod.rs | 20 +- .../api-server/src/routes/organizations.rs | 7 + software/api-server/src/routes/users.rs | 10 +- .../.agents/skills/pinia/GENERATION.md | 5 + .../h5-uniapp/.agents/skills/pinia/LICENSE.md | 21 + .../h5-uniapp/.agents/skills/pinia/SKILL.md | 59 + .../h5-uniapp/.agents/skills/pinia/SYNC.md | 5 + .../skills/pinia/references/advanced-hmr.md | 61 + .../skills/pinia/references/advanced-nuxt.md | 119 ++ .../skills/pinia/references/advanced-ssr.md | 121 ++ .../best-practices-outside-component.md | 115 ++ .../references/best-practices-testing.md | 212 +++ .../skills/pinia/references/core-stores.md | 389 ++++++ .../pinia/references/features-composables.md | 114 ++ .../references/features-composing-stores.md | 134 ++ .../pinia/references/features-plugins.md | 203 +++ .../.agents/skills/uni-app/GENERATION.md | 54 + .../h5-uniapp/.agents/skills/uni-app/SKILL.md | 113 ++ .../uni-app/references/config-manifest.md | 370 ++++++ .../skills/uni-app/references/config-pages.md | 270 ++++ .../references/core-form-components.md | 250 ++++ .../uni-app/references/core-framework.md | 133 ++ .../references/core-view-components.md | 202 +++ .../references/feature-file-operations.md | 391 ++++++ .../uni-app/references/feature-lifecycle.md | 423 ++++++ .../uni-app/references/feature-location.md | 417 ++++++ .../references/feature-media-components.md | 314 +++++ .../uni-app/references/feature-navigation.md | 257 ++++ .../uni-app/references/feature-network.md | 345 +++++ .../uni-app/references/feature-storage.md | 279 ++++ .../uni-app/references/feature-system-info.md | 300 +++++ .../uni-app/references/feature-ui-feedback.md | 382 ++++++ .../.agents/skills/uni-helper/GENERATION.md | 5 + .../.agents/skills/uni-helper/SKILL.md | 88 ++ .../uni-helper/references/lib-uni-network.md | 221 ++++ .../uni-helper/references/lib-uni-promises.md | 197 +++ .../uni-helper/references/lib-uni-typed.md | 200 +++ .../uni-helper/references/lib-uni-use.md | 185 +++ .../references/plugin-components.md | 93 ++ .../uni-helper/references/plugin-layouts.md | 143 +++ .../uni-helper/references/plugin-manifest.md | 103 ++ .../references/plugin-middleware.md | 181 +++ .../uni-helper/references/plugin-pages.md | 186 +++ .../references/plugin-platform-modifier.md | 119 ++ .../uni-helper/references/plugin-platform.md | 105 ++ .../references/starter-create-uni.md | 153 +++ .../uni-helper/references/starter-vitesse.md | 263 ++++ .../uni-helper/references/util-uni-env.md | 155 +++ .../references/util-unocss-preset.md | 175 +++ .../.agents/skills/unocss/GENERATION.md | 5 + .../.agents/skills/unocss/LICENSE.md | 21 + .../h5-uniapp/.agents/skills/unocss/SKILL.md | 64 + .../h5-uniapp/.agents/skills/unocss/SYNC.md | 5 + .../skills/unocss/references/core-config.md | 187 +++ .../unocss/references/core-extracting.md | 137 ++ .../skills/unocss/references/core-layers.md | 104 ++ .../skills/unocss/references/core-rules.md | 166 +++ .../skills/unocss/references/core-safelist.md | 105 ++ .../unocss/references/core-shortcuts.md | 89 ++ .../skills/unocss/references/core-theme.md | 172 +++ .../skills/unocss/references/core-variants.md | 175 +++ .../unocss/references/integrations-nuxt.md | 199 +++ .../unocss/references/integrations-vite.md | 283 ++++ .../unocss/references/preset-attributify.md | 142 ++ .../skills/unocss/references/preset-icons.md | 184 +++ .../skills/unocss/references/preset-mini.md | 158 +++ .../unocss/references/preset-rem-to-px.md | 97 ++ .../skills/unocss/references/preset-tagify.md | 134 ++ .../unocss/references/preset-typography.md | 95 ++ .../unocss/references/preset-web-fonts.md | 91 ++ .../skills/unocss/references/preset-wind3.md | 194 +++ .../skills/unocss/references/preset-wind4.md | 247 ++++ .../references/transformer-attributify-jsx.md | 156 +++ .../references/transformer-compile-class.md | 128 ++ .../references/transformer-directives.md | 157 +++ .../references/transformer-variant-group.md | 97 ++ .../.agents/skills/vite/GENERATION.md | 5 + .../h5-uniapp/.agents/skills/vite/LICENSE.md | 21 + .../h5-uniapp/.agents/skills/vite/SKILL.md | 50 + .../h5-uniapp/.agents/skills/vite/SYNC.md | 5 + .../skills/vite/references/advanced-api.md | 218 ++++ .../vite/references/advanced-backend.md | 164 +++ .../vite/references/advanced-performance.md | 168 +++ .../vite/references/advanced-plugin-api.md | 258 ++++ .../skills/vite/references/build-library.md | 172 +++ .../vite/references/build-production.md | 220 ++++ .../skills/vite/references/build-ssr.md | 194 +++ .../skills/vite/references/core-cli.md | 137 ++ .../skills/vite/references/core-config.md | 176 +++ .../skills/vite/references/core-features.md | 170 +++ .../skills/vite/references/core-plugins.md | 154 +++ .../skills/vite/references/features-assets.md | 138 ++ .../skills/vite/references/features-css.md | 215 ++++ .../vite/references/features-dep-bundling.md | 148 +++ .../skills/vite/references/features-env.md | 161 +++ .../vite/references/features-glob-import.md | 161 +++ .../skills/vite/references/features-hmr.md | 200 +++ .../vite/references/features-workers.md | 115 ++ .../skills/vue-best-practices/LICENSE.md | 21 + .../skills/vue-best-practices/SKILL.md | 313 +++++ .../.agents/skills/vue-best-practices/SYNC.md | 5 + .../animation-class-based-technique.md | 258 ++++ .../reference/animation-key-for-rerender.md | 160 +++ .../animation-state-driven-technique.md | 295 +++++ .../animation-transitiongroup-performance.md | 241 ++++ .../async-component-error-handling.md | 115 ++ .../async-component-hydration-strategies.md | 142 ++ .../async-component-keepalive-ref-issue.md | 112 ++ .../async-component-loading-delay.md | 76 ++ .../async-component-suspense-control.md | 74 ++ .../reference/async-component-vue-router.md | 109 ++ .../reference/attrs-event-listener-merging.md | 205 +++ .../attrs-hyphenated-property-access.md | 186 +++ .../reference/attrs-not-reactive.md | 162 +++ .../avoid-prop-drilling-use-provide-inject.md | 249 ++++ ...eckbox-true-false-value-form-submission.md | 118 ++ .../reference/cleanup-side-effects.md | 172 +++ .../reference/click-events-on-components.md | 180 +++ .../reference/component-events-dont-bubble.md | 252 ++++ .../reference/component-naming-conflicts.md | 159 +++ .../reference/component-naming-pascalcase.md | 114 ++ .../component-ref-requires-defineexpose.md | 176 +++ .../composable-avoid-hidden-side-effects.md | 208 +++ .../composable-call-location-restrictions.md | 141 ++ .../composable-composition-pattern.md | 236 ++++ .../composable-naming-return-pattern.md | 139 ++ .../composable-options-object-pattern.md | 209 +++ .../reference/composable-readonly-state.md | 221 ++++ .../composable-tovalue-inside-watcheffect.md | 182 +++ .../composable-vs-utility-functions.md | 193 +++ ...omposition-api-bundle-size-minification.md | 158 +++ .../composition-api-code-organization.md | 213 +++ .../composition-api-mixins-replacement.md | 208 +++ ...position-api-not-functional-programming.md | 120 ++ ...composition-api-options-api-coexistence.md | 185 +++ ...position-api-script-setup-async-context.md | 203 +++ ...position-api-vs-react-hooks-differences.md | 156 +++ .../reference/computed-array-mutation.md | 148 +++ .../computed-conditional-dependencies.md | 147 +++ .../reference/computed-no-parameters.md | 159 +++ .../reference/computed-no-side-effects.md | 107 ++ .../computed-properties-for-class-logic.md | 121 ++ .../computed-return-value-readonly.md | 160 +++ .../reference/computed-vs-methods-caching.md | 119 ++ .../reference/configure-app-before-mount.md | 89 ++ .../declare-emits-for-documentation.md | 212 +++ .../reference/define-expose-before-await.md | 192 +++ .../define-model-default-value-sync.md | 139 ++ .../defineEmits-must-be-top-level.md | 164 +++ .../defineEmits-no-runtime-and-type-mixed.md | 170 +++ .../definemodel-hidden-modifier-props.md | 134 ++ .../definemodel-object-mutation-no-emit.md | 148 +++ .../reference/definemodel-value-next-tick.md | 162 +++ .../directive-arguments-read-only.md | 180 +++ .../directive-avoid-on-components.md | 149 +++ .../directive-cleanup-in-unmounted.md | 219 ++++ .../reference/directive-function-shorthand.md | 189 +++ .../reference/directive-naming-v-prefix.md | 192 +++ ...directive-prefer-declarative-templating.md | 220 ++++ .../directive-vs-component-decision.md | 230 ++++ .../directive-vue2-migration-hooks.md | 210 +++ .../reference/dom-update-timing-nexttick.md | 90 ++ .../reference/dynamic-argument-constraints.md | 146 +++ .../dynamic-component-registration-vite.md | 147 +++ .../dynamic-components-with-keepalive.md | 233 ++++ .../reference/emit-kebab-case-in-templates.md | 166 +++ .../emit-validation-for-complex-payloads.md | 190 +++ .../reference/event-modifier-order-matters.md | 101 ++ .../event-once-modifier-for-single-use.md | 213 +++ .../exact-modifier-for-precise-shortcuts.md | 155 +++ .../fallthrough-attrs-overwrite-vue3.md | 159 +++ .../in-dom-template-parsing-caveats.md | 149 +++ ...heritattrs-false-for-wrapper-components.md | 230 ++++ .../keepalive-component-name-requirement.md | 218 ++++ .../reference/keepalive-memory-management.md | 186 +++ .../keepalive-no-cache-removal-vue3.md | 191 +++ .../keepalive-router-fresh-vs-cached.md | 226 ++++ .../keepalive-router-nested-double-mount.md | 222 ++++ .../keepalive-transition-memory-leak.md | 144 +++ .../reference/keyup-modifier-timing.md | 137 ++ .../reference/lifecycle-dom-access-timing.md | 216 ++++ ...ifecycle-hooks-synchronous-registration.md | 156 +++ .../reference/lifecycle-ssr-awareness.md | 184 +++ .../local-components-not-in-descendants.md | 151 +++ .../reference/mount-return-value.md | 88 ++ .../mouse-button-modifiers-intent.md | 134 ++ .../multi-root-component-class-attrs.md | 93 ++ .../reference/multiple-app-instances.md | 115 ++ .../native-event-collision-with-emits.md | 162 +++ .../reference/no-passive-with-prevent.md | 141 ++ .../reference/no-v-if-with-v-for.md | 136 ++ ...rf-avoid-component-abstraction-in-lists.md | 162 +++ .../perf-computed-object-stability.md | 157 +++ ...erf-props-stability-update-optimization.md | 140 ++ .../reference/perf-ssr-ssg-for-page-load.md | 170 +++ .../perf-v-once-v-memo-directives.md | 187 +++ .../reference/perf-virtualize-large-lists.md | 192 +++ .../plugin-global-properties-sparingly.md | 109 ++ .../reference/plugin-install-before-mount.md | 124 ++ ...r-provide-inject-over-global-properties.md | 120 ++ .../plugin-structure-install-method.md | 206 +++ .../reference/plugin-symbol-injection-keys.md | 165 +++ .../plugin-typescript-type-augmentation.md | 157 +++ .../prefer-local-component-registration.md | 137 ++ .../prefer-props-emit-over-component-refs.md | 216 ++++ .../reference/prefer-ref-over-reactive.md | 82 ++ .../reference/prop-boolean-casting-order.md | 104 ++ .../prop-composable-reactivity-loss.md | 158 +++ .../prop-defineprops-scope-limitation.md | 161 +++ .../prop-destructured-watch-getter.md | 138 ++ .../prop-validation-before-instance.md | 166 +++ .../reference/props-are-read-only.md | 214 ++++ .../provide-inject-debugging-challenges.md | 203 +++ .../provide-inject-default-value-factory.md | 244 ++++ .../provide-inject-mutations-in-provider.md | 190 +++ ...provide-inject-reactivity-not-automatic.md | 226 ++++ .../reference/provide-inject-symbol-keys.md | 232 ++++ .../provide-inject-synchronous-setup.md | 235 ++++ .../reference/reactive-destructuring.md | 89 ++ ...ity-computed-over-watcheffect-mutations.md | 128 ++ .../reference/reactivity-debugging-hooks.md | 132 ++ .../reactivity-external-state-integration.md | 165 +++ .../reactivity-markraw-for-non-reactive.md | 149 +++ .../reactivity-proxy-identity-hazard.md | 96 ++ .../reactivity-same-tick-batching.md | 166 +++ .../reference/ref-value-access.md | 61 + .../refs-in-collections-need-value.md | 81 ++ ...unction-avoid-internal-vnode-properties.md | 151 +++ .../render-function-custom-directives.md | 201 +++ .../render-function-event-modifiers.md | 190 +++ .../render-function-functional-components.md | 171 +++ .../render-function-v-for-keys-required.md | 160 +++ .../render-function-v-model-implementation.md | 221 ++++ .../render-function-vnodes-must-be-unique.md | 133 ++ ...g-excessive-rerenders-watch-vs-computed.md | 163 +++ ...-prefer-templates-over-render-functions.md | 125 ++ ...rendering-render-function-h-import-vue3.md | 148 +++ ...ering-render-function-return-from-setup.md | 148 +++ ...ring-render-function-slots-as-functions.md | 168 +++ ...ring-resolve-component-for-string-names.md | 231 ++++ ...ndering-understand-vdom-block-structure.md | 137 ++ .../reference/select-initial-value-ios-bug.md | 91 ++ .../self-referencing-component-name.md | 157 +++ .../reference/sfc-named-exports-forbidden.md | 184 +++ .../sfc-recommended-for-build-projects.md | 109 ++ .../sfc-scoped-css-child-component-styling.md | 156 +++ .../sfc-scoped-css-dynamic-content.md | 193 +++ .../reference/sfc-scoped-css-performance.md | 208 +++ .../reference/sfc-scoped-css-slot-content.md | 242 ++++ .../reference/sfc-script-setup-reactivity.md | 195 +++ .../sfc-separation-of-concerns-colocate.md | 225 ++++ .../reference/shallow-ref-for-performance.md | 112 ++ .../slot-conditional-rendering-with-slots.md | 138 ++ .../slot-define-slots-for-typescript.md | 171 +++ .../slot-fallback-content-default-values.md | 135 ++ .../slot-forwarding-to-child-components.md | 143 +++ .../slot-implicit-default-content.md | 155 +++ .../reference/slot-name-reserved-prop.md | 109 ++ .../slot-named-scoped-explicit-default.md | 95 ++ .../slot-render-scope-parent-only.md | 135 ++ ...ot-renderless-components-vs-composables.md | 153 +++ ...-v-slot-on-components-or-templates-only.md | 122 ++ .../ssr-custom-directive-getssrprops.md | 212 +++ .../ssr-hydration-mismatch-causes.md | 280 ++++ .../reference/ssr-platform-specific-apis.md | 256 ++++ .../state-ssr-cross-request-pollution.md | 276 ++++ .../reference/style-binding-camelcase.md | 131 ++ .../suspense-events-for-state-tracking.md | 169 +++ .../suspense-experimental-api-stability.md | 79 ++ ...spense-fallback-not-immediate-on-revert.md | 116 ++ .../suspense-nested-suspensible-prop.md | 144 +++ .../suspense-nesting-order-with-router.md | 129 ++ .../suspense-no-builtin-error-handling.md | 127 ++ .../suspense-revert-only-on-root-change.md | 139 ++ .../suspense-single-child-requirement.md | 91 ++ .../suspense-ssr-hydration-issues.md | 159 +++ .../tailwind-dynamic-class-generation.md | 144 +++ .../teleport-css-positioning-issues.md | 133 ++ .../teleport-disabled-for-responsive.md | 223 ++++ .../teleport-logical-hierarchy-preserved.md | 168 +++ .../teleport-multiple-to-same-target.md | 155 +++ .../teleport-scoped-styles-limitation.md | 191 +++ .../reference/teleport-ssr-hydration.md | 152 +++ .../reference/teleport-target-must-exist.md | 113 ++ .../template-expressions-restrictions.md | 114 ++ .../template-functions-no-side-effects.md | 187 +++ .../reference/template-ref-null-with-v-if.md | 123 ++ .../template-ref-unwrapping-top-level.md | 104 ++ .../reference/template-ref-v-for-order.md | 172 +++ .../reference/textarea-no-interpolation.md | 72 ++ .../transition-animate-transform-opacity.md | 186 +++ .../transition-group-flip-inline-elements.md | 152 +++ .../transition-group-key-requirement.md | 107 ++ ...-group-move-animation-position-absolute.md | 130 ++ ...ransition-group-no-default-wrapper-vue3.md | 152 +++ .../transition-group-no-mode-prop.md | 172 +++ .../transition-group-staggered-animations.md | 200 +++ .../transition-js-hooks-done-callback.md | 251 ++++ .../transition-key-for-same-element.md | 191 +++ .../reference/transition-mode-out-in.md | 212 +++ .../reference/transition-nested-duration.md | 182 +++ .../transition-reusable-scoped-style.md | 245 ++++ .../transition-router-view-appear.md | 193 +++ .../transition-single-element-slot.md | 118 ++ .../reference/transition-type-when-mixed.md | 172 +++ .../transition-unmount-hook-timing.md | 149 +++ .../ts-component-ref-typeof-instancetype.md | 260 ++++ .../ts-custom-directive-type-augmentation.md | 264 ++++ .../ts-defineemits-type-based-syntax.md | 191 +++ .../ts-defineprops-boolean-default-false.md | 225 ++++ ...-defineprops-imported-types-limitations.md | 281 ++++ .../ts-defineprops-type-based-declaration.md | 171 +++ .../ts-event-handler-explicit-typing.md | 213 +++ .../ts-provide-inject-injection-key.md | 257 ++++ .../ts-reactive-no-generic-argument.md | 196 +++ .../ts-shallowref-for-dynamic-components.md | 218 ++++ .../ts-template-ref-null-handling.md | 249 ++++ .../reference/ts-template-type-casting.md | 214 ++++ ...s-withdefaults-mutable-factory-function.md | 171 +++ .../undeclared-emits-double-firing.md | 195 +++ .../reference/updated-hook-performance.md | 190 +++ .../reference/use-template-ref-vue35.md | 158 +++ .../reference/v-else-must-follow-v-if.md | 136 ++ .../reference/v-for-component-props.md | 95 ++ .../reference/v-for-computed-reverse-sort.md | 86 ++ .../reference/v-for-key-attribute.md | 90 ++ .../reference/v-for-range-starts-at-one.md | 66 + .../v-for-use-computed-for-filtering.md | 99 ++ .../reference/v-html-xss-security.md | 111 ++ .../reference/v-if-null-check-order.md | 171 +++ .../reference/v-if-vs-v-show-performance.md | 103 ++ .../v-model-ignores-html-attributes.md | 83 ++ .../reference/v-model-ime-composition.md | 83 ++ .../v-model-number-modifier-behavior.md | 124 ++ .../v-model-vue3-breaking-changes.md | 189 +++ .../reference/v-show-template-limitation.md | 124 ++ .../reference/watch-async-cleanup.md | 180 +++ .../watch-async-creation-memory-leak.md | 176 +++ .../reference/watch-deep-performance.md | 162 +++ .../watch-deep-same-object-reference.md | 165 +++ .../reference/watch-flush-timing.md | 189 +++ .../reference/watch-immediate-option.md | 168 +++ .../watch-reactive-property-getter.md | 108 ++ .../reference/watch-vs-watcheffect.md | 186 +++ .../watcheffect-async-dependency-tracking.md | 173 +++ .../watcheffect-flush-post-for-refs.md | 176 +++ .../.agents/skills/vue/GENERATION.md | 5 + .../h5-uniapp/.agents/skills/vue/LICENSE.md | 21 + .../h5-uniapp/.agents/skills/vue/SKILL.md | 63 + software/h5-uniapp/.agents/skills/vue/SYNC.md | 5 + .../vue/references/advanced-async-suspense.md | 246 ++++ .../vue/references/advanced-provide-inject.md | 174 +++ .../skills/vue/references/components-emits.md | 139 ++ .../vue/references/components-lifecycle.md | 192 +++ .../skills/vue/references/components-props.md | 211 +++ .../skills/vue/references/components-slots.md | 201 +++ .../vue/references/components-v-model.md | 183 +++ .../skills/vue/references/core-reactivity.md | 289 +++++ .../vue/references/features-composables.md | 262 ++++ .../vue/references/features-directives.md | 224 ++++ .../vue/references/features-script-setup.md | 247 ++++ .../vue/references/features-template-refs.md | 212 +++ .../vue/references/features-typescript.md | 274 ++++ .../skills/vueuse-functions/LICENSE.md | 21 + .../.agents/skills/vueuse-functions/SKILL.md | 418 ++++++ .../.agents/skills/vueuse-functions/SYNC.md | 5 + .../references/computedAsync.md | 159 +++ .../references/computedEager.md | 62 + .../references/computedInject.md | 86 ++ .../references/computedWithControl.md | 100 ++ .../references/createEventHook.md | 86 ++ .../references/createGenericProjection.md | 25 + .../references/createGlobalState.md | 95 ++ .../references/createInjectionState.md | 215 ++++ .../references/createProjection.md | 31 + .../vueuse-functions/references/createRef.md | 54 + .../references/createReusableTemplate.md | 357 ++++++ .../references/createSharedComposable.md | 42 + .../references/createTemplatePromise.md | 259 ++++ .../references/createUnrefFn.md | 51 + .../vueuse-functions/references/extendRef.md | 76 ++ .../vueuse-functions/references/from.md | 48 + .../skills/vueuse-functions/references/get.md | 30 + .../references/injectLocal.md | 35 + .../vueuse-functions/references/isDefined.md | 31 + .../vueuse-functions/references/logicAnd.md | 40 + .../vueuse-functions/references/logicNot.md | 36 + .../vueuse-functions/references/logicOr.md | 40 + .../references/makeDestructurable.md | 41 + .../references/onClickOutside.md | 187 +++ .../references/onElementRemoval.md | 64 + .../references/onKeyStroke.md | 176 +++ .../references/onLongPress.md | 170 +++ .../references/onStartTyping.md | 43 + .../references/provideLocal.md | 37 + .../vueuse-functions/references/reactify.md | 144 +++ .../references/reactifyObject.md | 61 + .../references/reactiveComputed.md | 34 + .../references/reactiveOmit.md | 86 ++ .../references/reactivePick.md | 106 ++ .../references/refAutoReset.md | 44 + .../references/refDebounced.md | 81 ++ .../vueuse-functions/references/refDefault.md | 36 + .../references/refManualReset.md | 44 + .../references/refThrottled.md | 99 ++ .../references/refWithControl.md | 146 +++ .../skills/vueuse-functions/references/set.md | 30 + .../vueuse-functions/references/syncRef.md | 195 +++ .../vueuse-functions/references/syncRefs.md | 128 ++ .../references/templateRef.md | 86 ++ .../vueuse-functions/references/toObserver.md | 38 + .../vueuse-functions/references/toReactive.md | 41 + .../vueuse-functions/references/toRef.md | 75 ++ .../vueuse-functions/references/toRefs.md | 81 ++ .../references/tryOnBeforeMount.md | 34 + .../references/tryOnBeforeUnmount.md | 32 + .../references/tryOnMounted.md | 34 + .../references/tryOnScopeDispose.md | 31 + .../references/tryOnUnmounted.md | 32 + .../references/unrefElement.md | 54 + .../vueuse-functions/references/until.md | 165 +++ .../vueuse-functions/references/useAbs.md | 31 + .../references/useActiveElement.md | 65 + .../vueuse-functions/references/useAnimate.md | 140 ++ .../references/useArrayDifference.md | 84 ++ .../references/useArrayEvery.md | 59 + .../references/useArrayFilter.md | 63 + .../references/useArrayFind.md | 50 + .../references/useArrayFindIndex.md | 59 + .../references/useArrayFindLast.md | 52 + .../references/useArrayIncludes.md | 63 + .../references/useArrayJoin.md | 74 ++ .../references/useArrayMap.md | 59 + .../references/useArrayReduce.md | 81 ++ .../references/useArraySome.md | 59 + .../references/useArrayUnique.md | 76 ++ .../references/useAsyncQueue.md | 87 ++ .../references/useAsyncState.md | 140 ++ .../references/useAsyncValidator.md | 70 + .../vueuse-functions/references/useAuth.md | 114 ++ .../vueuse-functions/references/useAverage.md | 36 + .../vueuse-functions/references/useAxios.md | 292 +++++ .../vueuse-functions/references/useBase64.md | 88 ++ .../vueuse-functions/references/useBattery.md | 67 + .../references/useBluetooth.md | 161 +++ .../references/useBreakpoints.md | 128 ++ .../references/useBroadcastChannel.md | 74 ++ .../references/useBrowserLocation.md | 83 ++ .../vueuse-functions/references/useCached.md | 53 + .../vueuse-functions/references/useCeil.md | 31 + .../references/useChangeCase.md | 79 ++ .../vueuse-functions/references/useClamp.md | 51 + .../references/useClipboard.md | 99 ++ .../references/useClipboardItems.md | 93 ++ .../vueuse-functions/references/useCloned.md | 91 ++ .../references/useColorMode.md | 171 +++ .../references/useConfirmDialog.md | 159 +++ .../vueuse-functions/references/useCookies.md | 162 +++ .../references/useCountdown.md | 102 ++ .../vueuse-functions/references/useCounter.md | 86 ++ .../vueuse-functions/references/useCssVar.md | 50 + .../references/useCurrentElement.md | 61 + .../references/useCycleList.md | 75 ++ .../vueuse-functions/references/useDark.md | 142 ++ .../references/useDateFormat.md | 145 +++ .../references/useDebounceFn.md | 100 ++ .../references/useDebouncedRefHistory.md | 40 + .../references/useDeviceMotion.md | 87 ++ .../references/useDeviceOrientation.md | 62 + .../references/useDevicePixelRatio.md | 44 + .../references/useDevicesList.md | 90 ++ .../references/useDisplayMedia.md | 71 + .../references/useDocumentVisibility.md | 45 + .../references/useDraggable.md | 210 +++ .../vueuse-functions/references/useDrauu.md | 65 + .../references/useDropZone.md | 83 ++ .../references/useElementBounding.md | 131 ++ .../references/useElementByPoint.md | 45 + .../references/useElementHover.md | 79 ++ .../references/useElementSize.md | 78 ++ .../references/useElementVisibility.md | 126 ++ .../references/useEventBus.md | 101 ++ .../references/useEventListener.md | 184 +++ .../references/useEventSource.md | 204 +++ .../references/useExtractedObservable.md | 185 +++ .../references/useEyeDropper.md | 71 + .../vueuse-functions/references/useFavicon.md | 67 + .../vueuse-functions/references/useFetch.md | 546 ++++++++ .../references/useFileDialog.md | 91 ++ .../references/useFileSystemAccess.md | 162 +++ .../references/useFirestore.md | 104 ++ .../vueuse-functions/references/useFloor.md | 31 + .../vueuse-functions/references/useFocus.md | 99 ++ .../references/useFocusTrap.md | 245 ++++ .../references/useFocusWithin.md | 57 + .../vueuse-functions/references/useFps.md | 28 + .../references/useFullscreen.md | 75 ++ .../vueuse-functions/references/useFuse.md | 107 ++ .../vueuse-functions/references/useGamepad.md | 223 ++++ .../references/useGeolocation.md | 86 ++ .../references/useIDBKeyval.md | 93 ++ .../vueuse-functions/references/useIdle.md | 89 ++ .../vueuse-functions/references/useImage.md | 86 ++ .../references/useInfiniteScroll.md | 157 +++ .../references/useIntersectionObserver.md | 118 ++ .../references/useInterval.md | 69 + .../references/useIntervalFn.md | 50 + .../references/useIpcRenderer.md | 118 ++ .../references/useIpcRendererInvoke.md | 58 + .../references/useIpcRendererOn.md | 52 + .../vueuse-functions/references/useJwt.md | 57 + .../references/useKeyModifier.md | 87 ++ .../references/useLastChanged.md | 63 + .../references/useLocalStorage.md | 41 + .../references/useMagicKeys.md | 217 ++++ .../references/useManualRefHistory.md | 204 +++ .../vueuse-functions/references/useMath.md | 47 + .../vueuse-functions/references/useMax.md | 36 + .../references/useMediaControls.md | 571 +++++++++ .../references/useMediaQuery.md | 53 + .../vueuse-functions/references/useMemoize.md | 175 +++ .../vueuse-functions/references/useMemory.md | 56 + .../vueuse-functions/references/useMin.md | 36 + .../vueuse-functions/references/useMounted.md | 38 + .../vueuse-functions/references/useMouse.md | 114 ++ .../references/useMouseInElement.md | 123 ++ .../references/useMousePressed.md | 112 ++ .../references/useMutationObserver.md | 62 + .../references/useNProgress.md | 78 ++ .../references/useNavigatorLanguage.md | 57 + .../vueuse-functions/references/useNetwork.md | 106 ++ .../vueuse-functions/references/useNow.md | 75 ++ .../references/useObjectUrl.md | 55 + .../references/useObservable.md | 67 + .../references/useOffsetPagination.md | 199 +++ .../vueuse-functions/references/useOnline.md | 41 + .../references/usePageLeave.md | 42 + .../references/useParallax.md | 58 + .../references/useParentElement.md | 54 + .../references/usePerformanceObserver.md | 48 + .../references/usePermission.md | 78 ++ .../vueuse-functions/references/usePointer.md | 89 ++ .../references/usePointerLock.md | 60 + .../references/usePointerSwipe.md | 80 ++ .../references/usePrecision.md | 49 + .../references/usePreferredColorScheme.md | 42 + .../references/usePreferredContrast.md | 42 + .../references/usePreferredDark.md | 41 + .../references/usePreferredLanguages.md | 41 + .../references/usePreferredReducedMotion.md | 42 + .../usePreferredReducedTransparency.md | 42 + .../references/usePrevious.md | 40 + .../references/useProjection.md | 38 + .../vueuse-functions/references/useQRCode.md | 53 + .../vueuse-functions/references/useRTDB.md | 70 + .../vueuse-functions/references/useRafFn.md | 68 + .../references/useRefHistory.md | 292 +++++ .../references/useResizeObserver.md | 110 ++ .../vueuse-functions/references/useRound.md | 31 + .../references/useRouteHash.md | 27 + .../references/useRouteParams.md | 38 + .../references/useRouteQuery.md | 38 + .../references/useSSRWidth.md | 47 + .../references/useScreenOrientation.md | 96 ++ .../references/useScreenSafeArea.md | 60 + .../references/useScriptTag.md | 116 ++ .../vueuse-functions/references/useScroll.md | 238 ++++ .../references/useScrollLock.md | 66 + .../references/useSessionStorage.md | 41 + .../vueuse-functions/references/useShare.md | 68 + .../references/useSortable.md | 177 +++ .../vueuse-functions/references/useSorted.md | 90 ++ .../references/useSpeechRecognition.md | 94 ++ .../references/useSpeechSynthesis.md | 105 ++ .../vueuse-functions/references/useStepper.md | 137 ++ .../vueuse-functions/references/useStorage.md | 219 ++++ .../references/useStorageAsync.md | 134 ++ .../references/useStyleTag.md | 131 ++ .../vueuse-functions/references/useSubject.md | 52 + .../references/useSubscription.md | 33 + .../vueuse-functions/references/useSum.md | 36 + .../references/useSupported.md | 29 + .../vueuse-functions/references/useSwipe.md | 75 ++ .../references/useTemplateRefsList.md | 37 + .../references/useTextDirection.md | 75 ++ .../references/useTextSelection.md | 40 + .../references/useTextareaAutosize.md | 97 ++ .../references/useThrottleFn.md | 57 + .../references/useThrottledRefHistory.md | 47 + .../vueuse-functions/references/useTimeAgo.md | 152 +++ .../references/useTimeAgoIntl.md | 116 ++ .../vueuse-functions/references/useTimeout.md | 75 ++ .../references/useTimeoutFn.md | 51 + .../references/useTimeoutPoll.md | 47 + .../references/useTimestamp.md | 85 ++ .../vueuse-functions/references/useTitle.md | 113 ++ .../references/useToNumber.md | 54 + .../references/useToString.md | 34 + .../vueuse-functions/references/useToggle.md | 56 + .../references/useTransition.md | 265 ++++ .../vueuse-functions/references/useTrunc.md | 33 + .../references/useUrlSearchParams.md | 121 ++ .../references/useUserMedia.md | 1138 +++++++++++++++++ .../vueuse-functions/references/useVModel.md | 116 ++ .../vueuse-functions/references/useVModels.md | 67 + .../vueuse-functions/references/useVibrate.md | 83 ++ .../references/useVirtualList.md | 184 +++ .../references/useWakeLock.md | 50 + .../references/useWebNotification.md | 177 +++ .../references/useWebSocket.md | 268 ++++ .../references/useWebWorker.md | 60 + .../references/useWebWorkerFn.md | 102 ++ .../references/useWindowFocus.md | 46 + .../references/useWindowScroll.md | 62 + .../references/useWindowSize.md | 76 ++ .../references/useZoomFactor.md | 53 + .../references/useZoomLevel.md | 53 + .../vueuse-functions/references/watchArray.md | 53 + .../references/watchAtMost.md | 54 + .../references/watchDebounced.md | 72 ++ .../vueuse-functions/references/watchDeep.md | 54 + .../references/watchExtractedObservable.md | 167 +++ .../references/watchIgnorable.md | 120 ++ .../references/watchImmediate.md | 44 + .../vueuse-functions/references/watchOnce.md | 41 + .../references/watchPausable.md | 86 ++ .../references/watchThrottled.md | 73 ++ .../references/watchTriggerable.md | 97 ++ .../references/watchWithFilter.md | 55 + .../vueuse-functions/references/whenever.md | 100 ++ .../skills/web-design-guidelines/SKILL.md | 39 + .../skills/web-design-guidelines/SYNC.md | 5 + .../agent/skills/pinia/GENERATION.md | 5 + .../h5-uniapp/agent/skills/pinia/LICENSE.md | 21 + .../h5-uniapp/agent/skills/pinia/SKILL.md | 54 + software/h5-uniapp/agent/skills/pinia/SYNC.md | 5 + .../skills/pinia/references/advanced-hmr.md | 61 + .../skills/pinia/references/advanced-nuxt.md | 119 ++ .../skills/pinia/references/advanced-ssr.md | 121 ++ .../best-practices-outside-component.md | 115 ++ .../references/best-practices-testing.md | 212 +++ .../skills/pinia/references/core-stores.md | 389 ++++++ .../pinia/references/features-composables.md | 114 ++ .../references/features-composing-stores.md | 134 ++ .../pinia/references/features-plugins.md | 203 +++ .../agent/skills/uni-app/GENERATION.md | 54 + .../h5-uniapp/agent/skills/uni-app/SKILL.md | 108 ++ .../uni-app/references/config-manifest.md | 370 ++++++ .../skills/uni-app/references/config-pages.md | 270 ++++ .../references/core-form-components.md | 250 ++++ .../uni-app/references/core-framework.md | 133 ++ .../references/core-view-components.md | 202 +++ .../references/feature-file-operations.md | 391 ++++++ .../uni-app/references/feature-lifecycle.md | 423 ++++++ .../uni-app/references/feature-location.md | 417 ++++++ .../references/feature-media-components.md | 314 +++++ .../uni-app/references/feature-navigation.md | 257 ++++ .../uni-app/references/feature-network.md | 345 +++++ .../uni-app/references/feature-storage.md | 279 ++++ .../uni-app/references/feature-system-info.md | 300 +++++ .../uni-app/references/feature-ui-feedback.md | 382 ++++++ .../agent/skills/uni-helper/GENERATION.md | 5 + .../agent/skills/uni-helper/SKILL.md | 83 ++ .../uni-helper/references/lib-uni-network.md | 221 ++++ .../uni-helper/references/lib-uni-promises.md | 197 +++ .../uni-helper/references/lib-uni-typed.md | 200 +++ .../uni-helper/references/lib-uni-use.md | 185 +++ .../references/plugin-components.md | 93 ++ .../uni-helper/references/plugin-layouts.md | 143 +++ .../uni-helper/references/plugin-manifest.md | 103 ++ .../references/plugin-middleware.md | 181 +++ .../uni-helper/references/plugin-pages.md | 186 +++ .../references/plugin-platform-modifier.md | 119 ++ .../uni-helper/references/plugin-platform.md | 105 ++ .../references/starter-create-uni.md | 153 +++ .../uni-helper/references/starter-vitesse.md | 263 ++++ .../uni-helper/references/util-uni-env.md | 155 +++ .../references/util-unocss-preset.md | 175 +++ .../agent/skills/unocss/GENERATION.md | 5 + .../h5-uniapp/agent/skills/unocss/LICENSE.md | 21 + .../h5-uniapp/agent/skills/unocss/SKILL.md | 59 + .../h5-uniapp/agent/skills/unocss/SYNC.md | 5 + .../skills/unocss/references/core-config.md | 187 +++ .../unocss/references/core-extracting.md | 137 ++ .../skills/unocss/references/core-layers.md | 104 ++ .../skills/unocss/references/core-rules.md | 166 +++ .../skills/unocss/references/core-safelist.md | 105 ++ .../unocss/references/core-shortcuts.md | 89 ++ .../skills/unocss/references/core-theme.md | 172 +++ .../skills/unocss/references/core-variants.md | 175 +++ .../unocss/references/integrations-nuxt.md | 199 +++ .../unocss/references/integrations-vite.md | 283 ++++ .../unocss/references/preset-attributify.md | 142 ++ .../skills/unocss/references/preset-icons.md | 184 +++ .../skills/unocss/references/preset-mini.md | 158 +++ .../unocss/references/preset-rem-to-px.md | 97 ++ .../skills/unocss/references/preset-tagify.md | 134 ++ .../unocss/references/preset-typography.md | 95 ++ .../unocss/references/preset-web-fonts.md | 91 ++ .../skills/unocss/references/preset-wind3.md | 194 +++ .../skills/unocss/references/preset-wind4.md | 247 ++++ .../references/transformer-attributify-jsx.md | 156 +++ .../references/transformer-compile-class.md | 128 ++ .../references/transformer-directives.md | 157 +++ .../references/transformer-variant-group.md | 97 ++ .../h5-uniapp/agent/skills/vite/GENERATION.md | 5 + .../h5-uniapp/agent/skills/vite/LICENSE.md | 21 + software/h5-uniapp/agent/skills/vite/SKILL.md | 45 + software/h5-uniapp/agent/skills/vite/SYNC.md | 5 + .../skills/vite/references/advanced-api.md | 218 ++++ .../vite/references/advanced-backend.md | 164 +++ .../vite/references/advanced-performance.md | 168 +++ .../vite/references/advanced-plugin-api.md | 258 ++++ .../skills/vite/references/build-library.md | 172 +++ .../vite/references/build-production.md | 220 ++++ .../agent/skills/vite/references/build-ssr.md | 194 +++ .../agent/skills/vite/references/core-cli.md | 137 ++ .../skills/vite/references/core-config.md | 176 +++ .../skills/vite/references/core-features.md | 170 +++ .../skills/vite/references/core-plugins.md | 154 +++ .../skills/vite/references/features-assets.md | 138 ++ .../skills/vite/references/features-css.md | 215 ++++ .../vite/references/features-dep-bundling.md | 148 +++ .../skills/vite/references/features-env.md | 161 +++ .../vite/references/features-glob-import.md | 161 +++ .../skills/vite/references/features-hmr.md | 200 +++ .../vite/references/features-workers.md | 115 ++ .../skills/vue-best-practices/LICENSE.md | 21 + .../agent/skills/vue-best-practices/SKILL.md | 309 +++++ .../agent/skills/vue-best-practices/SYNC.md | 5 + .../animation-class-based-technique.md | 258 ++++ .../reference/animation-key-for-rerender.md | 160 +++ .../animation-state-driven-technique.md | 295 +++++ .../animation-transitiongroup-performance.md | 241 ++++ .../async-component-error-handling.md | 115 ++ .../async-component-hydration-strategies.md | 142 ++ .../async-component-keepalive-ref-issue.md | 112 ++ .../async-component-loading-delay.md | 76 ++ .../async-component-suspense-control.md | 74 ++ .../reference/async-component-vue-router.md | 109 ++ .../reference/attrs-event-listener-merging.md | 205 +++ .../attrs-hyphenated-property-access.md | 186 +++ .../reference/attrs-not-reactive.md | 162 +++ .../avoid-prop-drilling-use-provide-inject.md | 249 ++++ ...eckbox-true-false-value-form-submission.md | 118 ++ .../reference/cleanup-side-effects.md | 172 +++ .../reference/click-events-on-components.md | 180 +++ .../reference/component-events-dont-bubble.md | 252 ++++ .../reference/component-naming-conflicts.md | 159 +++ .../reference/component-naming-pascalcase.md | 114 ++ .../component-ref-requires-defineexpose.md | 176 +++ .../composable-avoid-hidden-side-effects.md | 208 +++ .../composable-call-location-restrictions.md | 141 ++ .../composable-composition-pattern.md | 236 ++++ .../composable-naming-return-pattern.md | 139 ++ .../composable-options-object-pattern.md | 209 +++ .../reference/composable-readonly-state.md | 221 ++++ .../composable-tovalue-inside-watcheffect.md | 182 +++ .../composable-vs-utility-functions.md | 193 +++ ...omposition-api-bundle-size-minification.md | 158 +++ .../composition-api-code-organization.md | 213 +++ .../composition-api-mixins-replacement.md | 208 +++ ...position-api-not-functional-programming.md | 120 ++ ...composition-api-options-api-coexistence.md | 185 +++ ...position-api-script-setup-async-context.md | 203 +++ ...position-api-vs-react-hooks-differences.md | 156 +++ .../reference/computed-array-mutation.md | 148 +++ .../computed-conditional-dependencies.md | 147 +++ .../reference/computed-no-parameters.md | 159 +++ .../reference/computed-no-side-effects.md | 107 ++ .../computed-properties-for-class-logic.md | 121 ++ .../computed-return-value-readonly.md | 160 +++ .../reference/computed-vs-methods-caching.md | 119 ++ .../reference/configure-app-before-mount.md | 89 ++ .../declare-emits-for-documentation.md | 212 +++ .../reference/define-expose-before-await.md | 192 +++ .../define-model-default-value-sync.md | 139 ++ .../defineEmits-must-be-top-level.md | 164 +++ .../defineEmits-no-runtime-and-type-mixed.md | 170 +++ .../definemodel-hidden-modifier-props.md | 134 ++ .../definemodel-object-mutation-no-emit.md | 148 +++ .../reference/definemodel-value-next-tick.md | 162 +++ .../directive-arguments-read-only.md | 180 +++ .../directive-avoid-on-components.md | 149 +++ .../directive-cleanup-in-unmounted.md | 219 ++++ .../reference/directive-function-shorthand.md | 189 +++ .../reference/directive-naming-v-prefix.md | 192 +++ ...directive-prefer-declarative-templating.md | 220 ++++ .../directive-vs-component-decision.md | 230 ++++ .../directive-vue2-migration-hooks.md | 210 +++ .../reference/dom-update-timing-nexttick.md | 90 ++ .../reference/dynamic-argument-constraints.md | 146 +++ .../dynamic-component-registration-vite.md | 147 +++ .../dynamic-components-with-keepalive.md | 233 ++++ .../reference/emit-kebab-case-in-templates.md | 166 +++ .../emit-validation-for-complex-payloads.md | 190 +++ .../reference/event-modifier-order-matters.md | 101 ++ .../event-once-modifier-for-single-use.md | 213 +++ .../exact-modifier-for-precise-shortcuts.md | 155 +++ .../fallthrough-attrs-overwrite-vue3.md | 159 +++ .../in-dom-template-parsing-caveats.md | 149 +++ ...heritattrs-false-for-wrapper-components.md | 230 ++++ .../keepalive-component-name-requirement.md | 218 ++++ .../reference/keepalive-memory-management.md | 186 +++ .../keepalive-no-cache-removal-vue3.md | 191 +++ .../keepalive-router-fresh-vs-cached.md | 226 ++++ .../keepalive-router-nested-double-mount.md | 222 ++++ .../keepalive-transition-memory-leak.md | 144 +++ .../reference/keyup-modifier-timing.md | 137 ++ .../reference/lifecycle-dom-access-timing.md | 216 ++++ ...ifecycle-hooks-synchronous-registration.md | 156 +++ .../reference/lifecycle-ssr-awareness.md | 184 +++ .../local-components-not-in-descendants.md | 151 +++ .../reference/mount-return-value.md | 88 ++ .../mouse-button-modifiers-intent.md | 134 ++ .../multi-root-component-class-attrs.md | 93 ++ .../reference/multiple-app-instances.md | 115 ++ .../native-event-collision-with-emits.md | 162 +++ .../reference/no-passive-with-prevent.md | 141 ++ .../reference/no-v-if-with-v-for.md | 136 ++ ...rf-avoid-component-abstraction-in-lists.md | 162 +++ .../perf-computed-object-stability.md | 157 +++ ...erf-props-stability-update-optimization.md | 140 ++ .../reference/perf-ssr-ssg-for-page-load.md | 170 +++ .../perf-v-once-v-memo-directives.md | 187 +++ .../reference/perf-virtualize-large-lists.md | 192 +++ .../plugin-global-properties-sparingly.md | 109 ++ .../reference/plugin-install-before-mount.md | 124 ++ ...r-provide-inject-over-global-properties.md | 120 ++ .../plugin-structure-install-method.md | 206 +++ .../reference/plugin-symbol-injection-keys.md | 165 +++ .../plugin-typescript-type-augmentation.md | 157 +++ .../prefer-local-component-registration.md | 137 ++ .../prefer-props-emit-over-component-refs.md | 216 ++++ .../reference/prefer-ref-over-reactive.md | 82 ++ .../reference/prop-boolean-casting-order.md | 104 ++ .../prop-composable-reactivity-loss.md | 158 +++ .../prop-defineprops-scope-limitation.md | 161 +++ .../prop-destructured-watch-getter.md | 138 ++ .../prop-validation-before-instance.md | 166 +++ .../reference/props-are-read-only.md | 214 ++++ .../provide-inject-debugging-challenges.md | 203 +++ .../provide-inject-default-value-factory.md | 244 ++++ .../provide-inject-mutations-in-provider.md | 190 +++ ...provide-inject-reactivity-not-automatic.md | 226 ++++ .../reference/provide-inject-symbol-keys.md | 232 ++++ .../provide-inject-synchronous-setup.md | 235 ++++ .../reference/reactive-destructuring.md | 89 ++ ...ity-computed-over-watcheffect-mutations.md | 128 ++ .../reference/reactivity-debugging-hooks.md | 132 ++ .../reactivity-external-state-integration.md | 165 +++ .../reactivity-markraw-for-non-reactive.md | 149 +++ .../reactivity-proxy-identity-hazard.md | 96 ++ .../reactivity-same-tick-batching.md | 166 +++ .../reference/ref-value-access.md | 61 + .../refs-in-collections-need-value.md | 81 ++ ...unction-avoid-internal-vnode-properties.md | 151 +++ .../render-function-custom-directives.md | 201 +++ .../render-function-event-modifiers.md | 190 +++ .../render-function-functional-components.md | 171 +++ .../render-function-v-for-keys-required.md | 160 +++ .../render-function-v-model-implementation.md | 221 ++++ .../render-function-vnodes-must-be-unique.md | 133 ++ ...g-excessive-rerenders-watch-vs-computed.md | 163 +++ ...-prefer-templates-over-render-functions.md | 125 ++ ...rendering-render-function-h-import-vue3.md | 148 +++ ...ering-render-function-return-from-setup.md | 148 +++ ...ring-render-function-slots-as-functions.md | 168 +++ ...ring-resolve-component-for-string-names.md | 231 ++++ ...ndering-understand-vdom-block-structure.md | 137 ++ .../reference/select-initial-value-ios-bug.md | 91 ++ .../self-referencing-component-name.md | 157 +++ .../reference/sfc-named-exports-forbidden.md | 184 +++ .../sfc-recommended-for-build-projects.md | 109 ++ .../sfc-scoped-css-child-component-styling.md | 156 +++ .../sfc-scoped-css-dynamic-content.md | 193 +++ .../reference/sfc-scoped-css-performance.md | 208 +++ .../reference/sfc-scoped-css-slot-content.md | 242 ++++ .../reference/sfc-script-setup-reactivity.md | 195 +++ .../sfc-separation-of-concerns-colocate.md | 225 ++++ .../reference/shallow-ref-for-performance.md | 112 ++ .../slot-conditional-rendering-with-slots.md | 138 ++ .../slot-define-slots-for-typescript.md | 171 +++ .../slot-fallback-content-default-values.md | 135 ++ .../slot-forwarding-to-child-components.md | 143 +++ .../slot-implicit-default-content.md | 155 +++ .../reference/slot-name-reserved-prop.md | 109 ++ .../slot-named-scoped-explicit-default.md | 95 ++ .../slot-render-scope-parent-only.md | 135 ++ ...ot-renderless-components-vs-composables.md | 153 +++ ...-v-slot-on-components-or-templates-only.md | 122 ++ .../ssr-custom-directive-getssrprops.md | 212 +++ .../ssr-hydration-mismatch-causes.md | 280 ++++ .../reference/ssr-platform-specific-apis.md | 256 ++++ .../state-ssr-cross-request-pollution.md | 276 ++++ .../reference/style-binding-camelcase.md | 131 ++ .../suspense-events-for-state-tracking.md | 169 +++ .../suspense-experimental-api-stability.md | 79 ++ ...spense-fallback-not-immediate-on-revert.md | 116 ++ .../suspense-nested-suspensible-prop.md | 144 +++ .../suspense-nesting-order-with-router.md | 129 ++ .../suspense-no-builtin-error-handling.md | 127 ++ .../suspense-revert-only-on-root-change.md | 139 ++ .../suspense-single-child-requirement.md | 91 ++ .../suspense-ssr-hydration-issues.md | 159 +++ .../tailwind-dynamic-class-generation.md | 144 +++ .../teleport-css-positioning-issues.md | 133 ++ .../teleport-disabled-for-responsive.md | 223 ++++ .../teleport-logical-hierarchy-preserved.md | 168 +++ .../teleport-multiple-to-same-target.md | 155 +++ .../teleport-scoped-styles-limitation.md | 191 +++ .../reference/teleport-ssr-hydration.md | 152 +++ .../reference/teleport-target-must-exist.md | 113 ++ .../template-expressions-restrictions.md | 114 ++ .../template-functions-no-side-effects.md | 187 +++ .../reference/template-ref-null-with-v-if.md | 123 ++ .../template-ref-unwrapping-top-level.md | 104 ++ .../reference/template-ref-v-for-order.md | 172 +++ .../reference/textarea-no-interpolation.md | 72 ++ .../transition-animate-transform-opacity.md | 186 +++ .../transition-group-flip-inline-elements.md | 152 +++ .../transition-group-key-requirement.md | 107 ++ ...-group-move-animation-position-absolute.md | 130 ++ ...ransition-group-no-default-wrapper-vue3.md | 152 +++ .../transition-group-no-mode-prop.md | 172 +++ .../transition-group-staggered-animations.md | 200 +++ .../transition-js-hooks-done-callback.md | 251 ++++ .../transition-key-for-same-element.md | 191 +++ .../reference/transition-mode-out-in.md | 212 +++ .../reference/transition-nested-duration.md | 182 +++ .../transition-reusable-scoped-style.md | 245 ++++ .../transition-router-view-appear.md | 193 +++ .../transition-single-element-slot.md | 118 ++ .../reference/transition-type-when-mixed.md | 172 +++ .../transition-unmount-hook-timing.md | 149 +++ .../ts-component-ref-typeof-instancetype.md | 260 ++++ .../ts-custom-directive-type-augmentation.md | 264 ++++ .../ts-defineemits-type-based-syntax.md | 191 +++ .../ts-defineprops-boolean-default-false.md | 225 ++++ ...-defineprops-imported-types-limitations.md | 281 ++++ .../ts-defineprops-type-based-declaration.md | 171 +++ .../ts-event-handler-explicit-typing.md | 213 +++ .../ts-provide-inject-injection-key.md | 257 ++++ .../ts-reactive-no-generic-argument.md | 196 +++ .../ts-shallowref-for-dynamic-components.md | 218 ++++ .../ts-template-ref-null-handling.md | 249 ++++ .../reference/ts-template-type-casting.md | 214 ++++ ...s-withdefaults-mutable-factory-function.md | 171 +++ .../undeclared-emits-double-firing.md | 195 +++ .../reference/updated-hook-performance.md | 190 +++ .../reference/use-template-ref-vue35.md | 158 +++ .../reference/v-else-must-follow-v-if.md | 136 ++ .../reference/v-for-component-props.md | 95 ++ .../reference/v-for-computed-reverse-sort.md | 86 ++ .../reference/v-for-key-attribute.md | 90 ++ .../reference/v-for-range-starts-at-one.md | 66 + .../v-for-use-computed-for-filtering.md | 99 ++ .../reference/v-html-xss-security.md | 111 ++ .../reference/v-if-null-check-order.md | 171 +++ .../reference/v-if-vs-v-show-performance.md | 103 ++ .../v-model-ignores-html-attributes.md | 83 ++ .../reference/v-model-ime-composition.md | 83 ++ .../v-model-number-modifier-behavior.md | 124 ++ .../v-model-vue3-breaking-changes.md | 189 +++ .../reference/v-show-template-limitation.md | 124 ++ .../reference/watch-async-cleanup.md | 180 +++ .../watch-async-creation-memory-leak.md | 176 +++ .../reference/watch-deep-performance.md | 162 +++ .../watch-deep-same-object-reference.md | 165 +++ .../reference/watch-flush-timing.md | 189 +++ .../reference/watch-immediate-option.md | 168 +++ .../watch-reactive-property-getter.md | 108 ++ .../reference/watch-vs-watcheffect.md | 186 +++ .../watcheffect-async-dependency-tracking.md | 173 +++ .../watcheffect-flush-post-for-refs.md | 176 +++ .../h5-uniapp/agent/skills/vue/GENERATION.md | 5 + .../h5-uniapp/agent/skills/vue/LICENSE.md | 21 + software/h5-uniapp/agent/skills/vue/SKILL.md | 58 + software/h5-uniapp/agent/skills/vue/SYNC.md | 5 + .../vue/references/advanced-async-suspense.md | 246 ++++ .../vue/references/advanced-provide-inject.md | 174 +++ .../skills/vue/references/components-emits.md | 139 ++ .../vue/references/components-lifecycle.md | 192 +++ .../skills/vue/references/components-props.md | 211 +++ .../skills/vue/references/components-slots.md | 201 +++ .../vue/references/components-v-model.md | 183 +++ .../skills/vue/references/core-reactivity.md | 289 +++++ .../vue/references/features-composables.md | 262 ++++ .../vue/references/features-directives.md | 224 ++++ .../vue/references/features-script-setup.md | 247 ++++ .../vue/references/features-template-refs.md | 212 +++ .../vue/references/features-typescript.md | 274 ++++ .../agent/skills/vueuse-functions/LICENSE.md | 21 + .../agent/skills/vueuse-functions/SKILL.md | 413 ++++++ .../agent/skills/vueuse-functions/SYNC.md | 5 + .../references/computedAsync.md | 159 +++ .../references/computedEager.md | 62 + .../references/computedInject.md | 86 ++ .../references/computedWithControl.md | 100 ++ .../references/createEventHook.md | 86 ++ .../references/createGenericProjection.md | 25 + .../references/createGlobalState.md | 95 ++ .../references/createInjectionState.md | 215 ++++ .../references/createProjection.md | 31 + .../vueuse-functions/references/createRef.md | 54 + .../references/createReusableTemplate.md | 357 ++++++ .../references/createSharedComposable.md | 42 + .../references/createTemplatePromise.md | 259 ++++ .../references/createUnrefFn.md | 51 + .../vueuse-functions/references/extendRef.md | 76 ++ .../vueuse-functions/references/from.md | 48 + .../skills/vueuse-functions/references/get.md | 30 + .../references/injectLocal.md | 35 + .../vueuse-functions/references/isDefined.md | 31 + .../vueuse-functions/references/logicAnd.md | 40 + .../vueuse-functions/references/logicNot.md | 36 + .../vueuse-functions/references/logicOr.md | 40 + .../references/makeDestructurable.md | 41 + .../references/onClickOutside.md | 187 +++ .../references/onElementRemoval.md | 64 + .../references/onKeyStroke.md | 176 +++ .../references/onLongPress.md | 170 +++ .../references/onStartTyping.md | 43 + .../references/provideLocal.md | 37 + .../vueuse-functions/references/reactify.md | 144 +++ .../references/reactifyObject.md | 61 + .../references/reactiveComputed.md | 34 + .../references/reactiveOmit.md | 86 ++ .../references/reactivePick.md | 106 ++ .../references/refAutoReset.md | 44 + .../references/refDebounced.md | 81 ++ .../vueuse-functions/references/refDefault.md | 36 + .../references/refManualReset.md | 44 + .../references/refThrottled.md | 99 ++ .../references/refWithControl.md | 146 +++ .../skills/vueuse-functions/references/set.md | 30 + .../vueuse-functions/references/syncRef.md | 195 +++ .../vueuse-functions/references/syncRefs.md | 128 ++ .../references/templateRef.md | 86 ++ .../vueuse-functions/references/toObserver.md | 38 + .../vueuse-functions/references/toReactive.md | 41 + .../vueuse-functions/references/toRef.md | 75 ++ .../vueuse-functions/references/toRefs.md | 81 ++ .../references/tryOnBeforeMount.md | 34 + .../references/tryOnBeforeUnmount.md | 32 + .../references/tryOnMounted.md | 34 + .../references/tryOnScopeDispose.md | 31 + .../references/tryOnUnmounted.md | 32 + .../references/unrefElement.md | 54 + .../vueuse-functions/references/until.md | 165 +++ .../vueuse-functions/references/useAbs.md | 31 + .../references/useActiveElement.md | 65 + .../vueuse-functions/references/useAnimate.md | 140 ++ .../references/useArrayDifference.md | 84 ++ .../references/useArrayEvery.md | 59 + .../references/useArrayFilter.md | 63 + .../references/useArrayFind.md | 50 + .../references/useArrayFindIndex.md | 59 + .../references/useArrayFindLast.md | 52 + .../references/useArrayIncludes.md | 63 + .../references/useArrayJoin.md | 74 ++ .../references/useArrayMap.md | 59 + .../references/useArrayReduce.md | 81 ++ .../references/useArraySome.md | 59 + .../references/useArrayUnique.md | 76 ++ .../references/useAsyncQueue.md | 87 ++ .../references/useAsyncState.md | 140 ++ .../references/useAsyncValidator.md | 70 + .../vueuse-functions/references/useAuth.md | 114 ++ .../vueuse-functions/references/useAverage.md | 36 + .../vueuse-functions/references/useAxios.md | 292 +++++ .../vueuse-functions/references/useBase64.md | 88 ++ .../vueuse-functions/references/useBattery.md | 67 + .../references/useBluetooth.md | 161 +++ .../references/useBreakpoints.md | 128 ++ .../references/useBroadcastChannel.md | 74 ++ .../references/useBrowserLocation.md | 83 ++ .../vueuse-functions/references/useCached.md | 53 + .../vueuse-functions/references/useCeil.md | 31 + .../references/useChangeCase.md | 79 ++ .../vueuse-functions/references/useClamp.md | 51 + .../references/useClipboard.md | 99 ++ .../references/useClipboardItems.md | 93 ++ .../vueuse-functions/references/useCloned.md | 91 ++ .../references/useColorMode.md | 171 +++ .../references/useConfirmDialog.md | 159 +++ .../vueuse-functions/references/useCookies.md | 162 +++ .../references/useCountdown.md | 102 ++ .../vueuse-functions/references/useCounter.md | 86 ++ .../vueuse-functions/references/useCssVar.md | 50 + .../references/useCurrentElement.md | 61 + .../references/useCycleList.md | 75 ++ .../vueuse-functions/references/useDark.md | 142 ++ .../references/useDateFormat.md | 145 +++ .../references/useDebounceFn.md | 100 ++ .../references/useDebouncedRefHistory.md | 40 + .../references/useDeviceMotion.md | 87 ++ .../references/useDeviceOrientation.md | 62 + .../references/useDevicePixelRatio.md | 44 + .../references/useDevicesList.md | 90 ++ .../references/useDisplayMedia.md | 71 + .../references/useDocumentVisibility.md | 45 + .../references/useDraggable.md | 210 +++ .../vueuse-functions/references/useDrauu.md | 65 + .../references/useDropZone.md | 83 ++ .../references/useElementBounding.md | 131 ++ .../references/useElementByPoint.md | 45 + .../references/useElementHover.md | 79 ++ .../references/useElementSize.md | 78 ++ .../references/useElementVisibility.md | 126 ++ .../references/useEventBus.md | 101 ++ .../references/useEventListener.md | 184 +++ .../references/useEventSource.md | 204 +++ .../references/useExtractedObservable.md | 185 +++ .../references/useEyeDropper.md | 71 + .../vueuse-functions/references/useFavicon.md | 67 + .../vueuse-functions/references/useFetch.md | 546 ++++++++ .../references/useFileDialog.md | 91 ++ .../references/useFileSystemAccess.md | 162 +++ .../references/useFirestore.md | 104 ++ .../vueuse-functions/references/useFloor.md | 31 + .../vueuse-functions/references/useFocus.md | 99 ++ .../references/useFocusTrap.md | 245 ++++ .../references/useFocusWithin.md | 57 + .../vueuse-functions/references/useFps.md | 28 + .../references/useFullscreen.md | 75 ++ .../vueuse-functions/references/useFuse.md | 107 ++ .../vueuse-functions/references/useGamepad.md | 223 ++++ .../references/useGeolocation.md | 86 ++ .../references/useIDBKeyval.md | 93 ++ .../vueuse-functions/references/useIdle.md | 89 ++ .../vueuse-functions/references/useImage.md | 86 ++ .../references/useInfiniteScroll.md | 157 +++ .../references/useIntersectionObserver.md | 118 ++ .../references/useInterval.md | 69 + .../references/useIntervalFn.md | 50 + .../references/useIpcRenderer.md | 118 ++ .../references/useIpcRendererInvoke.md | 58 + .../references/useIpcRendererOn.md | 52 + .../vueuse-functions/references/useJwt.md | 57 + .../references/useKeyModifier.md | 87 ++ .../references/useLastChanged.md | 63 + .../references/useLocalStorage.md | 41 + .../references/useMagicKeys.md | 217 ++++ .../references/useManualRefHistory.md | 204 +++ .../vueuse-functions/references/useMath.md | 47 + .../vueuse-functions/references/useMax.md | 36 + .../references/useMediaControls.md | 571 +++++++++ .../references/useMediaQuery.md | 53 + .../vueuse-functions/references/useMemoize.md | 175 +++ .../vueuse-functions/references/useMemory.md | 56 + .../vueuse-functions/references/useMin.md | 36 + .../vueuse-functions/references/useMounted.md | 38 + .../vueuse-functions/references/useMouse.md | 114 ++ .../references/useMouseInElement.md | 123 ++ .../references/useMousePressed.md | 112 ++ .../references/useMutationObserver.md | 62 + .../references/useNProgress.md | 78 ++ .../references/useNavigatorLanguage.md | 57 + .../vueuse-functions/references/useNetwork.md | 106 ++ .../vueuse-functions/references/useNow.md | 75 ++ .../references/useObjectUrl.md | 55 + .../references/useObservable.md | 67 + .../references/useOffsetPagination.md | 199 +++ .../vueuse-functions/references/useOnline.md | 41 + .../references/usePageLeave.md | 42 + .../references/useParallax.md | 58 + .../references/useParentElement.md | 54 + .../references/usePerformanceObserver.md | 48 + .../references/usePermission.md | 78 ++ .../vueuse-functions/references/usePointer.md | 89 ++ .../references/usePointerLock.md | 60 + .../references/usePointerSwipe.md | 80 ++ .../references/usePrecision.md | 49 + .../references/usePreferredColorScheme.md | 42 + .../references/usePreferredContrast.md | 42 + .../references/usePreferredDark.md | 41 + .../references/usePreferredLanguages.md | 41 + .../references/usePreferredReducedMotion.md | 42 + .../usePreferredReducedTransparency.md | 42 + .../references/usePrevious.md | 40 + .../references/useProjection.md | 38 + .../vueuse-functions/references/useQRCode.md | 53 + .../vueuse-functions/references/useRTDB.md | 70 + .../vueuse-functions/references/useRafFn.md | 68 + .../references/useRefHistory.md | 292 +++++ .../references/useResizeObserver.md | 110 ++ .../vueuse-functions/references/useRound.md | 31 + .../references/useRouteHash.md | 27 + .../references/useRouteParams.md | 38 + .../references/useRouteQuery.md | 38 + .../references/useSSRWidth.md | 47 + .../references/useScreenOrientation.md | 96 ++ .../references/useScreenSafeArea.md | 60 + .../references/useScriptTag.md | 116 ++ .../vueuse-functions/references/useScroll.md | 238 ++++ .../references/useScrollLock.md | 66 + .../references/useSessionStorage.md | 41 + .../vueuse-functions/references/useShare.md | 68 + .../references/useSortable.md | 177 +++ .../vueuse-functions/references/useSorted.md | 90 ++ .../references/useSpeechRecognition.md | 94 ++ .../references/useSpeechSynthesis.md | 105 ++ .../vueuse-functions/references/useStepper.md | 137 ++ .../vueuse-functions/references/useStorage.md | 219 ++++ .../references/useStorageAsync.md | 134 ++ .../references/useStyleTag.md | 131 ++ .../vueuse-functions/references/useSubject.md | 52 + .../references/useSubscription.md | 33 + .../vueuse-functions/references/useSum.md | 36 + .../references/useSupported.md | 29 + .../vueuse-functions/references/useSwipe.md | 75 ++ .../references/useTemplateRefsList.md | 37 + .../references/useTextDirection.md | 75 ++ .../references/useTextSelection.md | 40 + .../references/useTextareaAutosize.md | 97 ++ .../references/useThrottleFn.md | 57 + .../references/useThrottledRefHistory.md | 47 + .../vueuse-functions/references/useTimeAgo.md | 152 +++ .../references/useTimeAgoIntl.md | 116 ++ .../vueuse-functions/references/useTimeout.md | 75 ++ .../references/useTimeoutFn.md | 51 + .../references/useTimeoutPoll.md | 47 + .../references/useTimestamp.md | 85 ++ .../vueuse-functions/references/useTitle.md | 113 ++ .../references/useToNumber.md | 54 + .../references/useToString.md | 34 + .../vueuse-functions/references/useToggle.md | 56 + .../references/useTransition.md | 265 ++++ .../vueuse-functions/references/useTrunc.md | 33 + .../references/useUrlSearchParams.md | 121 ++ .../references/useUserMedia.md | 1138 +++++++++++++++++ .../vueuse-functions/references/useVModel.md | 116 ++ .../vueuse-functions/references/useVModels.md | 67 + .../vueuse-functions/references/useVibrate.md | 83 ++ .../references/useVirtualList.md | 184 +++ .../references/useWakeLock.md | 50 + .../references/useWebNotification.md | 177 +++ .../references/useWebSocket.md | 268 ++++ .../references/useWebWorker.md | 60 + .../references/useWebWorkerFn.md | 102 ++ .../references/useWindowFocus.md | 46 + .../references/useWindowScroll.md | 62 + .../references/useWindowSize.md | 76 ++ .../references/useZoomFactor.md | 53 + .../references/useZoomLevel.md | 53 + .../vueuse-functions/references/watchArray.md | 53 + .../references/watchAtMost.md | 54 + .../references/watchDebounced.md | 72 ++ .../vueuse-functions/references/watchDeep.md | 54 + .../references/watchExtractedObservable.md | 167 +++ .../references/watchIgnorable.md | 120 ++ .../references/watchImmediate.md | 44 + .../vueuse-functions/references/watchOnce.md | 41 + .../references/watchPausable.md | 86 ++ .../references/watchThrottled.md | 73 ++ .../references/watchTriggerable.md | 97 ++ .../references/watchWithFilter.md | 55 + .../vueuse-functions/references/whenever.md | 100 ++ .../skills/web-design-guidelines/SKILL.md | 34 + .../skills/web-design-guidelines/SYNC.md | 5 + software/h5-uniapp/index.html | 9 +- software/h5-uniapp/package.json | 1 + software/h5-uniapp/skills-lock.json | 59 + software/h5-uniapp/src/App.vue | 6 + software/h5-uniapp/src/api/index.ts | 57 +- software/h5-uniapp/src/pages.json | 20 +- .../src/pages/compartment-detail/index.vue | 42 +- .../src/pages/device-detail/index.vue | 227 +++- software/h5-uniapp/src/pages/home/index.vue | 701 ++++++++-- software/h5-uniapp/src/pages/login/index.vue | 57 +- software/h5-uniapp/src/pages/me/index.vue | 38 +- software/h5-uniapp/src/pages/orders/index.vue | 195 +++ .../src/static/tabbar/device-active.png | Bin 69 -> 0 bytes .../h5-uniapp/src/static/tabbar/device.png | Bin 69 -> 0 bytes .../src/static/tabbar/home-active.png | Bin 69 -> 1214 bytes software/h5-uniapp/src/static/tabbar/home.png | Bin 69 -> 1212 bytes software/h5-uniapp/src/static/tabbar/home.svg | 16 + .../h5-uniapp/src/static/tabbar/me-active.png | Bin 69 -> 1474 bytes software/h5-uniapp/src/static/tabbar/me.png | Bin 69 -> 1474 bytes .../src/static/tabbar/notebook-tabs.svg | 1 + .../src/static/tabbar/orders-active.png | Bin 0 -> 1104 bytes .../h5-uniapp/src/static/tabbar/orders.png | Bin 0 -> 1095 bytes software/h5-uniapp/src/static/tabbar/user.svg | 16 + software/h5-uniapp/src/static/tabbar/zap.svg | 1 + software/h5-uniapp/src/store/auth.ts | 11 +- software/web/src/api/devices.ts | 1 + software/web/src/api/request.ts | 2 +- software/web/src/api/user.ts | 1 + .../web/src/pages/devices/CabinetGrid.tsx | 149 ++- software/web/src/pages/devices/cabinet.tsx | 129 +- software/web/src/pages/devices/index.tsx | 2 +- software/web/src/pages/settings/roles.tsx | 14 +- software/web/src/pages/settings/users.tsx | 47 +- 1315 files changed, 171710 insertions(+), 2617 deletions(-) delete mode 100644 docs/1TN协议内容_完整.txt create mode 100644 docs/API.md delete mode 100644 docs/build_contract.js delete mode 100644 docs/convert.js delete mode 100644 docs/package-lock.json delete mode 100644 docs/package.json delete mode 100644 docs/toPdf.js delete mode 100644 docs/开发计划.md create mode 100644 software/h5-uniapp/.agents/skills/pinia/GENERATION.md create mode 100644 software/h5-uniapp/.agents/skills/pinia/LICENSE.md create mode 100644 software/h5-uniapp/.agents/skills/pinia/SKILL.md create mode 100644 software/h5-uniapp/.agents/skills/pinia/SYNC.md create mode 100644 software/h5-uniapp/.agents/skills/pinia/references/advanced-hmr.md create mode 100644 software/h5-uniapp/.agents/skills/pinia/references/advanced-nuxt.md create mode 100644 software/h5-uniapp/.agents/skills/pinia/references/advanced-ssr.md create mode 100644 software/h5-uniapp/.agents/skills/pinia/references/best-practices-outside-component.md create mode 100644 software/h5-uniapp/.agents/skills/pinia/references/best-practices-testing.md create mode 100644 software/h5-uniapp/.agents/skills/pinia/references/core-stores.md create mode 100644 software/h5-uniapp/.agents/skills/pinia/references/features-composables.md create mode 100644 software/h5-uniapp/.agents/skills/pinia/references/features-composing-stores.md create mode 100644 software/h5-uniapp/.agents/skills/pinia/references/features-plugins.md create mode 100644 software/h5-uniapp/.agents/skills/uni-app/GENERATION.md create mode 100644 software/h5-uniapp/.agents/skills/uni-app/SKILL.md create mode 100644 software/h5-uniapp/.agents/skills/uni-app/references/config-manifest.md create mode 100644 software/h5-uniapp/.agents/skills/uni-app/references/config-pages.md create mode 100644 software/h5-uniapp/.agents/skills/uni-app/references/core-form-components.md create mode 100644 software/h5-uniapp/.agents/skills/uni-app/references/core-framework.md create mode 100644 software/h5-uniapp/.agents/skills/uni-app/references/core-view-components.md create mode 100644 software/h5-uniapp/.agents/skills/uni-app/references/feature-file-operations.md create mode 100644 software/h5-uniapp/.agents/skills/uni-app/references/feature-lifecycle.md create mode 100644 software/h5-uniapp/.agents/skills/uni-app/references/feature-location.md create mode 100644 software/h5-uniapp/.agents/skills/uni-app/references/feature-media-components.md create mode 100644 software/h5-uniapp/.agents/skills/uni-app/references/feature-navigation.md create mode 100644 software/h5-uniapp/.agents/skills/uni-app/references/feature-network.md create mode 100644 software/h5-uniapp/.agents/skills/uni-app/references/feature-storage.md create mode 100644 software/h5-uniapp/.agents/skills/uni-app/references/feature-system-info.md create mode 100644 software/h5-uniapp/.agents/skills/uni-app/references/feature-ui-feedback.md create mode 100644 software/h5-uniapp/.agents/skills/uni-helper/GENERATION.md create mode 100644 software/h5-uniapp/.agents/skills/uni-helper/SKILL.md create mode 100644 software/h5-uniapp/.agents/skills/uni-helper/references/lib-uni-network.md create mode 100644 software/h5-uniapp/.agents/skills/uni-helper/references/lib-uni-promises.md create mode 100644 software/h5-uniapp/.agents/skills/uni-helper/references/lib-uni-typed.md create mode 100644 software/h5-uniapp/.agents/skills/uni-helper/references/lib-uni-use.md create mode 100644 software/h5-uniapp/.agents/skills/uni-helper/references/plugin-components.md create mode 100644 software/h5-uniapp/.agents/skills/uni-helper/references/plugin-layouts.md create mode 100644 software/h5-uniapp/.agents/skills/uni-helper/references/plugin-manifest.md create mode 100644 software/h5-uniapp/.agents/skills/uni-helper/references/plugin-middleware.md create mode 100644 software/h5-uniapp/.agents/skills/uni-helper/references/plugin-pages.md create mode 100644 software/h5-uniapp/.agents/skills/uni-helper/references/plugin-platform-modifier.md create mode 100644 software/h5-uniapp/.agents/skills/uni-helper/references/plugin-platform.md create mode 100644 software/h5-uniapp/.agents/skills/uni-helper/references/starter-create-uni.md create mode 100644 software/h5-uniapp/.agents/skills/uni-helper/references/starter-vitesse.md create mode 100644 software/h5-uniapp/.agents/skills/uni-helper/references/util-uni-env.md create mode 100644 software/h5-uniapp/.agents/skills/uni-helper/references/util-unocss-preset.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/GENERATION.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/LICENSE.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/SKILL.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/SYNC.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/core-config.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/core-extracting.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/core-layers.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/core-rules.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/core-safelist.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/core-shortcuts.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/core-theme.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/core-variants.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/integrations-nuxt.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/integrations-vite.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/preset-attributify.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/preset-icons.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/preset-mini.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/preset-rem-to-px.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/preset-tagify.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/preset-typography.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/preset-web-fonts.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/preset-wind3.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/preset-wind4.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/transformer-attributify-jsx.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/transformer-compile-class.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/transformer-directives.md create mode 100644 software/h5-uniapp/.agents/skills/unocss/references/transformer-variant-group.md create mode 100644 software/h5-uniapp/.agents/skills/vite/GENERATION.md create mode 100644 software/h5-uniapp/.agents/skills/vite/LICENSE.md create mode 100644 software/h5-uniapp/.agents/skills/vite/SKILL.md create mode 100644 software/h5-uniapp/.agents/skills/vite/SYNC.md create mode 100644 software/h5-uniapp/.agents/skills/vite/references/advanced-api.md create mode 100644 software/h5-uniapp/.agents/skills/vite/references/advanced-backend.md create mode 100644 software/h5-uniapp/.agents/skills/vite/references/advanced-performance.md create mode 100644 software/h5-uniapp/.agents/skills/vite/references/advanced-plugin-api.md create mode 100644 software/h5-uniapp/.agents/skills/vite/references/build-library.md create mode 100644 software/h5-uniapp/.agents/skills/vite/references/build-production.md create mode 100644 software/h5-uniapp/.agents/skills/vite/references/build-ssr.md create mode 100644 software/h5-uniapp/.agents/skills/vite/references/core-cli.md create mode 100644 software/h5-uniapp/.agents/skills/vite/references/core-config.md create mode 100644 software/h5-uniapp/.agents/skills/vite/references/core-features.md create mode 100644 software/h5-uniapp/.agents/skills/vite/references/core-plugins.md create mode 100644 software/h5-uniapp/.agents/skills/vite/references/features-assets.md create mode 100644 software/h5-uniapp/.agents/skills/vite/references/features-css.md create mode 100644 software/h5-uniapp/.agents/skills/vite/references/features-dep-bundling.md create mode 100644 software/h5-uniapp/.agents/skills/vite/references/features-env.md create mode 100644 software/h5-uniapp/.agents/skills/vite/references/features-glob-import.md create mode 100644 software/h5-uniapp/.agents/skills/vite/references/features-hmr.md create mode 100644 software/h5-uniapp/.agents/skills/vite/references/features-workers.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/LICENSE.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/SKILL.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/SYNC.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/animation-class-based-technique.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/animation-key-for-rerender.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/animation-state-driven-technique.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/animation-transitiongroup-performance.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/async-component-error-handling.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/async-component-hydration-strategies.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/async-component-keepalive-ref-issue.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/async-component-loading-delay.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/async-component-suspense-control.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/async-component-vue-router.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/attrs-event-listener-merging.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/attrs-hyphenated-property-access.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/attrs-not-reactive.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/avoid-prop-drilling-use-provide-inject.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/checkbox-true-false-value-form-submission.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/cleanup-side-effects.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/click-events-on-components.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/component-events-dont-bubble.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/component-naming-conflicts.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/component-naming-pascalcase.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/component-ref-requires-defineexpose.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/composable-avoid-hidden-side-effects.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/composable-call-location-restrictions.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/composable-composition-pattern.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/composable-naming-return-pattern.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/composable-options-object-pattern.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/composable-readonly-state.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/composable-tovalue-inside-watcheffect.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/composable-vs-utility-functions.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/composition-api-bundle-size-minification.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/composition-api-code-organization.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/composition-api-mixins-replacement.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/composition-api-not-functional-programming.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/composition-api-options-api-coexistence.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/composition-api-script-setup-async-context.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/composition-api-vs-react-hooks-differences.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/computed-array-mutation.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/computed-conditional-dependencies.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/computed-no-parameters.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/computed-no-side-effects.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/computed-properties-for-class-logic.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/computed-return-value-readonly.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/computed-vs-methods-caching.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/configure-app-before-mount.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/declare-emits-for-documentation.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/define-expose-before-await.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/define-model-default-value-sync.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/defineEmits-must-be-top-level.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/defineEmits-no-runtime-and-type-mixed.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/definemodel-hidden-modifier-props.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/definemodel-object-mutation-no-emit.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/definemodel-value-next-tick.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/directive-arguments-read-only.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/directive-avoid-on-components.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/directive-cleanup-in-unmounted.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/directive-function-shorthand.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/directive-naming-v-prefix.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/directive-prefer-declarative-templating.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/directive-vs-component-decision.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/directive-vue2-migration-hooks.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/dom-update-timing-nexttick.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/dynamic-argument-constraints.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/dynamic-component-registration-vite.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/dynamic-components-with-keepalive.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/emit-kebab-case-in-templates.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/emit-validation-for-complex-payloads.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/event-modifier-order-matters.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/event-once-modifier-for-single-use.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/exact-modifier-for-precise-shortcuts.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/fallthrough-attrs-overwrite-vue3.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/in-dom-template-parsing-caveats.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/inheritattrs-false-for-wrapper-components.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/keepalive-component-name-requirement.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/keepalive-memory-management.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/keepalive-no-cache-removal-vue3.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/keepalive-router-fresh-vs-cached.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/keepalive-router-nested-double-mount.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/keepalive-transition-memory-leak.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/keyup-modifier-timing.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/lifecycle-dom-access-timing.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/lifecycle-hooks-synchronous-registration.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/lifecycle-ssr-awareness.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/local-components-not-in-descendants.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/mount-return-value.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/mouse-button-modifiers-intent.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/multi-root-component-class-attrs.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/multiple-app-instances.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/native-event-collision-with-emits.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/no-passive-with-prevent.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/no-v-if-with-v-for.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/perf-avoid-component-abstraction-in-lists.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/perf-computed-object-stability.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/perf-props-stability-update-optimization.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/perf-ssr-ssg-for-page-load.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/perf-v-once-v-memo-directives.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/perf-virtualize-large-lists.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/plugin-global-properties-sparingly.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/plugin-install-before-mount.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/plugin-prefer-provide-inject-over-global-properties.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/plugin-structure-install-method.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/plugin-symbol-injection-keys.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/plugin-typescript-type-augmentation.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/prefer-local-component-registration.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/prefer-props-emit-over-component-refs.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/prefer-ref-over-reactive.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/prop-boolean-casting-order.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/prop-composable-reactivity-loss.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/prop-defineprops-scope-limitation.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/prop-destructured-watch-getter.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/prop-validation-before-instance.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/props-are-read-only.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/provide-inject-debugging-challenges.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/provide-inject-default-value-factory.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/provide-inject-mutations-in-provider.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/provide-inject-reactivity-not-automatic.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/provide-inject-symbol-keys.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/provide-inject-synchronous-setup.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/reactive-destructuring.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/reactivity-computed-over-watcheffect-mutations.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/reactivity-debugging-hooks.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/reactivity-external-state-integration.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/reactivity-markraw-for-non-reactive.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/reactivity-proxy-identity-hazard.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/reactivity-same-tick-batching.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/ref-value-access.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/refs-in-collections-need-value.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/render-function-avoid-internal-vnode-properties.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/render-function-custom-directives.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/render-function-event-modifiers.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/render-function-functional-components.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/render-function-v-for-keys-required.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/render-function-v-model-implementation.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/render-function-vnodes-must-be-unique.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/rendering-excessive-rerenders-watch-vs-computed.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/rendering-prefer-templates-over-render-functions.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/rendering-render-function-h-import-vue3.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/rendering-render-function-return-from-setup.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/rendering-render-function-slots-as-functions.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/rendering-resolve-component-for-string-names.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/rendering-understand-vdom-block-structure.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/select-initial-value-ios-bug.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/self-referencing-component-name.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/sfc-named-exports-forbidden.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/sfc-recommended-for-build-projects.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/sfc-scoped-css-child-component-styling.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/sfc-scoped-css-dynamic-content.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/sfc-scoped-css-performance.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/sfc-scoped-css-slot-content.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/sfc-script-setup-reactivity.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/sfc-separation-of-concerns-colocate.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/shallow-ref-for-performance.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/slot-conditional-rendering-with-slots.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/slot-define-slots-for-typescript.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/slot-fallback-content-default-values.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/slot-forwarding-to-child-components.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/slot-implicit-default-content.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/slot-name-reserved-prop.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/slot-named-scoped-explicit-default.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/slot-render-scope-parent-only.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/slot-renderless-components-vs-composables.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/slot-v-slot-on-components-or-templates-only.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/ssr-custom-directive-getssrprops.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/ssr-hydration-mismatch-causes.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/ssr-platform-specific-apis.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/state-ssr-cross-request-pollution.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/style-binding-camelcase.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/suspense-events-for-state-tracking.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/suspense-experimental-api-stability.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/suspense-fallback-not-immediate-on-revert.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/suspense-nested-suspensible-prop.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/suspense-nesting-order-with-router.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/suspense-no-builtin-error-handling.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/suspense-revert-only-on-root-change.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/suspense-single-child-requirement.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/suspense-ssr-hydration-issues.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/tailwind-dynamic-class-generation.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/teleport-css-positioning-issues.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/teleport-disabled-for-responsive.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/teleport-logical-hierarchy-preserved.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/teleport-multiple-to-same-target.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/teleport-scoped-styles-limitation.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/teleport-ssr-hydration.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/teleport-target-must-exist.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/template-expressions-restrictions.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/template-functions-no-side-effects.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/template-ref-null-with-v-if.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/template-ref-unwrapping-top-level.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/template-ref-v-for-order.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/textarea-no-interpolation.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-animate-transform-opacity.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-group-flip-inline-elements.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-group-key-requirement.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-group-move-animation-position-absolute.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-group-no-default-wrapper-vue3.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-group-no-mode-prop.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-group-staggered-animations.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-js-hooks-done-callback.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-key-for-same-element.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-mode-out-in.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-nested-duration.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-reusable-scoped-style.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-router-view-appear.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-single-element-slot.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-type-when-mixed.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-unmount-hook-timing.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/ts-component-ref-typeof-instancetype.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/ts-custom-directive-type-augmentation.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/ts-defineemits-type-based-syntax.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/ts-defineprops-boolean-default-false.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/ts-defineprops-imported-types-limitations.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/ts-defineprops-type-based-declaration.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/ts-event-handler-explicit-typing.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/ts-provide-inject-injection-key.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/ts-reactive-no-generic-argument.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/ts-shallowref-for-dynamic-components.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/ts-template-ref-null-handling.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/ts-template-type-casting.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/ts-withdefaults-mutable-factory-function.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/undeclared-emits-double-firing.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/updated-hook-performance.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/use-template-ref-vue35.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/v-else-must-follow-v-if.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/v-for-component-props.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/v-for-computed-reverse-sort.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/v-for-key-attribute.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/v-for-range-starts-at-one.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/v-for-use-computed-for-filtering.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/v-html-xss-security.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/v-if-null-check-order.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/v-if-vs-v-show-performance.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/v-model-ignores-html-attributes.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/v-model-ime-composition.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/v-model-number-modifier-behavior.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/v-model-vue3-breaking-changes.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/v-show-template-limitation.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/watch-async-cleanup.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/watch-async-creation-memory-leak.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/watch-deep-performance.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/watch-deep-same-object-reference.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/watch-flush-timing.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/watch-immediate-option.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/watch-reactive-property-getter.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/watch-vs-watcheffect.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/watcheffect-async-dependency-tracking.md create mode 100644 software/h5-uniapp/.agents/skills/vue-best-practices/reference/watcheffect-flush-post-for-refs.md create mode 100644 software/h5-uniapp/.agents/skills/vue/GENERATION.md create mode 100644 software/h5-uniapp/.agents/skills/vue/LICENSE.md create mode 100644 software/h5-uniapp/.agents/skills/vue/SKILL.md create mode 100644 software/h5-uniapp/.agents/skills/vue/SYNC.md create mode 100644 software/h5-uniapp/.agents/skills/vue/references/advanced-async-suspense.md create mode 100644 software/h5-uniapp/.agents/skills/vue/references/advanced-provide-inject.md create mode 100644 software/h5-uniapp/.agents/skills/vue/references/components-emits.md create mode 100644 software/h5-uniapp/.agents/skills/vue/references/components-lifecycle.md create mode 100644 software/h5-uniapp/.agents/skills/vue/references/components-props.md create mode 100644 software/h5-uniapp/.agents/skills/vue/references/components-slots.md create mode 100644 software/h5-uniapp/.agents/skills/vue/references/components-v-model.md create mode 100644 software/h5-uniapp/.agents/skills/vue/references/core-reactivity.md create mode 100644 software/h5-uniapp/.agents/skills/vue/references/features-composables.md create mode 100644 software/h5-uniapp/.agents/skills/vue/references/features-directives.md create mode 100644 software/h5-uniapp/.agents/skills/vue/references/features-script-setup.md create mode 100644 software/h5-uniapp/.agents/skills/vue/references/features-template-refs.md create mode 100644 software/h5-uniapp/.agents/skills/vue/references/features-typescript.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/LICENSE.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/SKILL.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/SYNC.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/computedAsync.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/computedEager.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/computedInject.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/computedWithControl.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/createEventHook.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/createGenericProjection.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/createGlobalState.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/createInjectionState.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/createProjection.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/createRef.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/createReusableTemplate.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/createSharedComposable.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/createTemplatePromise.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/createUnrefFn.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/extendRef.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/from.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/get.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/injectLocal.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/isDefined.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/logicAnd.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/logicNot.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/logicOr.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/makeDestructurable.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/onClickOutside.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/onElementRemoval.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/onKeyStroke.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/onLongPress.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/onStartTyping.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/provideLocal.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/reactify.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/reactifyObject.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/reactiveComputed.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/reactiveOmit.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/reactivePick.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/refAutoReset.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/refDebounced.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/refDefault.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/refManualReset.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/refThrottled.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/refWithControl.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/set.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/syncRef.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/syncRefs.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/templateRef.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/toObserver.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/toReactive.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/toRef.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/toRefs.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/tryOnBeforeMount.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/tryOnBeforeUnmount.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/tryOnMounted.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/tryOnScopeDispose.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/tryOnUnmounted.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/unrefElement.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/until.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useAbs.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useActiveElement.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useAnimate.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useArrayDifference.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useArrayEvery.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useArrayFilter.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useArrayFind.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useArrayFindIndex.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useArrayFindLast.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useArrayIncludes.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useArrayJoin.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useArrayMap.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useArrayReduce.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useArraySome.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useArrayUnique.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useAsyncQueue.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useAsyncState.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useAsyncValidator.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useAuth.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useAverage.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useAxios.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useBase64.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useBattery.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useBluetooth.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useBreakpoints.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useBroadcastChannel.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useBrowserLocation.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useCached.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useCeil.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useChangeCase.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useClamp.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useClipboard.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useClipboardItems.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useCloned.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useColorMode.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useConfirmDialog.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useCookies.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useCountdown.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useCounter.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useCssVar.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useCurrentElement.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useCycleList.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useDark.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useDateFormat.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useDebounceFn.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useDebouncedRefHistory.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useDeviceMotion.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useDeviceOrientation.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useDevicePixelRatio.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useDevicesList.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useDisplayMedia.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useDocumentVisibility.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useDraggable.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useDrauu.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useDropZone.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useElementBounding.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useElementByPoint.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useElementHover.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useElementSize.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useElementVisibility.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useEventBus.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useEventListener.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useEventSource.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useExtractedObservable.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useEyeDropper.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useFavicon.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useFetch.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useFileDialog.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useFileSystemAccess.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useFirestore.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useFloor.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useFocus.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useFocusTrap.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useFocusWithin.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useFps.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useFullscreen.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useFuse.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useGamepad.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useGeolocation.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useIDBKeyval.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useIdle.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useImage.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useInfiniteScroll.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useIntersectionObserver.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useInterval.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useIntervalFn.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useIpcRenderer.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useIpcRendererInvoke.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useIpcRendererOn.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useJwt.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useKeyModifier.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useLastChanged.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useLocalStorage.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useMagicKeys.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useManualRefHistory.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useMath.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useMax.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useMediaControls.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useMediaQuery.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useMemoize.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useMemory.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useMin.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useMounted.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useMouse.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useMouseInElement.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useMousePressed.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useMutationObserver.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useNProgress.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useNavigatorLanguage.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useNetwork.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useNow.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useObjectUrl.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useObservable.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useOffsetPagination.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useOnline.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/usePageLeave.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useParallax.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useParentElement.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/usePerformanceObserver.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/usePermission.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/usePointer.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/usePointerLock.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/usePointerSwipe.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/usePrecision.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/usePreferredColorScheme.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/usePreferredContrast.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/usePreferredDark.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/usePreferredLanguages.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/usePreferredReducedMotion.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/usePreferredReducedTransparency.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/usePrevious.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useProjection.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useQRCode.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useRTDB.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useRafFn.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useRefHistory.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useResizeObserver.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useRound.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useRouteHash.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useRouteParams.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useRouteQuery.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useSSRWidth.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useScreenOrientation.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useScreenSafeArea.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useScriptTag.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useScroll.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useScrollLock.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useSessionStorage.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useShare.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useSortable.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useSorted.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useSpeechRecognition.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useSpeechSynthesis.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useStepper.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useStorage.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useStorageAsync.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useStyleTag.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useSubject.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useSubscription.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useSum.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useSupported.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useSwipe.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useTemplateRefsList.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useTextDirection.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useTextSelection.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useTextareaAutosize.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useThrottleFn.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useThrottledRefHistory.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useTimeAgo.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useTimeAgoIntl.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useTimeout.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useTimeoutFn.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useTimeoutPoll.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useTimestamp.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useTitle.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useToNumber.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useToString.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useToggle.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useTransition.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useTrunc.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useUrlSearchParams.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useUserMedia.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useVModel.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useVModels.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useVibrate.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useVirtualList.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useWakeLock.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useWebNotification.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useWebSocket.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useWebWorker.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useWebWorkerFn.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useWindowFocus.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useWindowScroll.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useWindowSize.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useZoomFactor.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/useZoomLevel.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/watchArray.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/watchAtMost.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/watchDebounced.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/watchDeep.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/watchExtractedObservable.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/watchIgnorable.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/watchImmediate.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/watchOnce.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/watchPausable.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/watchThrottled.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/watchTriggerable.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/watchWithFilter.md create mode 100644 software/h5-uniapp/.agents/skills/vueuse-functions/references/whenever.md create mode 100644 software/h5-uniapp/.agents/skills/web-design-guidelines/SKILL.md create mode 100644 software/h5-uniapp/.agents/skills/web-design-guidelines/SYNC.md create mode 100644 software/h5-uniapp/agent/skills/pinia/GENERATION.md create mode 100644 software/h5-uniapp/agent/skills/pinia/LICENSE.md create mode 100644 software/h5-uniapp/agent/skills/pinia/SKILL.md create mode 100644 software/h5-uniapp/agent/skills/pinia/SYNC.md create mode 100644 software/h5-uniapp/agent/skills/pinia/references/advanced-hmr.md create mode 100644 software/h5-uniapp/agent/skills/pinia/references/advanced-nuxt.md create mode 100644 software/h5-uniapp/agent/skills/pinia/references/advanced-ssr.md create mode 100644 software/h5-uniapp/agent/skills/pinia/references/best-practices-outside-component.md create mode 100644 software/h5-uniapp/agent/skills/pinia/references/best-practices-testing.md create mode 100644 software/h5-uniapp/agent/skills/pinia/references/core-stores.md create mode 100644 software/h5-uniapp/agent/skills/pinia/references/features-composables.md create mode 100644 software/h5-uniapp/agent/skills/pinia/references/features-composing-stores.md create mode 100644 software/h5-uniapp/agent/skills/pinia/references/features-plugins.md create mode 100644 software/h5-uniapp/agent/skills/uni-app/GENERATION.md create mode 100644 software/h5-uniapp/agent/skills/uni-app/SKILL.md create mode 100644 software/h5-uniapp/agent/skills/uni-app/references/config-manifest.md create mode 100644 software/h5-uniapp/agent/skills/uni-app/references/config-pages.md create mode 100644 software/h5-uniapp/agent/skills/uni-app/references/core-form-components.md create mode 100644 software/h5-uniapp/agent/skills/uni-app/references/core-framework.md create mode 100644 software/h5-uniapp/agent/skills/uni-app/references/core-view-components.md create mode 100644 software/h5-uniapp/agent/skills/uni-app/references/feature-file-operations.md create mode 100644 software/h5-uniapp/agent/skills/uni-app/references/feature-lifecycle.md create mode 100644 software/h5-uniapp/agent/skills/uni-app/references/feature-location.md create mode 100644 software/h5-uniapp/agent/skills/uni-app/references/feature-media-components.md create mode 100644 software/h5-uniapp/agent/skills/uni-app/references/feature-navigation.md create mode 100644 software/h5-uniapp/agent/skills/uni-app/references/feature-network.md create mode 100644 software/h5-uniapp/agent/skills/uni-app/references/feature-storage.md create mode 100644 software/h5-uniapp/agent/skills/uni-app/references/feature-system-info.md create mode 100644 software/h5-uniapp/agent/skills/uni-app/references/feature-ui-feedback.md create mode 100644 software/h5-uniapp/agent/skills/uni-helper/GENERATION.md create mode 100644 software/h5-uniapp/agent/skills/uni-helper/SKILL.md create mode 100644 software/h5-uniapp/agent/skills/uni-helper/references/lib-uni-network.md create mode 100644 software/h5-uniapp/agent/skills/uni-helper/references/lib-uni-promises.md create mode 100644 software/h5-uniapp/agent/skills/uni-helper/references/lib-uni-typed.md create mode 100644 software/h5-uniapp/agent/skills/uni-helper/references/lib-uni-use.md create mode 100644 software/h5-uniapp/agent/skills/uni-helper/references/plugin-components.md create mode 100644 software/h5-uniapp/agent/skills/uni-helper/references/plugin-layouts.md create mode 100644 software/h5-uniapp/agent/skills/uni-helper/references/plugin-manifest.md create mode 100644 software/h5-uniapp/agent/skills/uni-helper/references/plugin-middleware.md create mode 100644 software/h5-uniapp/agent/skills/uni-helper/references/plugin-pages.md create mode 100644 software/h5-uniapp/agent/skills/uni-helper/references/plugin-platform-modifier.md create mode 100644 software/h5-uniapp/agent/skills/uni-helper/references/plugin-platform.md create mode 100644 software/h5-uniapp/agent/skills/uni-helper/references/starter-create-uni.md create mode 100644 software/h5-uniapp/agent/skills/uni-helper/references/starter-vitesse.md create mode 100644 software/h5-uniapp/agent/skills/uni-helper/references/util-uni-env.md create mode 100644 software/h5-uniapp/agent/skills/uni-helper/references/util-unocss-preset.md create mode 100644 software/h5-uniapp/agent/skills/unocss/GENERATION.md create mode 100644 software/h5-uniapp/agent/skills/unocss/LICENSE.md create mode 100644 software/h5-uniapp/agent/skills/unocss/SKILL.md create mode 100644 software/h5-uniapp/agent/skills/unocss/SYNC.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/core-config.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/core-extracting.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/core-layers.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/core-rules.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/core-safelist.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/core-shortcuts.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/core-theme.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/core-variants.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/integrations-nuxt.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/integrations-vite.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/preset-attributify.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/preset-icons.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/preset-mini.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/preset-rem-to-px.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/preset-tagify.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/preset-typography.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/preset-web-fonts.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/preset-wind3.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/preset-wind4.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/transformer-attributify-jsx.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/transformer-compile-class.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/transformer-directives.md create mode 100644 software/h5-uniapp/agent/skills/unocss/references/transformer-variant-group.md create mode 100644 software/h5-uniapp/agent/skills/vite/GENERATION.md create mode 100644 software/h5-uniapp/agent/skills/vite/LICENSE.md create mode 100644 software/h5-uniapp/agent/skills/vite/SKILL.md create mode 100644 software/h5-uniapp/agent/skills/vite/SYNC.md create mode 100644 software/h5-uniapp/agent/skills/vite/references/advanced-api.md create mode 100644 software/h5-uniapp/agent/skills/vite/references/advanced-backend.md create mode 100644 software/h5-uniapp/agent/skills/vite/references/advanced-performance.md create mode 100644 software/h5-uniapp/agent/skills/vite/references/advanced-plugin-api.md create mode 100644 software/h5-uniapp/agent/skills/vite/references/build-library.md create mode 100644 software/h5-uniapp/agent/skills/vite/references/build-production.md create mode 100644 software/h5-uniapp/agent/skills/vite/references/build-ssr.md create mode 100644 software/h5-uniapp/agent/skills/vite/references/core-cli.md create mode 100644 software/h5-uniapp/agent/skills/vite/references/core-config.md create mode 100644 software/h5-uniapp/agent/skills/vite/references/core-features.md create mode 100644 software/h5-uniapp/agent/skills/vite/references/core-plugins.md create mode 100644 software/h5-uniapp/agent/skills/vite/references/features-assets.md create mode 100644 software/h5-uniapp/agent/skills/vite/references/features-css.md create mode 100644 software/h5-uniapp/agent/skills/vite/references/features-dep-bundling.md create mode 100644 software/h5-uniapp/agent/skills/vite/references/features-env.md create mode 100644 software/h5-uniapp/agent/skills/vite/references/features-glob-import.md create mode 100644 software/h5-uniapp/agent/skills/vite/references/features-hmr.md create mode 100644 software/h5-uniapp/agent/skills/vite/references/features-workers.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/LICENSE.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/SKILL.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/SYNC.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/animation-class-based-technique.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/animation-key-for-rerender.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/animation-state-driven-technique.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/animation-transitiongroup-performance.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/async-component-error-handling.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/async-component-hydration-strategies.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/async-component-keepalive-ref-issue.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/async-component-loading-delay.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/async-component-suspense-control.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/async-component-vue-router.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/attrs-event-listener-merging.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/attrs-hyphenated-property-access.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/attrs-not-reactive.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/avoid-prop-drilling-use-provide-inject.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/checkbox-true-false-value-form-submission.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/cleanup-side-effects.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/click-events-on-components.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/component-events-dont-bubble.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/component-naming-conflicts.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/component-naming-pascalcase.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/component-ref-requires-defineexpose.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/composable-avoid-hidden-side-effects.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/composable-call-location-restrictions.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/composable-composition-pattern.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/composable-naming-return-pattern.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/composable-options-object-pattern.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/composable-readonly-state.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/composable-tovalue-inside-watcheffect.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/composable-vs-utility-functions.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/composition-api-bundle-size-minification.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/composition-api-code-organization.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/composition-api-mixins-replacement.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/composition-api-not-functional-programming.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/composition-api-options-api-coexistence.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/composition-api-script-setup-async-context.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/composition-api-vs-react-hooks-differences.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/computed-array-mutation.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/computed-conditional-dependencies.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/computed-no-parameters.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/computed-no-side-effects.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/computed-properties-for-class-logic.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/computed-return-value-readonly.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/computed-vs-methods-caching.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/configure-app-before-mount.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/declare-emits-for-documentation.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/define-expose-before-await.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/define-model-default-value-sync.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/defineEmits-must-be-top-level.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/defineEmits-no-runtime-and-type-mixed.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/definemodel-hidden-modifier-props.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/definemodel-object-mutation-no-emit.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/definemodel-value-next-tick.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/directive-arguments-read-only.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/directive-avoid-on-components.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/directive-cleanup-in-unmounted.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/directive-function-shorthand.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/directive-naming-v-prefix.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/directive-prefer-declarative-templating.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/directive-vs-component-decision.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/directive-vue2-migration-hooks.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/dom-update-timing-nexttick.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/dynamic-argument-constraints.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/dynamic-component-registration-vite.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/dynamic-components-with-keepalive.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/emit-kebab-case-in-templates.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/emit-validation-for-complex-payloads.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/event-modifier-order-matters.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/event-once-modifier-for-single-use.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/exact-modifier-for-precise-shortcuts.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/fallthrough-attrs-overwrite-vue3.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/in-dom-template-parsing-caveats.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/inheritattrs-false-for-wrapper-components.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/keepalive-component-name-requirement.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/keepalive-memory-management.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/keepalive-no-cache-removal-vue3.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/keepalive-router-fresh-vs-cached.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/keepalive-router-nested-double-mount.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/keepalive-transition-memory-leak.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/keyup-modifier-timing.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/lifecycle-dom-access-timing.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/lifecycle-hooks-synchronous-registration.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/lifecycle-ssr-awareness.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/local-components-not-in-descendants.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/mount-return-value.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/mouse-button-modifiers-intent.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/multi-root-component-class-attrs.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/multiple-app-instances.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/native-event-collision-with-emits.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/no-passive-with-prevent.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/no-v-if-with-v-for.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/perf-avoid-component-abstraction-in-lists.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/perf-computed-object-stability.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/perf-props-stability-update-optimization.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/perf-ssr-ssg-for-page-load.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/perf-v-once-v-memo-directives.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/perf-virtualize-large-lists.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/plugin-global-properties-sparingly.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/plugin-install-before-mount.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/plugin-prefer-provide-inject-over-global-properties.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/plugin-structure-install-method.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/plugin-symbol-injection-keys.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/plugin-typescript-type-augmentation.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/prefer-local-component-registration.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/prefer-props-emit-over-component-refs.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/prefer-ref-over-reactive.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/prop-boolean-casting-order.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/prop-composable-reactivity-loss.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/prop-defineprops-scope-limitation.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/prop-destructured-watch-getter.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/prop-validation-before-instance.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/props-are-read-only.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/provide-inject-debugging-challenges.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/provide-inject-default-value-factory.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/provide-inject-mutations-in-provider.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/provide-inject-reactivity-not-automatic.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/provide-inject-symbol-keys.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/provide-inject-synchronous-setup.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/reactive-destructuring.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/reactivity-computed-over-watcheffect-mutations.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/reactivity-debugging-hooks.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/reactivity-external-state-integration.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/reactivity-markraw-for-non-reactive.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/reactivity-proxy-identity-hazard.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/reactivity-same-tick-batching.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/ref-value-access.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/refs-in-collections-need-value.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/render-function-avoid-internal-vnode-properties.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/render-function-custom-directives.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/render-function-event-modifiers.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/render-function-functional-components.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/render-function-v-for-keys-required.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/render-function-v-model-implementation.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/render-function-vnodes-must-be-unique.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/rendering-excessive-rerenders-watch-vs-computed.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/rendering-prefer-templates-over-render-functions.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/rendering-render-function-h-import-vue3.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/rendering-render-function-return-from-setup.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/rendering-render-function-slots-as-functions.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/rendering-resolve-component-for-string-names.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/rendering-understand-vdom-block-structure.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/select-initial-value-ios-bug.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/self-referencing-component-name.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/sfc-named-exports-forbidden.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/sfc-recommended-for-build-projects.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/sfc-scoped-css-child-component-styling.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/sfc-scoped-css-dynamic-content.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/sfc-scoped-css-performance.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/sfc-scoped-css-slot-content.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/sfc-script-setup-reactivity.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/sfc-separation-of-concerns-colocate.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/shallow-ref-for-performance.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/slot-conditional-rendering-with-slots.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/slot-define-slots-for-typescript.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/slot-fallback-content-default-values.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/slot-forwarding-to-child-components.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/slot-implicit-default-content.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/slot-name-reserved-prop.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/slot-named-scoped-explicit-default.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/slot-render-scope-parent-only.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/slot-renderless-components-vs-composables.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/slot-v-slot-on-components-or-templates-only.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/ssr-custom-directive-getssrprops.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/ssr-hydration-mismatch-causes.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/ssr-platform-specific-apis.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/state-ssr-cross-request-pollution.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/style-binding-camelcase.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/suspense-events-for-state-tracking.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/suspense-experimental-api-stability.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/suspense-fallback-not-immediate-on-revert.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/suspense-nested-suspensible-prop.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/suspense-nesting-order-with-router.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/suspense-no-builtin-error-handling.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/suspense-revert-only-on-root-change.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/suspense-single-child-requirement.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/suspense-ssr-hydration-issues.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/tailwind-dynamic-class-generation.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/teleport-css-positioning-issues.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/teleport-disabled-for-responsive.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/teleport-logical-hierarchy-preserved.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/teleport-multiple-to-same-target.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/teleport-scoped-styles-limitation.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/teleport-ssr-hydration.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/teleport-target-must-exist.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/template-expressions-restrictions.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/template-functions-no-side-effects.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/template-ref-null-with-v-if.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/template-ref-unwrapping-top-level.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/template-ref-v-for-order.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/textarea-no-interpolation.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/transition-animate-transform-opacity.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/transition-group-flip-inline-elements.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/transition-group-key-requirement.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/transition-group-move-animation-position-absolute.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/transition-group-no-default-wrapper-vue3.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/transition-group-no-mode-prop.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/transition-group-staggered-animations.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/transition-js-hooks-done-callback.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/transition-key-for-same-element.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/transition-mode-out-in.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/transition-nested-duration.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/transition-reusable-scoped-style.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/transition-router-view-appear.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/transition-single-element-slot.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/transition-type-when-mixed.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/transition-unmount-hook-timing.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/ts-component-ref-typeof-instancetype.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/ts-custom-directive-type-augmentation.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/ts-defineemits-type-based-syntax.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/ts-defineprops-boolean-default-false.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/ts-defineprops-imported-types-limitations.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/ts-defineprops-type-based-declaration.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/ts-event-handler-explicit-typing.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/ts-provide-inject-injection-key.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/ts-reactive-no-generic-argument.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/ts-shallowref-for-dynamic-components.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/ts-template-ref-null-handling.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/ts-template-type-casting.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/ts-withdefaults-mutable-factory-function.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/undeclared-emits-double-firing.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/updated-hook-performance.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/use-template-ref-vue35.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/v-else-must-follow-v-if.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/v-for-component-props.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/v-for-computed-reverse-sort.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/v-for-key-attribute.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/v-for-range-starts-at-one.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/v-for-use-computed-for-filtering.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/v-html-xss-security.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/v-if-null-check-order.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/v-if-vs-v-show-performance.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/v-model-ignores-html-attributes.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/v-model-ime-composition.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/v-model-number-modifier-behavior.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/v-model-vue3-breaking-changes.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/v-show-template-limitation.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/watch-async-cleanup.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/watch-async-creation-memory-leak.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/watch-deep-performance.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/watch-deep-same-object-reference.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/watch-flush-timing.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/watch-immediate-option.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/watch-reactive-property-getter.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/watch-vs-watcheffect.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/watcheffect-async-dependency-tracking.md create mode 100644 software/h5-uniapp/agent/skills/vue-best-practices/reference/watcheffect-flush-post-for-refs.md create mode 100644 software/h5-uniapp/agent/skills/vue/GENERATION.md create mode 100644 software/h5-uniapp/agent/skills/vue/LICENSE.md create mode 100644 software/h5-uniapp/agent/skills/vue/SKILL.md create mode 100644 software/h5-uniapp/agent/skills/vue/SYNC.md create mode 100644 software/h5-uniapp/agent/skills/vue/references/advanced-async-suspense.md create mode 100644 software/h5-uniapp/agent/skills/vue/references/advanced-provide-inject.md create mode 100644 software/h5-uniapp/agent/skills/vue/references/components-emits.md create mode 100644 software/h5-uniapp/agent/skills/vue/references/components-lifecycle.md create mode 100644 software/h5-uniapp/agent/skills/vue/references/components-props.md create mode 100644 software/h5-uniapp/agent/skills/vue/references/components-slots.md create mode 100644 software/h5-uniapp/agent/skills/vue/references/components-v-model.md create mode 100644 software/h5-uniapp/agent/skills/vue/references/core-reactivity.md create mode 100644 software/h5-uniapp/agent/skills/vue/references/features-composables.md create mode 100644 software/h5-uniapp/agent/skills/vue/references/features-directives.md create mode 100644 software/h5-uniapp/agent/skills/vue/references/features-script-setup.md create mode 100644 software/h5-uniapp/agent/skills/vue/references/features-template-refs.md create mode 100644 software/h5-uniapp/agent/skills/vue/references/features-typescript.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/LICENSE.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/SKILL.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/SYNC.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/computedAsync.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/computedEager.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/computedInject.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/computedWithControl.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/createEventHook.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/createGenericProjection.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/createGlobalState.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/createInjectionState.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/createProjection.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/createRef.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/createReusableTemplate.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/createSharedComposable.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/createTemplatePromise.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/createUnrefFn.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/extendRef.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/from.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/get.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/injectLocal.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/isDefined.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/logicAnd.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/logicNot.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/logicOr.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/makeDestructurable.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/onClickOutside.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/onElementRemoval.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/onKeyStroke.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/onLongPress.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/onStartTyping.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/provideLocal.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/reactify.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/reactifyObject.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/reactiveComputed.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/reactiveOmit.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/reactivePick.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/refAutoReset.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/refDebounced.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/refDefault.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/refManualReset.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/refThrottled.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/refWithControl.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/set.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/syncRef.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/syncRefs.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/templateRef.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/toObserver.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/toReactive.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/toRef.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/toRefs.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/tryOnBeforeMount.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/tryOnBeforeUnmount.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/tryOnMounted.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/tryOnScopeDispose.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/tryOnUnmounted.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/unrefElement.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/until.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useAbs.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useActiveElement.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useAnimate.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useArrayDifference.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useArrayEvery.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useArrayFilter.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useArrayFind.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useArrayFindIndex.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useArrayFindLast.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useArrayIncludes.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useArrayJoin.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useArrayMap.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useArrayReduce.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useArraySome.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useArrayUnique.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useAsyncQueue.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useAsyncState.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useAsyncValidator.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useAuth.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useAverage.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useAxios.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useBase64.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useBattery.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useBluetooth.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useBreakpoints.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useBroadcastChannel.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useBrowserLocation.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useCached.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useCeil.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useChangeCase.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useClamp.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useClipboard.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useClipboardItems.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useCloned.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useColorMode.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useConfirmDialog.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useCookies.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useCountdown.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useCounter.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useCssVar.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useCurrentElement.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useCycleList.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useDark.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useDateFormat.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useDebounceFn.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useDebouncedRefHistory.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useDeviceMotion.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useDeviceOrientation.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useDevicePixelRatio.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useDevicesList.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useDisplayMedia.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useDocumentVisibility.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useDraggable.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useDrauu.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useDropZone.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useElementBounding.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useElementByPoint.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useElementHover.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useElementSize.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useElementVisibility.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useEventBus.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useEventListener.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useEventSource.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useExtractedObservable.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useEyeDropper.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useFavicon.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useFetch.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useFileDialog.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useFileSystemAccess.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useFirestore.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useFloor.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useFocus.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useFocusTrap.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useFocusWithin.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useFps.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useFullscreen.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useFuse.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useGamepad.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useGeolocation.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useIDBKeyval.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useIdle.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useImage.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useInfiniteScroll.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useIntersectionObserver.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useInterval.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useIntervalFn.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useIpcRenderer.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useIpcRendererInvoke.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useIpcRendererOn.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useJwt.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useKeyModifier.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useLastChanged.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useLocalStorage.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useMagicKeys.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useManualRefHistory.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useMath.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useMax.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useMediaControls.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useMediaQuery.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useMemoize.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useMemory.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useMin.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useMounted.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useMouse.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useMouseInElement.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useMousePressed.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useMutationObserver.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useNProgress.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useNavigatorLanguage.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useNetwork.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useNow.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useObjectUrl.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useObservable.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useOffsetPagination.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useOnline.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/usePageLeave.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useParallax.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useParentElement.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/usePerformanceObserver.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/usePermission.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/usePointer.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/usePointerLock.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/usePointerSwipe.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/usePrecision.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/usePreferredColorScheme.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/usePreferredContrast.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/usePreferredDark.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/usePreferredLanguages.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/usePreferredReducedMotion.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/usePreferredReducedTransparency.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/usePrevious.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useProjection.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useQRCode.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useRTDB.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useRafFn.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useRefHistory.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useResizeObserver.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useRound.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useRouteHash.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useRouteParams.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useRouteQuery.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useSSRWidth.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useScreenOrientation.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useScreenSafeArea.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useScriptTag.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useScroll.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useScrollLock.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useSessionStorage.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useShare.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useSortable.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useSorted.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useSpeechRecognition.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useSpeechSynthesis.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useStepper.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useStorage.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useStorageAsync.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useStyleTag.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useSubject.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useSubscription.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useSum.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useSupported.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useSwipe.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useTemplateRefsList.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useTextDirection.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useTextSelection.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useTextareaAutosize.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useThrottleFn.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useThrottledRefHistory.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useTimeAgo.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useTimeAgoIntl.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useTimeout.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useTimeoutFn.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useTimeoutPoll.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useTimestamp.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useTitle.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useToNumber.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useToString.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useToggle.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useTransition.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useTrunc.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useUrlSearchParams.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useUserMedia.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useVModel.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useVModels.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useVibrate.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useVirtualList.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useWakeLock.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useWebNotification.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useWebSocket.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useWebWorker.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useWebWorkerFn.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useWindowFocus.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useWindowScroll.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useWindowSize.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useZoomFactor.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/useZoomLevel.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/watchArray.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/watchAtMost.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/watchDebounced.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/watchDeep.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/watchExtractedObservable.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/watchIgnorable.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/watchImmediate.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/watchOnce.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/watchPausable.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/watchThrottled.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/watchTriggerable.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/watchWithFilter.md create mode 100644 software/h5-uniapp/agent/skills/vueuse-functions/references/whenever.md create mode 100644 software/h5-uniapp/agent/skills/web-design-guidelines/SKILL.md create mode 100644 software/h5-uniapp/agent/skills/web-design-guidelines/SYNC.md create mode 100644 software/h5-uniapp/skills-lock.json create mode 100644 software/h5-uniapp/src/pages/orders/index.vue delete mode 100644 software/h5-uniapp/src/static/tabbar/device-active.png delete mode 100644 software/h5-uniapp/src/static/tabbar/device.png create mode 100644 software/h5-uniapp/src/static/tabbar/home.svg create mode 100644 software/h5-uniapp/src/static/tabbar/notebook-tabs.svg create mode 100644 software/h5-uniapp/src/static/tabbar/orders-active.png create mode 100644 software/h5-uniapp/src/static/tabbar/orders.png create mode 100644 software/h5-uniapp/src/static/tabbar/user.svg create mode 100644 software/h5-uniapp/src/static/tabbar/zap.svg diff --git a/AGENTS.md b/AGENTS.md index b6b06d9..a6e9ba3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -142,21 +142,15 @@ python tools/db.py import data.sql - **禁止在服务器上编译Rust** — 2核/3.5G会OOM,本地编译传二进制 - **禁止使用socat fork模式** — 进程泄漏会耗尽内存,用nginx stream代理 - -### 编码规范 - -- 任务需求写到 `tasks/` 目录的md文件 -- 通过Agent子代理包裹opencode执行,异步通知 -- 任务要拆的尽量细 -- 质量约束模板固定追加到每个任务文件 -- opencode超时必须报告用户,不能fallback手动执行 -- 审查用不同模型(不用开发同款) +- **禁止提交密码/密钥到仓库** — 使用环境变量或本地配置文件(已gitignore) ### 编码任务下发 - 任务需求写到 `tasks/` 目录的md文件 - 通过Agent子代理包裹opencode执行,异步通知 - 质量约束模板固定追加到每个任务文件 +- opencode超时必须报告用户,不能fallback手动执行 +- 审查用不同模型(不用开发同款) ### 质量约束模板 @@ -176,6 +170,278 @@ python tools/db.py import data.sql 2. 跑起来看效果 3. 抽查关键逻辑(异常处理、权限校验、协议解析) +--- + +## Rust 编码规范 + +### 错误处理 + +- 使用 `thiserror` 定义业务错误枚举,`AppError` 统一包装 +- 所有 `sqlx` / `redis` 调用必须 `.map_err(AppError::Database)?` +- 网络请求/外部IO 必须 `match` 或 `if let` 处理,禁止裸 `.unwrap()` +- 关键业务路径用 `tracing::error!` 记录错误上下文 + +### Async 模式 + +- 使用 `tokio` 运行时,数据库/Redis 操作用 `.await` +- 长时间任务用 `tokio::spawn` 异步处理,不阻塞主循环 +- Redis 队列消费:`blpop` 阻塞读取 + `tokio::spawn` 并发处理 + +### 模块划分 + +``` +src/ +├── routes/ # HTTP 路由处理器(按业务拆分) +├── middleware/ # 中间件(认证、日志、CORS) +├── db/ # 数据库连接池、迁移 +├── commands/ # 设备指令下发逻辑 +├── nodes/ # 设备服务节点管理 +├── workers/ # 后台 Worker(report/reply) +└── error.rs # 统一错误定义 +``` + +### 命名约定 + +- 路由处理器:`动词 + 名词`(如 `get_cabinets`、`create_organization`) +- 数据库查询结构体:`XxxRow`(如 `CabinetBasicRow`) +- 请求体:`XxxBody` 或 `XxxQuery`(如 `CreateCabinetBody`) +- 常量:`UPPER_SNAKE_CASE`(如 `JWT_SECRET_ENV`) + +--- + +## React 编码规范 + +### 组件规范 + +- 纯函数组件 + Hooks,禁止 Class 组件 +- 组件文件与路由页面一一对应,放 `src/pages/` 下 +- 复用组件放 `src/components/`,按功能命名 +- Props 接口定义在组件文件顶部,用 `interface` 声明 + +### 状态管理 + +- 页面级状态用 `useState` / `useEffect` +- 全局状态用 `zustand`(如用户信息、权限) +- 服务端数据用 `@tanstack/react-query`,配置缓存策略 + +### 样式规范 + +- 使用 Tailwind CSS,禁止内联 style(除动态计算值) +- shadcn/ui 组件优先,不重复造轮子 +- 表格列统一 `min-width` + `align="center"`,无 `fixed="right"` +- 分页:上下双分页,`page-sizes="[10,20,30,50]"` + +### 图标映射 + +| 操作 | 图标 | +|------|------| +| 编辑 | Edit | +| 删除 | Delete | +| 查看 | ViewIcon | +| 搜索 | Search | +| 重置 | Refresh | +| 新增 | Plus(保留文字) | + +--- + +## API 设计规范 + +### 响应格式 + +```json +{ + "code": 0, // 0=成功,非0=失败 + "data": { ... }, // 业务数据 + "message": "ok" // 提示信息 +} +``` + +### 错误码 + +| code | 含义 | +|------|------| +| 0 | 成功 | +| 400 | 请求参数错误 | +| 401 | 未认证(token缺失/无效) | +| 403 | 无权限 | +| 404 | 资源不存在 | +| 500 | 服务器内部错误 | + +### 分页约定 + +```json +{ + "code": 0, + "data": { + "list": [...], + "total": 100, + "page": 1, + "page_size": 20 + } +} +``` + +### RESTful 约定 + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | /api/xxx | 列表查询 | +| GET | /api/xxx/:id | 详情 | +| POST | /api/xxx | 创建 | +| PUT | /api/xxx/:id | 更新 | +| DELETE | /api/xxx/:id | 删除 | + +--- + +## Git 工作流 + +### 分支策略 + +- `main` — 正式环境,只接受 PR 合并 +- `develop` — 开发分支,日常开发在此分支 +- `feature/*` — 功能分支,从 develop 拉出,完成后合并回 develop +- `fix/*` — 修复分支,从 main 或 develop 拉出 + +### Commit 规范 + +格式:`(): ` + +| type | 说明 | +|------|------| +| feat | 新功能 | +| fix | 修复 bug | +| docs | 文档变更 | +| style | 代码格式(不影响逻辑) | +| refactor | 重构(不新增功能/修复bug) | +| test | 测试相关 | +| chore | 构建/工具变更 | + +示例: +- `feat(cabinets): 添加设备RSSI信号显示` +- `fix(h5): 修复登录后token未持久化问题` +- `docs(api): 更新充电记录接口文档` + +--- + +## 测试标准 + +### 测试类型 + +| 类型 | 工具 | 要求 | +|------|------|------| +| 单元测试 | Rust `#[test]` / Vitest | 核心业务逻辑必须覆盖 | +| 集成测试 | API 请求验证 | 关键接口(登录/充电/开门)必须验证 | +| 静态校验 | `cargo check` / `tsc --noEmit` | 零报错零警告 | + +### 测试覆盖率 + +- 核心业务逻辑(充电控制、指令下发、权限校验):≥80% +- 数据库查询函数:≥60% +- 工具函数:≥90% + +### 测试命名 + +``` +test_<被测函数>_<场景>_<期望结果> + +test_verify_password_correct_returns_true +test_verify_password_wrong_returns_false +test_check_permission_admin_returns_ok +test_check_permission_user_returns_forbidden +``` + +--- + +## 安全规范 + +### 认证授权 + +- JWT Token 有效期:7天,过期需重新登录 +- 密码存储:argon2 哈希,禁止明文或 MD5/SHA1 +- RBAC 权限码:精确到按钮级别(如 `device:door:unlock`) +- 敏感操作(删除/禁用/开门)需二次确认 + +### 输入校验 + +- 所有 API 入参必须校验(长度、格式、范围) +- SQL 查询使用参数化查询,禁止字符串拼接 +- IMEI 校验:必须15位数字 +- 手机号校验:11位数字,正则 `^1[3-9]\d{9}$` + +### 数据安全 + +- 生产数据库密码不入代码,使用环境变量 +- Redis 密码同上 +- 日志禁止输出密码/Token/密钥 +- 敏感字段(密码、auth_str)在序列化时跳过 + +--- + +## 日志规范 + +### 日志级别 + +| 级别 | 用途 | 示例 | +|------|------|------| +| `ERROR` | 系统异常、业务错误 | 数据库连接失败、指令下发超时 | +| `WARN` | 潜在问题、可恢复异常 | 设备离线、重试成功 | +| `INFO` | 关键业务事件 | 设备登录、充电开始/结束、用户操作 | +| `DEBUG` | 调试信息 | API 请求参数、SQL 执行时间 | + +### 日志格式 + +```rust +// 使用 tracing crate +tracing::info!(imei = %imei, board_id = board_id, "设备登录成功"); +tracing::error!(err = %e, "数据库查询失败"); +tracing::warn!(compartment_id = id, "仓体状态异常"); +``` + +### 关键操作日志 + +以下操作必须记录: + +| 操作 | 日志内容 | +|------|----------| +| 用户登录/登出 | 用户ID、手机号、IP | +| 设备指令下发 | IMEI、指令类型、仓体ID | +| 充电开始/结束 | 仓体ID、时长、电量 | +| 组织/项目变更 | 操作类型、ID、名称 | +| 权限变更 | 用户ID、权限码、操作人 | +| 数据删除 | 表名、ID、操作人 | + +--- + +## 性能规范 + +### 数据库 + +- 查询必须走索引,禁止全表扫描(`EXPLAIN` 验证) +- 分页查询:避免深分页,用游标分页(`WHERE id > ?`) +- 批量操作:用 `INSERT ... VALUES (...), (...)` 而非逐条插入 +- 连接池:MySQL 最大连接数 ≤ 50,Redis 连接数 ≤ 20 + +### Redis + +- 缓存 key 规范:`{业务}:{标识}`(如 `device:{imei}`) +- TTL 设置:在线状态 300秒,缓存数据按业务设定 +- 避免大 Key:单个 Hash ≤ 500 字段,List ≤ 1000 元素 +- 批量操作:用 Pipeline 减少往返次数 + +### API 响应 + +- 列表接口响应时间 ≤ 500ms +- 详情接口响应时间 ≤ 200ms +- 批量操作接口响应时间 ≤ 1s +- 响应体大小:列表 ≤ 1MB,详情 ≤ 100KB + +### 前端 + +- 首屏加载 ≤ 3s(3G网络) +- 交互响应 ≤ 100ms +- 列表滚动:虚拟滚动(≥100条数据) +- 图片:WebP 格式,懒加载 + ## 部署与维护 ### SSH连接 @@ -190,19 +456,48 @@ echo y | plink -ssh root@10.8.0.252 -pw "Hbhyg731024@" ### 数据库连接 +所有数据库操作通过 ZeroTier 直接连接 `10.8.0.252`,无需 SSH 隧道到服务器。 + +**MySQL(通过 ZeroTier):** ```bash -# MySQL(通过ZeroTier代理) +# 命令行 mysql -h 10.8.0.252 -u root -p # 密码:Hbhyg731024@ # 测试环境库:pms_dev # 正式环境库:pms - -# Redis(通过ZeroTier代理) -redis-cli -h 10.8.0.252 -a "Hbhyg731024@" -# 测试环境:db6 -# 正式环境:db5 ``` +```python +# Python(Agent 常用方式) +import pymysql +conn = pymysql.connect(host='10.8.0.252', user='root', password='Hbhyg731024@', database='pms') # 正式环境 +conn = pymysql.connect(host='10.8.0.252', user='root', password='Hbhyg731024@', database='pms_dev') # 测试环境 +``` + +**Redis(通过 ZeroTier):** +```bash +# 命令行 +redis-cli -h 10.8.0.252 -a "Hbhyg731024@" +# 测试环境:select 6 +# 正式环境:select 5 +``` + +```python +# Python(Agent 常用方式) +import redis +r = redis.Redis(host='10.8.0.252', password='Hbhyg731024@', db=5) # 正式环境 +r = redis.Redis(host='10.8.0.252', password='Hbhyg731024@', db=6) # 测试环境 +``` + +**数据库工具 `tools/db.py`(默认连接测试库 pms_dev):** +```bash +python tools/db.py tables # 列出所有表 +python tools/db.py count # 统计各表数据量 +python tools/db.py query "SELECT * FROM users" # 查询 +python tools/db.py exec "UPDATE users SET ..." # 写入 +``` +> 注意:`tools/db.py` 默认连的是 `pms_dev`(测试库),要操作正式库请用上面的 Python 直连方式。 + ### Nginx配置 **正式环境** (`/etc/nginx/conf.d/app.anzhizhichong.com.conf`): @@ -235,30 +530,21 @@ server { client_max_body_size 500m; } - location /pms/api/ { - proxy_pass http://127.0.0.1:9000/; + # PMS API 入口(微服务统一前缀 /api/{服务}) + location /api/pms/ { + proxy_pass http://127.0.0.1:3000/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } - # PMS管理前端 - 不带斜杠时重定向 - location = /pms { - return 301 /pms/; - } - # PMS管理前端 location /pms/ { alias /data/pms/web/; try_files $uri $uri/ /pms/index.html; } - # H5用户端 - 不带斜杠时重定向 - location = /h5 { - return 301 /h5/; - } - # H5用户端 location /h5/ { alias /data/pms/h5/; @@ -287,8 +573,9 @@ server { root /data/pms_dev; index index.html; - location /pms/api/ { - proxy_pass http://127.0.0.1:3000/; + # PMS API 入口(微服务统一前缀 /api/{服务}) + location /api/pms/ { + proxy_pass http://127.0.0.1:3000/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -304,6 +591,12 @@ server { alias /data/pms_dev/h5/; try_files $uri $uri/ /h5/index.html; } + + # H5用户端 + location /h5/ { + alias /data/pms_dev/h5/; + try_files $uri $uri/ /h5/index.html; + } } ``` @@ -328,11 +621,11 @@ npm run build **2. 传输到服务器** ```bash # 传输后端二进制(测试环境) -scp software/api-server/target/release/api-server root@118.31.119.206:/data/pms_dev/api-server/ +scp software/api-server/target/release/pms-api root@118.31.119.206:/data/pms_dev/api-server/ scp software/device-server/target/release/device-server root@118.31.119.206:/data/pms_dev/device-server/ # 传输后端二进制(正式环境) -scp software/api-server/target/release/api-server root@118.31.119.206:/data/pms/api-server/ +scp software/api-server/target/release/pms-api root@118.31.119.206:/data/pms/api-server/ scp software/device-server/target/release/device-server root@118.31.119.206:/data/pms/device-server/ # 传输前端静态文件 @@ -486,259 +779,78 @@ device:{imei} # 设备级 Hash ## TCP通讯协议 -- 长连接,无需PING包 -- 空闲1分钟上报,充电15秒上报 -- 超时5000ms -- JSON去空格回车省流量,LF处理粘包 -- `dev_id`:IMEI(10进制),`sub_device_id`:485 ID -- `msg_id`:自增会话ID -- 全局通道号:`dev_id-sub_device_id-通道号`(如`123456789012345-01-6`) +详见 [docs/API.md](docs/API.md#tcp-设备通讯协议) — 包含签名机制、指令清单、指令详情、充电结束原因。 -### 签名机制 +## 数据单位规范 -- 设备首次连接调 `auth_str` 接口获取8位安全码(仅一次) -- `sign = SHA256(dev_id + auth_str + timestamp).substring(56, 63)` -- 时间戳秒级,平台校验±60秒 +详见 [docs/API.md](docs/API.md#数据单位规范) — 所有数值字段使用整数存储,禁止浮点类型。 -### 指令清单 +**现有浮点问题(需清理):** -| 动作 | 方向 | id格式 | 说明 | -|------|------|--------|------| -| `login` | 设备→平台 | 无 | 登录(含sign+timestamp) | -| `auth_str` | 设备→平台 | 无 | 获取安全码(仅首次) | -| `status_post` | 设备→平台 | 无 | 定时上报(rssi+status字典) | -| `pow_fail` | 设备→平台 | 无 | 12V电源掉电上报 | -| `bat_in` | 设备→平台 | `IMEI-仓控板-通道` | 电池插入(含BMS数据,字段待定) | -| `bat_out` | 设备→平台 | `IMEI-仓控板-通道` | 电池拔出 | -| `on` | **双向** | `IMEI-仓控板-通道` | 开始充电(平台下发/设备上报) | -| `off` | **双向** | `IMEI-仓控板-通道` | 停止供电(平台手动/设备自动) | -| `open` | 平台→设备 | `IMEI` | 开门 | -| `pow_on` | 平台→设备 | `IMEI` | 接触器合闸(恢复380V供电) | -| `pow_off` | 平台→设备 | `IMEI` | 接触器分闸(切断380V供电) | -| `reset` | 平台→设备 | `IMEI` 或 `IMEI-仓控板` | 重启整柜或仓控板 | - -### 指令详情 - -#### login(设备→平台) - -设备登录,验证签名。 - -```json -// 请求 -{"act":"login","msg_id":1,"dev_id":"123456789012345","sign":"abc12345","timestamp":1719800000,"h_ver":"1.0.0","s_ver":"1.0.0"} - -// 响应 -{"suc":1,"msg_id":1,"cell":6} -``` - -**字段说明:** -- `sign` — 签名(SHA256(dev_id+auth_str+timestamp)后8位) -- `timestamp` — 秒级时间戳(平台校验±60秒) -- `h_ver` — 硬件版本 -- `s_ver` — 软件版本 -- `cell` — 仓控板数量(响应) - ---- - -#### auth_str(设备→平台) - -获取8位安全码,仅首次连接调用。 - -```json -// 请求 -{"act":"auth_str","msg_id":2,"dev_id":"123456789012345","ccid":"89860000000000000000"} - -// 响应 -{"suc":1,"msg_id":2,"data":{"auth_str":"abcd1234"}} -``` - -**字段说明:** -- `ccid` — SIM卡ICCID号 -- `auth_str` — 8位随机安全码(响应,仅返回一次) - ---- - -#### status_post(设备→平台) - -定时上报,空闲60秒/充电15秒。 - -```json -// 请求 -{"act":"status_post","msg_id":10,"dev_id":"123456789012345","rssi":85,"status":{"123456789012345-01":"000100020003000400050006"}} - -// 响应 -{"suc":1,"msg_id":10} -``` - -**status字段说明:** -- key: `IMEI-仓控板ID` -- value: 6个通道状态拼接(每通道2字节=4个HEX字符) - ---- - -#### pow_fail(设备→平台) - -电源掉电上报。 - -```json -// 请求 -{"act":"pow_fail","msg_id":15,"dev_id":"123456789012345","info":"0"} - -// 响应 -{"suc":1,"msg_id":15} -``` - -**字段说明:** -- `info` — 停电类型:`0`=直流停电(12V掉电),`1`=交流停电(380V掉电) - ---- - -#### pow_on(平台→设备) - -接触器合闸,恢复380V充电电源供电。 - -```json -// 请求 -{"act":"pow_on","msg_id":16,"dev_id":"123456789012345"} - -// 响应 -{"suc":1,"msg_id":16} -``` - ---- - -#### pow_off(平台→设备) - -接触器分闸,切断380V充电电源供电。 - -```json -// 请求 -{"act":"pow_off","msg_id":16,"dev_id":"123456789012345"} - -// 响应 -{"suc":1,"msg_id":16} -``` - ---- - -#### bat_in(设备→平台) - -电池插入事件,立即上报。 - -```json -// 请求 -{"act":"bat_in","msg_id":20,"id":"123456789012345-01-6","bms":{"soc":45,"voltage":52.3}} - -// 响应 -{"suc":1,"msg_id":20} -``` - -**平台处理:** -1. 记录电池信息 -2. 验证电池合规性 -3. 自动下发 `on` 指令 - ---- - -#### bat_out(设备→平台) - -电池拔出事件,立即上报。 - -```json -// 请求 -{"act":"bat_out","msg_id":21,"id":"123456789012345-01-6"} - -// 响应 -{"suc":1,"msg_id":21} -``` - ---- - -#### on(双向) - -**平台→设备:** 开始充电 - -```json -// 请求 -{"act":"on","msg_id":30,"id":"123456789012345-01-6"} - -// 响应 -{"suc":1,"msg_id":30} -``` - -**设备→平台:** 充电开始确认(自动充电模式) - -```json -// 上报 -{"act":"on","msg_id":30,"id":"123456789012345-01-6"} -``` - ---- - -#### off(双向) - -**平台→设备:** 手动停止充电 - -```json -// 请求 -{"act":"off","msg_id":31,"id":"123456789012345-01-6"} - -// 响应 -{"suc":1,"msg_id":31} -``` - -**设备→平台:** 充满自动停止 - -```json -// 上报 -{"act":"off","msg_id":31,"id":"123456789012345-01-6"} -``` - ---- - -#### open(平台→设备) - -开门,只需IMEI。 - -```json -// 请求 -{"act":"open","msg_id":40,"id":"123456789012345"} - -// 响应 -{"suc":1,"msg_id":40} -``` - -**错误码63 = 门锁故障。** - ---- - -#### reset(平台→设备) - -重启整柜或仓控板。 - -```json -// 重启整柜 -{"act":"reset","msg_id":60,"id":"123456789012345"} - -// 重启仓控板 -{"act":"reset","msg_id":61,"id":"123456789012345-01"} - -// 响应 -{"suc":1,"msg_id":60} -``` - -### 充电结束原因(平台判断) - -| 平台收到 | 判断原因 | -|---------|---------| -| `off` (设备→平台) | `normal` — 正常充满 | -| `off` (平台→设备) | `user_stop` — 用户手动停止 | -| `bat_out` | `normal` — 电池拔出 | -| `bat_in` (通道已有电池) | `forced` — 新电池插入,旧电池强制结束 | -| 超时未响应 | `timeout` — 通信超时 | +| 文件 | 位置 | 问题 | 处理 | +|------|------|------|------| +| `src/routes/charge_records.rs` | L46-47, L77-78 | `Option` 读 DECIMAL | 需 CAST AS DOUBLE [有CAST] | +| `src/routes/energy_stats.rs` | L106, L116 | `query_scalar::<_, f64>` 直接读 DECIMAL 无 CAST ❌ | 需加 CAST AS DOUBLE 或改用整数 | +| `src/routes/energy_stats.rs` | L43-72 | 多个 `Option` 字段 | 待迁移为 i64 | +| `src/db/migrate.rs` | L76-77, L97 | `DECIMAL` 列定义 | 待改为 `BIGINT` | ## H5用户端 -- 路径:`/pms/{抽象ID}` -- 功能:显示柜子每个通道状态,可开始/停止充电 -- 无支付/计费功能 +### 信号强度显示(RSSI) + +设备卡片右上角使用 Lucide 信号图标显示 RSSI 信号强度,PC 管理端和 H5 移动端共用同一套逻辑。 + +**RSSI 映射:** + +| RSSI 范围 | 图标 (Lucide) | 信号格 | 颜色 | +|-----------|--------------|--------|------| +| ≥ 70 | signal | 5 格(含 M22) | #16a34a 绿色 | +| ≥ 55 | signal-high | 4 格(含 M17) | #2563eb 蓝色 | +| ≥ 40 | signal-medium | 3 格(含 M12) | #f59e0b 黄色 | +| ≥ 30 | signal-low | 2 格(含 M7) | #f97316 橙色 | +| < 30 或 null | signal-zero | 1 格(仅 M2) | #9ca3af 灰色(离线) | + +**实现方式(移动端 H5):** + +```vue + + + + + + + +``` + +- `v-if` 按 RSSI 阈值动态显示信号格 +- `` 不支持 `display: flex`,统一用 `` 包裹 + +**实现方式(PC 管理端):** + +React 组件 `SignalIcon`,同上逻辑用条件渲染 ``。 + +**后端 RSSI 获取:** + +设备列表接口通过 Redis pipeline 批量 HGET 查询 `device:{imei}` 的 `rssi` 字段: +``` +HSET device:{imei} online 1 rssi 85 +``` + +**相关文件:** +- PC: `software/web/src/pages/devices/CabinetGrid.tsx` — `SignalIcon` 组件 +- H5: `software/h5-uniapp/src/pages/home/index.vue` — signalColor 函数 + 模板 +- H5 API: `software/api-server/src/routes/h5.rs` — `get_cabinets` 加 Redis pipeline +- 类型: `software/h5-uniapp/src/api/index.ts` — `CabinetItem.rssi` + +### 近期 UI 改动 + +1. **设备卡片头部**:去掉"在线/离线"文字 badge,改为信号图标 + RSSI 数值 +2. **通道统计**:图标+数字一行排列,图标用 `` 内联 Lucide(不加容器,flex 居中) +3. **右上角扫码**:scan-line 图标(Lucide),灰色 #6b7280,居中垂直对齐 + +### 开发约束(uni-app 特定) + +1. **`` 不支持 `display: flex`** — uni-app 的 `` 组件是纯文本组件,不接受 flex 布局。需要 flex 的地方用 `` +2. **SVG 图标居中**:`.stat-chip svg { display: block; flex-shrink: 0 }` 用 CSS 统一控制尺寸,比内联属性更可靠 +3. **内联 SVG + v-if**:Vue 3 支持在 `` 上用 `v-if` 条件渲染,适用于动态图标 +4. **CSS 覆盖 SVG 尺寸**:用 `.xxx svg { width: Xpx; height: Xpx }` 统一控制图标大小,不在每个 `` 上写死 diff --git a/deploy/deploy-prod.sh b/deploy/deploy-prod.sh index 7518c6e..d172527 100644 --- a/deploy/deploy-prod.sh +++ b/deploy/deploy-prod.sh @@ -13,7 +13,7 @@ mkdir -p /data/pms/web # 2. 复制文件 echo "2. 复制服务文件..." -cp software/api-server/target/release/api-server /data/pms/api-server/ +cp software/api-server/target/release/pms-api /data/pms/api-server/ cp software/api-server/.env.production /data/pms/api-server/.env cp software/device-server/target/release/device-server /data/pms/device-server/ diff --git a/deploy/deploy-test.sh b/deploy/deploy-test.sh index 657c58c..5b92aed 100644 --- a/deploy/deploy-test.sh +++ b/deploy/deploy-test.sh @@ -13,7 +13,7 @@ mkdir -p /data/pms_dev/web # 2. 复制文件 echo "2. 复制服务文件..." -cp software/api-server/target/release/api-server /data/pms_dev/api-server/ +cp software/api-server/target/release/pms-api /data/pms_dev/api-server/ cp software/api-server/.env.test /data/pms_dev/api-server/.env cp software/device-server/target/release/device-server /data/pms_dev/device-server/ diff --git a/deploy/pms-api-test.service b/deploy/pms-api-test.service index 7675019..118096f 100644 --- a/deploy/pms-api-test.service +++ b/deploy/pms-api-test.service @@ -6,7 +6,7 @@ After=network.target Type=simple User=root WorkingDirectory=/data/pms_dev/api-server -ExecStart=/data/pms_dev/api-server/api-server +ExecStart=/data/pms_dev/api-server/pms-api EnvironmentFile=/data/pms_dev/api-server/.env Restart=always RestartSec=5 diff --git a/docs/1TN协议内容_完整.txt b/docs/1TN协议内容_完整.txt deleted file mode 100644 index 61fcba5..0000000 --- a/docs/1TN协议内容_完整.txt +++ /dev/null @@ -1,384 +0,0 @@ ---- Page 1 --- --1- -- -技 -术 -标 -准 -标准号:YG20240118-1 -换电柜用锂电池充电器 -1TN 标准通信协议 -RS485 -2024 年1 月 -版 -本:1.0 -苏州翌工电源科技有限公司 -研发中心 - ---- Page 2 --- --2- -- -版本修改记录: -版本 -修改内容 -时间 -修改人 -S1.0 -初步建立1TN RS485 协议内容。 -2024-01-18 -Eleven - ---- Page 3 --- --3- -- -一、 -概述 -换电柜1TN 多路充电器支持通过RS485 总线进行通信,通信协议为客户自定义协议, -采用Mod Bus-BTU 传输模式,波特率9600。 -Mod Bus-BTU 传输模式在一根通讯线上采用主从应答方式的通讯连接方式。由主机发 -起,主机的信号寻址到一台唯一地址的终端设备(从机),然后终端设备发出的应答信号以 -相反的方向传输给主机,即半双工的工作模式。该协议只允许在主机和终端设备之间通讯, -而不允许独立的终端设备之间的数据交换,这样各终端设备不会在它们初始化时占据通讯线 -路,而仅限于响应到达本机的查询信号。 -1.1 通讯格式 -信息传输为异步方式,并以字节为单位,在主机和从机之间传递的通讯信息是11 位的 -字格式。包含1 个起始位(0),8 个数据位和1 个停止位。 -信息帧格式: -地址 -通道 -功能码 -长度 -数据区 -校验码 -ADDR -CHx -CMD -LEN -DATA -SUM -0x01 -- -0x03 -0x00 -- -0x0X -0x00 -- -0xFF -0x01-0xFF -DATA -有效字节数 -读写有效的数据内 -容,采用BigEndian -网络字节序 -SUM =(ADDR + -CHx+CMD+LEN -+DATA)按字节 -相加,取低8 位 -1 个字节 -1 个字节 -1 个字节 -1 个字节 -LEN 个字节 -1 个字节 -1.2 通讯信息过程 -信息发送至从机时,从机接收通讯命令,如果SUM 校验无误则执行相应的操作,然后 -把执行结果(数据)返送给主机。返回的信息中包括Header、CMD、ATS、执行后的数据以 -及SUM 校验码和END。如果CRC 校验出错就不返回任何信息,则分析返回相应的错误 -信息。 -1.2.1 -地址码(ADDR) -对应电源双拨码开关,01 代表0x01,10 代表0x02,11 代表0x03。 -对应电源双拨码开关,001 代表0x01,010 代表0x02,011 代表0x03,100 代表0x04, -101 代表0x05,110 代表0x06,111 代表0x07。 -1.2.2 -通道码(CHx) -对应各个通道识别码。 -通道 -Bit1-7 -Bit0 -CHx Byte 解析 -通道 -Bit1-7 -Bit0 -CHx Byte 解析 -CH1 -00 -0/1 -00:CH1 主通道 -01:CH1 副通道 -CH4 -03 -0/1 -06:CH4 主通道 -07:CH4 副通道 - ---- Page 4 --- --4- -- -CH2 -01 -0/1 -02:CH2 主通道 -03:CH2 副通道 -CH5 -04 -0/1 -08:CH5 主通道 -09:CH5 副通道 -CH3 -02 -0/1 -04:CH3 主通道 -05:CH3 副通道 -CH6 -05 -0/1 -0A:CH6 主通道 -0B:CH6 副通道 -1.2.3 -功能码(CMD) -每个通讯信息帧的第4 个字节。主机发送,通过功能码告诉从机应执行什么动作。从 -机响应,从机返回的功能码与从主机发送来的功能码一样,表明从机已响应主机并已执行了 -相关的操作。本协议支持以下功能码: -序号 -命令 -CMD1 -LEN -备注 -1 -设置通道 -输出电压/电流 -0x02 -0x04 -2 -设置通道 -开关机 -0x03 -0x01 -3 -通道组合指令 -开关机/调压/调流 -0x04 -0x05 -4 -电源通道信息查询 -0x05 -0x01 -5 -供应商电源型号/版本 -号/序列号查询 -0x12 -0x01 -10 -升级状态查询 -0x18 -NC -详见升级 -协议 -11 -OTA 远程升级 -0x19 -NC -详见升级 -协议 -1.2.4 -数据区长度(LEN) -见上表 -1.2.5 -数据区(DATA) -数据区随功能码不同而异。这些数据可以是数值、参考地址等。对于不同的从机地址和 -数据信息都不相同,应给出通讯信息表,详见例程。 -1.2.6 -校验区(SUM) -校验区采用和校验方式,从地址码一直到数据域最后一位,求和并取低八位作为校验码。 - ---- Page 5 --- --5- -- -指令解析: -1. 设置1#电源CH1主通道输出电压电流 -主控发送给电源 -ADDR -CHx -CMD -LEN -DATA -SUM -0x01 -0x00 -0x02 -0x04 -0x02 58 00 64 -0xC5 -DATA:0x0258代表60.0V, 0x0064代表10.0A. -电源回复给主控 -ADDR -CHx -CMD -LEN -DATA -SUM -0x01 -0x00 -0x02 -0x01 -0x01 -0x05 -回复:0x01代表设置输出电压电流成功 -2. 设置1#电源CH1主通道输出开关机 -主控发送给电源 -ADDR -CHx -CMD -LEN -DATA -SUM -0x01 -0x00 -0x03 -0x01 -0x01 -0x06 -开关机:0x01代表开机,0x00代表关机 -电源回复给主控 -ADDR -CHx -CMD -LEN -DATA -SUM -0x01 -0x00 -0x03 -0x01 -0x01 -0x06 -回复:0x01代表当前设置开关机成功 -3. 设置1#电源CH1主通道组合控制命令 -主控发送给电源 -ADDR -CHx -CMD -LEN -DATA -SUM -0x01 -0x00 -0x04 -0x05 -0x01 02 58 00 64 -0xC9 -DATA: 0x01代表开机状态,0258代表设置电压60.0V,0064代表设置电流10.0A -电源回复给主控 -ADDR -CHx -CMD -LEN -DATA -SUM -0x01 -0x00 -0x04 -0x07 -0x02 58 00 64 01 01 1A -0xE6 -回复DATA: 0258代表当前输出电压60.0V,0064代表当前输出电流10.0A,01 01代表故障码,1A代表环温26C. -4. 读取1#电源CH1主通道运行信息 -主控发送给电源 -ADDR -CHx -CMD -LEN -DATA -SUM - ---- Page 6 --- --6- -- -0x01 -0x00 -0x05 -0x01 -0x00 -0x07 -电源回复给主控 -ADDR -CHx -CMD -LEN -DATA -SUM -0x01 -0x00 -0x05 -0x0D -02 58 00 64 01 01 1A 55 F0 03 E8 08 98 -0xBD -回复DATA: 0x0258代表设置电压60.0V,0064代表设置电流10.0A,0x01 01代表故障码,1A代表环温26C,55F0输 -入电压220.00V,03E8输入电流10.00A,0898输入功率2200W -故障码解析: -故障字节1 -Bit0 -输入欠压 -故障字节1 -Bit0 -硬件故障 -Bit1 -输入过压 -Bit1 -PFC故障 -Bit2 -NC -Bit2 -风扇故障 -Bit3 -输出过压 -Bit3 -电池反接故障 -Bit4 -输出欠压 -Bit4 -NC -Bit5 -输出短路 -Bit5 -NC -Bit6 -输出过流 -Bit6 -NC -Bit7 -过温 -Bit7 -NC -5. 读取1#电源供应商电源型号/版本号/序列号查询 -主控发送给电源 -ADDR -CHx -CMD -LEN -DATA -SUM -0x01 -0x00 -0x12 -0x01 -0x00 -0x14 -电源回复给主控 -ADDR -CHx -CMD -LEN -DATA -SUM -0x01 -0x00 -0x12 -0x0F -‘YGCP1TN-A’ 01 01 18 02 17 00 01 -+ -回复DATA: ASCII码’YG’代表厂商,CP1TN-A代表电源型号,S1.0V1.0代表硬件版本1软件版本1, 18 02 17 -代表24年2月23日,00 01代表序列号00001. - diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..cc06abb --- /dev/null +++ b/docs/API.md @@ -0,0 +1,351 @@ +# API 接口文档 + +> 充电柜矩阵系统(PMS)接口规范。PC 管理端与 H5 用户端共用同一套 API。 + +## 基础信息 + +- **测试环境**:`https://test.anzhizhichong.com/pms/api/` +- **正式环境**:`https://app.anzhizhichong.com/pms/api/` +- **认证方式**:Bearer Token(JWT) +- **响应格式**:`{ "code": 0, "data": ..., "message": "ok" }` + +## 统一端点清单 + +| 方法 | 路径 | 说明 | 权限 | +|------|------|------|------| +| POST | `/api/auth/login` | 登录 | 免认证 | +| GET | `/api/auth/logout` | 登出 | 需认证 | +| GET | `/api/auth/me` | 当前用户信息 | 需认证 | +| GET | `/api/dashboard` | 仪表盘数据 | 需认证 | +| GET | `/api/cabinets` | 设备列表(含 RSSI) | `device:view` | +| POST | `/api/cabinets` | 批量创建柜子 | `device:create` | +| GET | `/api/cabinets/:id` | 设备详情(含仓板+仓体) | `device:view` | +| PUT | `/api/cabinets/:id` | 更新柜子 | `device:edit` | +| DELETE | `/api/cabinets/:id` | 删除柜子 | `device:delete` | +| GET | `/api/cabinets/abstract/:abstract_id` | 扫码查设备(免认证) | 免认证 | +| GET | `/api/cabinets/scan/:abstract_id` | 扫码验证(需认证) | 需认证 | +| GET | `/api/cabinets/filter` | 设备列表(管理端) | 需认证 | +| GET | `/api/cabinets/realtime-status` | 设备实时状态 | 需认证 | +| GET | `/api/compartments/:id` | 仓体/通道详情 | 需认证 | +| POST | `/api/charge/start` | 开始充电 | `charge:start` | +| POST | `/api/charge/stop` | 停止充电 | `charge:stop` | +| POST | `/api/door/open` | 开门 | `device:door:unlock` | +| GET | `/api/organizations` | 组织列表 | `org:view` | +| POST | `/api/organizations` | 创建组织 | `org:create` | +| GET | `/api/organizations/:org_id/projects` | 项目列表 | 需认证 | +| GET | `/api/charge-records` | 充电记录 | `charge_record:view` | +| GET | `/api/energy-stats/summary` | 能耗统计 | 需认证 | + +--- + +## 认证接口 + +### POST /api/auth/login + +**请求:** +```json +{ + "phone": "13800138000", + "password": "admin123" +} +``` + +**响应:** +```json +{ + "code": 0, + "data": { + "token": "eyJ0eXAi...", + "user": { + "id": 1, + "name": "管理员", + "phone": "13800138000", + "role_level": 2, + "organization_id": null, + "permissions": ["device:view", "device:create", "..."] + } + }, + "message": "登录成功" +} +``` + +**角色等级:** 1=普通用户,2=超级管理员 + +--- + +## H5 专用接口 + +### GET /api/dashboard + +H5 首页仪表盘数据。支持 `org_id` 参数按组织过滤。 + +**响应:** +```json +{ + "code": 0, + "data": { + "online_cabinets": 68, + "charging_count": 214, + "idle_channels": 722, + "fault_channels": 0, + "full_count": 0, + "today_charge_count": 0, + "today_energy_kwh": 0, + "today_charge_hours": 0, + "alerts": [], + "projects": [ + { "id": 1, "name": "无锡太湖新城换电站", "cabinet_count": 2, "charging": 16, "idle": 56, "fault": 0 } + ] + } +} +``` + +### GET /api/cabinets + +设备列表,支持 `project_id` 和 `org_id` 过滤。响应包含实时 RSSI。 + +**响应字段:** +```json +{ + "id": 1, + "abstract_id": "10-00000001", + "name": "太湖新城换电柜A", + "status": 1, + "project_id": 1, + "rssi": 85, + "channel_count": 36, + "idle_count": 30, + "charging_count": 6, + "full_count": 0, + "fault_count": 0 +} +``` + +### GET /api/cabinets/abstract/:abstract_id + +扫码入口,免认证。通过二维码中的抽象ID查找设备。 + +**响应:** +```json +{ + "code": 0, + "data": { + "id": 1, + "abstract_id": "10-00000001", + "imei": "864720562557021", + "iccid": "8986012520983901841", + "name": "太湖新城换电柜A", + "status": 1, + "cabin_boards": [ + { + "id": 1, + "board_id": 1, + "status": 0, + "compartments": [ + { "id": 1, "channel_id": 1, "status": 0 } + ] + } + ] + } +} +``` + +### GET /api/compartments/:id + +仓体/通道详情,含实时 BMS 数据和充电记录。 + +**响应字段:** +- `voltage` — mV(整数) +- `current` — mA(整数) +- `power` — mW(整数) +- `energy` — Wh(整数) +- `soc` — %(0-100) +- `soh` — %(0-100) +- `cell_voltages` — mV 数组 + +### POST /api/charge/start + +**请求:** `{ "compartment_id": 123 }` + +### POST /api/charge/stop + +**请求:** `{ "compartment_id": 123 }` + +### POST /api/door/open + +**请求:** `{ "compartment_id": 123 }` + +### GET /api/cabinets/scan/:abstract_id + +扫码验证,需认证。验证当前用户是否有权访问该设备。 + +--- + +## 数据单位规范 + +**核心原则:所有数值字段使用整数存储,禁止浮点类型 (f32/f64/DECIMAL/FLOAT)** + +| 字段 | 类型 | 单位 | 说明 | +|------|------|------|------| +| `energy` | i64 | **Wh** | 充电量,不存小数 | +| `voltage` | i64 | **mV** | 电池组总电压 | +| `current` | i64 | **mA** | 电流,正=充电 负=放电 | +| `power` | i64 | **mW** | 功率 | +| `soc` / `soh` | i64 | **%** | 电量百分比 / 健康度 | +| `max_temperature` | i64 | **0.1°C** | 25.3°C → `253` | +| `rssi` | i64 | **dBm** | 信号强度 | + +**温度转换:** 前端显示时除以10加小数点(25.3°C → `253`) + +--- + +## TCP 设备通讯协议 + +### 通信规则 + +- TCP 长连接,无需 PING 包 +- 空闲 1 分钟上报,充电 15 秒上报 +- 超时 5000ms +- JSON 去空格回车省流量,LF 处理粘包 +- `dev_id`:IMEI(10进制),`sub_device_id`:485 ID +- `msg_id`:自增会话 ID +- 全局通道号:`dev_id-sub_device_id-通道号`(如 `123456789012345-01-6`) + +### 签名机制 + +- 设备首次连接调 `auth_str` 接口获取8位安全码(仅一次) +- `sign = SHA256(dev_id + auth_str + timestamp).substring(56, 63)` +- 时间戳秒级,平台校验±60秒 + +### 指令清单 + +| 动作 | 方向 | id格式 | 说明 | +|------|------|--------|------| +| `login` | 设备→平台 | 无 | 登录(含sign+timestamp) | +| `auth_str` | 设备→平台 | 无 | 获取安全码(仅首次) | +| `status_post` | 设备→平台 | 无 | 定时上报(rssi+status字典) | +| `pow_fail` | 设备→平台 | 无 | 电源掉电上报 | +| `bat_in` | 设备→平台 | `IMEI-仓控板-通道` | 电池插入 | +| `bat_out` | 设备→平台 | `IMEI-仓控板-通道` | 电池拔出 | +| `on` | **双向** | `IMEI-仓控板-通道` | 开始充电 | +| `off` | **双向** | `IMEI-仓控板-通道` | 停止供电 | +| `open` | 平台→设备 | `IMEI` | 开门 | +| `pow_on` | 平台→设备 | `IMEI` | 接触器合闸 | +| `pow_off` | 平台→设备 | `IMEI` | 接触器分闸 | +| `reset` | 平台→设备 | `IMEI` 或 `IMEI-仓控板` | 重启整柜或仓控板 | + +### 指令详情 + +#### login(设备→平台) + +```json +{"act":"login","msg_id":1,"dev_id":"123456789012345","sign":"abc12345","timestamp":1719800000,"h_ver":"1.0.0","s_ver":"1.0.0"} +// 响应:{"suc":1,"msg_id":1,"cell":6} +``` + +#### auth_str(设备→平台) + +```json +{"act":"auth_str","msg_id":2,"dev_id":"123456789012345","ccid":"89860000000000000000"} +// 响应:{"suc":1,"msg_id":2,"data":{"auth_str":"abcd1234"}} +``` + +#### status_post(设备→平台) + +```json +{"act":"status_post","msg_id":10,"dev_id":"123456789012345","rssi":85, + "status":{"123456789012345-01":"000100020003000400050006"}} +// 响应:{"suc":1,"msg_id":10} +``` + +#### bat_in / bat_out(设备→平台) + +```json +{"act":"bat_in","msg_id":20,"id":"123456789012345-01-6","bms":{"soc":45,"voltage":52.3}} +{"act":"bat_out","msg_id":21,"id":"123456789012345-01-6"} +``` + +#### on / off(双向) + +```json +// 平台→设备:开始充电 +{"act":"on","msg_id":30,"id":"123456789012345-01-6"} +// 设备→平台:充电开始确认 +{"act":"on","msg_id":30,"id":"123456789012345-01-6"} +``` + +#### open(平台→设备) + +```json +{"act":"open","msg_id":40,"id":"123456789012345"} +// 错误码63 = 门锁故障 +``` + +#### reset(平台→设备) + +```json +// 重启整柜 +{"act":"reset","msg_id":60,"id":"123456789012345"} +// 重启仓控板 +{"act":"reset","msg_id":61,"id":"123456789012345-01"} +``` + +### 充电结束原因 + +| 平台收到 | 判断原因 | +|---------|---------| +| `off` (设备→平台) | `normal` — 正常充满 | +| `off` (平台→设备) | `user_stop` — 用户手动停止 | +| `bat_out` | `normal` — 电池拔出 | +| `bat_in` (通道已有电池) | `forced` — 新电池插入强制结束 | +| 超时未响应 | `timeout` — 通信超时 | + +--- + +## Redis 实时状态存储 + +### 层级结构 + +``` +device:{imei} # 设备级 Hash +├─ online = "1" # 在线标记(TTL 300秒) +├─ rssi = "85" # 信号强度 +├─ pow_fail_dc = "0/1" # 直流停电 +└─ device:{imei}:board:{board_idx} # 板级 Hash + ├─ status_hex = "00010002..." # 原始hex状态 + └─ device:{imei}:board:{board_idx}:ch:{0-5} # 通道级 Hash + ├─ on = "0/1" # 充电中 + ├─ full = "0/1" # 充满 + ├─ fault = "0/1" # 故障 + └─ hex = "00010002" # 原始hex +``` + +### BMS 字段 + +| 字段 | 类型 | 单位 | 说明 | +|------|------|------|------| +| `soc` | i64 | % | 电池电量百分比 | +| `soh` | i64 | % | 电池健康度百分比 | +| `voltage` | i64 | mV | 电池组总电压 | +| `current` | i64 | mA | 电流 | +| `power` | i64 | mW | 功率 | +| `energy` | i64 | Wh | 累计充电量 | +| `cell_voltages` | i64[] | mV | 单体电芯电压数组 | + +--- + +## 权限码列表 + +| 权限码 | 说明 | +|--------|------| +| `device:view` | 查看设备 | +| `device:create` | 创建设备 | +| `device:edit` | 编辑设备 | +| `device:delete` | 删除设备 | +| `device:door:unlock` | 开门 | +| `charge:start` | 开始充电 | +| `charge:stop` | 停止充电 | +| `charge_record:view` | 查看充电记录 | +| `org:view` | 查看组织 | +| `org:create` | 创建组织 | +| `org:edit` | 编辑组织 | diff --git a/docs/build_contract.js b/docs/build_contract.js deleted file mode 100644 index a1554de..0000000 --- a/docs/build_contract.js +++ /dev/null @@ -1,272 +0,0 @@ -// 开发合同.docx 生成脚本 -// 标准 Word 排版:A4、宋体、清晰的层级 -const { - Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell, - AlignmentType, HeadingLevel, BorderStyle, WidthType, ShadingType, - PageNumber, Footer, Header, LevelFormat -} = require('docx'); -const fs = require('fs'); - -// 边框样式 -const noBorder = { style: BorderStyle.NONE, size: 0, color: "FFFFFF" }; -const tableBorders = { - top: { style: BorderStyle.SINGLE, size: 6, color: "000000" }, - bottom: { style: BorderStyle.SINGLE, size: 6, color: "000000" }, - left: { style: BorderStyle.SINGLE, size: 6, color: "000000" }, - right: { style: BorderStyle.SINGLE, size: 6, color: "000000" }, - insideHorizontal: { style: BorderStyle.SINGLE, size: 4, color: "000000" }, - insideVertical: { style: BorderStyle.SINGLE, size: 4, color: "000000" }, -}; - -// 标题段落(一级) -function h1(text) { - return new Paragraph({ - heading: HeadingLevel.HEADING_1, - spacing: { before: 360, after: 200 }, - children: [new TextRun({ text, bold: true, size: 32, font: "SimSun" })], - }); -} - -// 标题段落(二级) -function h2(text) { - return new Paragraph({ - heading: HeadingLevel.HEADING_2, - spacing: { before: 240, after: 120 }, - children: [new TextRun({ text, bold: true, size: 28, font: "SimSun" })], - }); -} - -// 正文段落 -function p(text, opts = {}) { - return new Paragraph({ - spacing: { line: 360, before: 80, after: 80, lineRule: "auto" }, - alignment: opts.align || AlignmentType.JUSTIFIED, - indent: opts.indent ? { firstLine: opts.indent } : undefined, - children: [new TextRun({ text, size: 24, font: "SimSun" })], - }); -} - -// 粗体段落 -function pBold(text, opts = {}) { - return new Paragraph({ - spacing: { line: 360, before: 80, after: 80, lineRule: "auto" }, - alignment: opts.align || AlignmentType.LEFT, - children: [new TextRun({ text, bold: true, size: 24, font: "SimSun" })], - }); -} - -// 空行 -function empty() { - return new Paragraph({ children: [new TextRun({ text: "" })] }); -} - -// 居中粗体大标题 -function bigTitle(text) { - return new Paragraph({ - alignment: AlignmentType.CENTER, - spacing: { before: 200, after: 400 }, - children: [new TextRun({ text, bold: true, size: 44, font: "SimHei" })], - }); -} - -// 表格单元格 -function cell(text, opts = {}) { - return new TableCell({ - borders: tableBorders, - width: { size: opts.width || 2000, type: WidthType.DXA }, - shading: opts.header ? { fill: "E7E6E6", type: ShadingType.CLEAR, color: "auto" } : undefined, - margins: { top: 100, bottom: 100, left: 120, right: 120 }, - children: [new Paragraph({ - alignment: opts.align || AlignmentType.LEFT, - spacing: { line: 300, before: 0, after: 0 }, - children: [new TextRun({ - text: String(text), - bold: !!opts.header, - size: 24, - font: "SimSun", - })], - })], - }); -} - -// 创建表格 -function makeTable(rows, colWidths) { - return new Table({ - width: { size: colWidths.reduce((a, b) => a + b, 0), type: WidthType.DXA }, - columnWidths: colWidths, - rows: rows.map((row, i) => new TableRow({ - children: row.map((text, j) => cell(text, { width: colWidths[j], header: i === 0 })), - })), - }); -} - -// ==================== 内容 ==================== - -const children = []; - -// 大标题 -children.push(bigTitle("36仓体直流电瓶车充电柜")); -children.push(bigTitle("开发合同")); - -// 合同信息 -children.push(p("合同编号:_______________", { align: AlignmentType.LEFT })); -children.push(p("签订地点:_______________", { align: AlignmentType.LEFT })); -children.push(p("签订日期:______年______月______日", { align: AlignmentType.LEFT })); -children.push(empty()); - -// 第一条 -children.push(h1("第一条 合同双方")); -children.push(pBold("甲方(委托方):")); -children.push(p("公司名称:____________________________________")); -children.push(p("法定代表人:________________")); -children.push(p("地址:____________________________________")); -children.push(p("联系人:________________ 联系电话:________________")); -children.push(empty()); -children.push(pBold("乙方(开发方):")); -children.push(p("公司名称:____________________________________")); -children.push(p("法定代表人:________________")); -children.push(p("地址:____________________________________")); -children.push(p("联系人:________________ 联系电话:________________")); - -// 第二条 -children.push(h1("第二条 项目概述")); -children.push(p("1. 项目名称:36仓体直流电瓶车充电柜开发项目。")); -children.push(p("2. 项目目标:开发充电柜配套的柜控主板、仓控主板、嵌入式固件、后台管理系统及微信小程序。")); -children.push(p("3. 开发周期:合同生效后12周。")); -children.push(p("4. 交付成果:详见第五条及附件。")); - -// 第三条 -children.push(h1("第三条 开发内容")); -children.push(makeTable( - [ - ["序号", "开发内容", "详见附件"], - ["1", "柜控主板硬件设计开发", "附件一"], - ["2", "仓控主板硬件设计开发", "附件一"], - ["3", "嵌入式固件开发(柜控板+仓控板)", "附件一"], - ["4", "后台管理系统开发", "附件二"], - ["5", "微信小程序开发", "附件二"], - ["6", "通讯协议栈开发实现", "附件三"], - ], - [1000, 5360, 3000] -)); -children.push(empty()); -children.push(p("乙方负责主板设计、元器件采购、焊接调试及联调支持。柜体、电源及外部器件由甲方提供。本合同不交付任何源代码。")); - -// 第四条 -children.push(h1("第四条 开发阶段")); -children.push(makeTable( - [ - ["阶段", "时间", "交付物"], - ["M1 方案定稿", "第1-2周", "原理图、PCB、BOM、协议文档"], - ["M2 开发完成", "第3-6周", "硬件样板、固件、后台、小程序"], - ["M3 联调完成", "第7-9周", "集成测试报告"], - ["M4 验收交付", "第10-12周", "完整成品、技术文档"], - ], - [2500, 2000, 4860] -)); -children.push(empty()); -children.push(p("付款方式:______________________________________________________________________")); - -// 第五条 -children.push(h1("第五条 交付与验收")); -children.push(p("交付物清单及验收标准详见附件三。整机验收时乙方到甲方现场完成联调,验收所需柜体及电源由甲方提供。")); - -// 第六条 -children.push(h1("第六条 知识产权")); -children.push(p("1. 乙方在本合同履行过程中产生的全部技术成果(包括但不限于源代码、设计图纸、技术文档等)的知识产权归甲乙双方共同所有。")); -children.push(p("2. 任何一方均有权在自身业务范围内自由使用上述技术成果,无需征得另一方同意或另行支付费用。")); - -// 第七条 -children.push(h1("第七条 保密条款")); -children.push(p("1. 双方对合同履行过程中知悉的对方商业秘密、技术秘密负有保密义务。")); -children.push(p("2. 保密期限自合同生效之日起3年。")); -children.push(p("3. 未经对方书面同意,不得向第三方披露任何与本合同相关的信息。")); - -// 第八条 -children.push(h1("第八条 质保与维护")); -children.push(p("1. 硬件质保期自验收通过之日起12个月,质保期内免费维修或更换。")); -children.push(p("2. 软件质保期自验收通过之日起6个月,质保期内免费修复Bug。")); -children.push(p("3. 质保期外的维护服务费用由双方另行协商。")); - -// 第九条 -children.push(h1("第九条 违约责任")); -children.push(p("1. 甲方逾期付款的,每逾期一日按应付未付金额的千分之三支付违约金。")); -children.push(p("2. 乙方逾期交付的,每逾期一日按合同总金额的千分之三支付违约金。")); -children.push(p("3. 任何一方违反知识产权或保密条款的,应赔偿对方全部损失。")); - -// 第十条 -children.push(h1("第十条 合同总金额")); -children.push(p("合同总金额:人民币____________元(大写:__________________________)。")); - -// 第十一条 -children.push(h1("第十一条 争议解决")); -children.push(p("本合同履行过程中发生争议的,双方应协商解决;协商不成的,提交合同签订地人民法院诉讼解决。")); - -// 第十二条 -children.push(h1("第十二条 其他")); -children.push(p("1. 本合同一式两份,甲乙双方各执一份,具有同等法律效力。")); -children.push(p("2. 本合同附件为合同的组成部分,与本合同具有同等法律效力。")); -children.push(p("3. 未尽事宜,双方可另行签订补充协议。")); - -// 签章区 -children.push(empty()); -children.push(empty()); -children.push(pBold("甲方(盖章):__________________________")); -children.push(p("授权代表签字:________________________")); -children.push(p("日期:______年______月______日")); -children.push(empty()); -children.push(pBold("乙方(盖章):__________________________")); -children.push(p("授权代表签字:________________________")); -children.push(p("日期:______年______月______日")); - -// ==================== 生成文档 ==================== - -const doc = new Document({ - creator: "CodeBuddy", - title: "36仓体直流电瓶车充电柜开发合同", - styles: { - default: { - document: { run: { font: "SimSun", size: 24 } }, - }, - paragraphStyles: [ - { - id: "Heading1", name: "Heading 1", basedOn: "Normal", next: "Normal", quickFormat: true, - run: { size: 32, bold: true, font: "SimHei" }, - paragraph: { spacing: { before: 360, after: 200 }, outlineLevel: 0 }, - }, - { - id: "Heading2", name: "Heading 2", basedOn: "Normal", next: "Normal", quickFormat: true, - run: { size: 28, bold: true, font: "SimHei" }, - paragraph: { spacing: { before: 240, after: 120 }, outlineLevel: 1 }, - }, - ], - }, - sections: [{ - properties: { - page: { - size: { width: 11906, height: 16838 }, // A4 - margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 }, - }, - }, - footers: { - default: new Footer({ - children: [new Paragraph({ - alignment: AlignmentType.CENTER, - children: [ - new TextRun({ text: "第 ", size: 20, font: "SimSun" }), - new TextRun({ children: [PageNumber.CURRENT], size: 20, font: "SimSun" }), - new TextRun({ text: " 页 / 共 ", size: 20, font: "SimSun" }), - new TextRun({ children: [PageNumber.TOTAL_PAGES], size: 20, font: "SimSun" }), - new TextRun({ text: " 页", size: 20, font: "SimSun" }), - ], - })], - }), - }, - children, - }], -}); - -Packer.toBuffer(doc).then(buffer => { - fs.writeFileSync("开发合同.docx", buffer); - console.log("OK: 开发合同.docx 已生成 (" + buffer.length + " bytes)"); -}); diff --git a/docs/convert.js b/docs/convert.js deleted file mode 100644 index ad58133..0000000 --- a/docs/convert.js +++ /dev/null @@ -1,22 +0,0 @@ -const marked = require('marked'); -const fs = require('fs'); - -const files = ['开发合同.md', '附件一-硬件规格.md', '附件二-软件规格.md', '附件三-交付与验收标准.md']; -const styles = `body{font-family:"Microsoft YaHei",sans-serif;max-width:900px;margin:40px auto;padding:20px;line-height:1.8;color:#333} -h1{text-align:center;font-size:22px;border-bottom:2px solid #333;padding-bottom:10px} -h2{font-size:18px;margin-top:30px;border-left:4px solid #333;padding-left:10px} -h3{font-size:16px;margin-top:20px}h4{font-size:15px;margin-top:15px} -table{width:100%;border-collapse:collapse;margin:15px 0;font-size:14px} -th,td{border:1px solid #999;padding:8px;text-align:left}th{background:#f0f0f0} -p,li{font-size:14px}hr{border:none;border-top:1px solid #ccc;margin:30px 0} -pre{background:#f5f5f5;padding:15px;border-radius:4px;overflow-x:auto;font-size:13px} -code{font-family:Consolas,monospace} -.blank{color:#999;border-bottom:1px solid #999;display:inline-block;min-width:150px} -.sign{margin-top:60px}.sign div{display:inline-block;width:45%;vertical-align:top}`; - -files.forEach(f => { - const md = fs.readFileSync(f, 'utf8'); - const html = `${f.replace('.md','')}${marked.parse(md)}`; - fs.writeFileSync(f.replace('.md', '.html'), html); - console.log(`OK: ${f} -> ${f.replace('.md', '.html')}`); -}); diff --git a/docs/package-lock.json b/docs/package-lock.json deleted file mode 100644 index 0a20dc9..0000000 --- a/docs/package-lock.json +++ /dev/null @@ -1,548 +0,0 @@ -{ - "name": "docs", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "docx": "^9.7.1", - "marked": "^18.0.5", - "puppeteer": "^25.1.0" - } - }, - "node_modules/@puppeteer/browsers": { - "version": "3.0.4", - "resolved": "https://registry.npmmirror.com/@puppeteer/browsers/-/browsers-3.0.4.tgz", - "integrity": "sha512-HGM8iAmGTf+Y7t0373szVbTmt3d7vPkYL/1bpOkOFO0YUYLgSeuYBCzESklogNPvOBnZ/MRD5f07OkpqH1trtA==", - "license": "Apache-2.0", - "dependencies": { - "modern-tar": "^0.7.6", - "yargs": "^17.7.2" - }, - "bin": { - "browsers": "lib/main-cli.js" - }, - "engines": { - "node": ">=22.12.0" - }, - "peerDependencies": { - "proxy-agent": ">=8.0.1" - }, - "peerDependenciesMeta": { - "proxy-agent": { - "optional": true - } - } - }, - "node_modules/@types/node": { - "version": "25.9.4", - "resolved": "https://registry.npmmirror.com/@types/node/-/node-25.9.4.tgz", - "integrity": "sha512-dszCsrKb5U7ZsVZBWiHFklTloVl0mSEnWH/iZXfZUlI4rzCUnsvGmgqfuVRHL54ugE7/wRuxEIXRa2iMZ+BG6g==", - "license": "MIT", - "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/chromium-bidi": { - "version": "16.0.1", - "resolved": "https://registry.npmmirror.com/chromium-bidi/-/chromium-bidi-16.0.1.tgz", - "integrity": "sha512-J63PGu/9PpeCwLIcKYyzWP6yaVL5pxuBc0shlYCYM8BaAkmlwiQboXO1iNbOgSDbVklEyYFfNEcHD8oOAWacUA==", - "license": "Apache-2.0", - "dependencies": { - "mitt": "^3.0.1", - "zod": "^3.24.1" - }, - "engines": { - "node": ">=20.19.0 <22.0.0 || >=22.12.0" - }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, - "node_modules/devtools-protocol": { - "version": "0.0.1624250", - "resolved": "https://registry.npmmirror.com/devtools-protocol/-/devtools-protocol-0.0.1624250.tgz", - "integrity": "sha512-YFAat/lOiIk0ARmBweG+ygrEcbZrq5B9urRyUoeQKp53MlidHXE2TmTbxKcaXoQj7u/aX+jebDO4BW55rs0WwA==", - "license": "BSD-3-Clause" - }, - "node_modules/docx": { - "version": "9.7.1", - "resolved": "https://registry.npmmirror.com/docx/-/docx-9.7.1.tgz", - "integrity": "sha512-ilXFf9Moz47ABjFpDiA5s1w9lpb4EFSp7+5iiJSbfyYDM+bpZdAgLlSr7fW4aXhVe/E+F6QCv0EvRVFEd5CsWg==", - "license": "MIT", - "dependencies": { - "@types/node": "^25.2.3", - "hash.js": "^1.1.7", - "jszip": "^3.10.1", - "nanoid": "^5.1.3", - "xml": "^1.0.1", - "xml-js": "^1.6.8" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmmirror.com/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "license": "MIT" - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "license": "(MIT OR GPL-3.0-or-later)", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "license": "MIT", - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/marked": { - "version": "18.0.5", - "resolved": "https://registry.npmmirror.com/marked/-/marked-18.0.5.tgz", - "integrity": "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==", - "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "license": "ISC" - }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "license": "MIT" - }, - "node_modules/modern-tar": { - "version": "0.7.6", - "resolved": "https://registry.npmmirror.com/modern-tar/-/modern-tar-0.7.6.tgz", - "integrity": "sha512-sweCIVXzx1aIGTCdzcMlSZt1h8k5Tmk08VNAuRk3IU28XamGiOH5ypi11g6De2CH7PhYqSSnGy2A/EFhbWnVKg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/nanoid": { - "version": "5.1.15", - "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-5.1.15.tgz", - "integrity": "sha512-kBg3RpGtIe+RpTbyXwoI6pk5yD7KUiI3sygUqgeBMRst42KmhB4RZC7eiO9Wa1HIpaCCtpE2DJ6OI4Wi5ebwFw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.js" - }, - "engines": { - "node": "^18 || >=20" - } - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "license": "(MIT AND Zlib)" - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/puppeteer": { - "version": "25.1.0", - "resolved": "https://registry.npmmirror.com/puppeteer/-/puppeteer-25.1.0.tgz", - "integrity": "sha512-7L6/0JM7XStK99lIL4xQySyNEXNfII6pk0BxkI5kKBTOhR7AsoQiv067YTsE/rIXxQiq9ajlO4WcqBjS/FWK1A==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@puppeteer/browsers": "3.0.4", - "chromium-bidi": "16.0.1", - "devtools-protocol": "0.0.1624250", - "lilconfig": "^3.1.3", - "puppeteer-core": "25.1.0", - "typed-query-selector": "^2.12.2" - }, - "bin": { - "puppeteer": "lib/puppeteer/node/cli.js" - }, - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/puppeteer-core": { - "version": "25.1.0", - "resolved": "https://registry.npmmirror.com/puppeteer-core/-/puppeteer-core-25.1.0.tgz", - "integrity": "sha512-jKzy5y4WG6uNuFbTWgW1D7mqoT9o0nllc/6a1DGF775T1mPmgw3scdFEtEq67yVFikavQmbYq6NLfbTfxHSlqQ==", - "license": "Apache-2.0", - "dependencies": { - "@puppeteer/browsers": "3.0.4", - "chromium-bidi": "16.0.1", - "devtools-protocol": "0.0.1624250", - "typed-query-selector": "^2.12.2", - "webdriver-bidi-protocol": "0.4.2", - "ws": "^8.21.0" - }, - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/sax": { - "version": "1.6.0", - "resolved": "https://registry.npmmirror.com/sax/-/sax-1.6.0.tgz", - "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=11.0.0" - } - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "license": "MIT" - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/typed-query-selector": { - "version": "2.12.2", - "resolved": "https://registry.npmmirror.com/typed-query-selector/-/typed-query-selector-2.12.2.tgz", - "integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==", - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", - "license": "MIT" - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/webdriver-bidi-protocol": { - "version": "0.4.2", - "resolved": "https://registry.npmmirror.com/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.2.tgz", - "integrity": "sha512-VSV+fzfChirL3e7jay2yUC7B4HQCGtEWEg/MSSQbK+qWbqeGlRLlXTzPpYr3XGUvbpDHumWZBJxgesg4N7dbtA==", - "license": "Apache-2.0" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/ws": { - "version": "8.21.0", - "resolved": "https://registry.npmmirror.com/ws/-/ws-8.21.0.tgz", - "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/xml/-/xml-1.0.1.tgz", - "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", - "license": "MIT" - }, - "node_modules/xml-js": { - "version": "1.6.11", - "resolved": "https://registry.npmmirror.com/xml-js/-/xml-js-1.6.11.tgz", - "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", - "license": "MIT", - "dependencies": { - "sax": "^1.2.4" - }, - "bin": { - "xml-js": "bin/cli.js" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmmirror.com/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } -} diff --git a/docs/package.json b/docs/package.json deleted file mode 100644 index 94c34e3..0000000 --- a/docs/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "dependencies": { - "docx": "^9.7.1", - "marked": "^18.0.5", - "puppeteer": "^25.1.0" - } -} diff --git a/docs/toPdf.js b/docs/toPdf.js deleted file mode 100644 index 7c71c23..0000000 --- a/docs/toPdf.js +++ /dev/null @@ -1,26 +0,0 @@ -const puppeteer = require('puppeteer'); -const fs = require('fs'); -const path = require('path'); - -const dir = 'D:\\projects\\charging-cabinet\\docs'; - -async function convert(htmlName, pdfName) { - const html = fs.readFileSync(path.join(dir, htmlName), 'utf8'); - const browser = await puppeteer.launch({ headless: 'new', executablePath: 'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe', args: ['--no-sandbox'] }); - const page = await browser.newPage(); - await page.setContent(html, { waitUntil: 'networkidle0' }); - await page.pdf({ - path: path.join(dir, pdfName), - format: 'A4', - margin: { top: '20mm', bottom: '20mm', left: '20mm', right: '20mm' }, - printBackground: true, - }); - await browser.close(); - console.log(`OK: ${pdfName}`); -} - -(async () => { - await convert('开发合同.html', '开发合同.pdf'); - await convert('附件-开发需求规格说明书.html', '附件-开发需求规格说明书.pdf'); - console.log('Done'); -})(); diff --git a/docs/开发文档.md b/docs/开发文档.md index b11306f..89b61c8 100644 --- a/docs/开发文档.md +++ b/docs/开发文档.md @@ -765,13 +765,16 @@ TCP断连处理: ### 7.1 后台管理系统 -#### 7.1.1 技术栈建议 +#### 7.1.1 技术栈(已确定) ``` -后端框架:Spring Boot / Node.js (Egg.js/Nest.js) / Python (FastAPI) -数据库:MySQL + Redis + InfluxDB(时序数据) -通信协议:自定义TCP协议长连接(设备通讯)+ HTTP/WebSocket(前端) -前端框架:React / Vue3 + Element Plus / Ant Design +后端框架:Rust + Axum +前端框架:React + Arco Design Pro(管理端) +H5用户端:uni-app + Vue 3 + Pinia(跨平台,支持 H5 / 微信小程序) +数据库:MySQL(阿里云RDS) +缓存:Redis(阿里云Redis) +通信协议:自定义TCP协议长连接(设备通讯)+ HTTP(API) +权限:RBAC,精确到按钮级别 ``` #### 7.1.2 核心模块 @@ -833,22 +836,24 @@ TCP断连处理: - 故障等级(预警/保护/严重) ``` -### 7.2 微信小程序 +### 7.2 用户端(H5 / 小程序) + +> 当前阶段使用 H5 用户端(uni-app 构建),后续可转换为微信小程序。 #### 7.2.1 功能页面 | 页面 | 功能 | |------|------| -| 首页 | 附近充电柜列表/地图,空闲舱位显示 | +| 首页 | 附近充电柜列表,空闲舱位显示,RSSI信号强度 | +| 设备详情 | 仓控板+仓体状态,ICCID信息 | | 扫码充电 | 扫描仓体二维码,启动充电 | -| 预约充电 | 选择仓体,预约时间段 | | 充电进度 | 实时显示SOC/电压/温度/充电时间 | | 消息通知 | 充电完成、故障告警、系统通知 | | 个人中心 | 充电记录、用户信息、反馈 | #### 7.2.2 仓体状态展示 -小程序同步显示仓体指示灯状态及门锁状态: +用户端同步显示仓体指示灯状态及门锁状态: - 绿灯闪烁 → "充电中" - 绿灯常亮 + 门锁图标开 → "空闲(门已开)" - 绿灯常亮 + 门锁图标关 → "空闲(门已关)" @@ -856,7 +861,7 @@ TCP断连处理: 扫码开门流程: 1. 用户扫描仓体二维码 -2. 小程序显示仓体信息和操作按钮 +2. 用户端显示仓体信息和操作按钮 3. 点击"开门"按钮 → 平台验证 → 下发开门指令 4. 门锁打开,用户放入/取出电池 5. 关门后系统自动检测门磁闭合 diff --git a/docs/开发计划.md b/docs/开发计划.md deleted file mode 100644 index 064c823..0000000 --- a/docs/开发计划.md +++ /dev/null @@ -1,347 +0,0 @@ -# 36仓体直流电瓶车充电柜 - 开发计划 - -## 1. 项目概述 - -- **项目名称**:36仓体直流电瓶车充电柜 -- **开发周期**:12周 -- **核心交付**:充电柜硬件(含仓控主板×6、柜控主板×1)+ 后台管理系统 + 微信小程序 - ---- - -## 2. 阶段划分 - -### 第1阶段:需求确认与方案设计(第1-2周) - -**目标**:完成需求确认、技术调研、方案定稿 - -| 任务 | 负责人 | 输出物 | 时间 | -|------|--------|--------|------| -| 需求评审与确认 | PM/研发 | 需求规格说明书 | 第1周 | -| 技术调研 | 研发 | 技术调研报告 | 第1周 | -| 系统架构设计 | 架构师 | 系统架构设计文档 | 第2周 | -| 硬件方案设计 | 硬件工程师 | 硬件方案文档 | 第2周 | -| 元器件选型 | 硬件工程师 | 选型清单(BOM) | 第2周 | -| 仓控主板详细设计 | 硬件工程师 | 仓控主板原理图+PCB设计 | 第2周 | -| 柜控主板详细设计 | 硬件工程师 | 柜控主板原理图+PCB设计 | 第2周 | -| 通讯协议设计 | 嵌入式工程师 | 协议设计文档 | 第2周 | -| 指示灯选型确认 | 硬件工程师 | 指示灯规格确认单 | 第2周 | -| 平台禁用功能方案 | PM/研发 | 功能方案文档 | 第2周 | -| 电池握手方案设计 | 嵌入式工程师 | 握手流程设计文档 | 第2周 | -| 双模式充电控制方案 | 嵌入式工程师 | 控制逻辑设计文档 | 第2周 | -| 接口定义评审 | 全组 | 接口定义确认 | 第2周 | -| 仓控板拨码ID方案 | 硬件工程师 | ID设置方案确认 | 第2周 | -| 仓控-电源独立通讯链路设计 | 嵌入式工程师 | 通讯链路设计文档 | 第2周 | - -> **里程碑1(第2周末)**:需求确认完成,技术方案定稿,元器件选型完成,仓控板与柜控板定制方案明确。 - ---- - -### 第2阶段:硬件开发与软件开发(第3-6周) - -#### 2.1 硬件开发 - -| 任务 | 负责人 | 输出物 | 时间 | -|------|--------|--------|------| -| 充电柜本体结构设计 | 结构工程师 | 3D模型+工程图 | 第3-4周 | -| 充电柜本体打样组装 | 工厂 | 样机柜体 | 第4-5周 | -| 电源选型采购 | 采购 | 电源到货 | 第3周 | -| 指示灯选型采购 | 采购 | 指示灯到货 | 第3周 | -| 通讯模块采购 | 采购 | 4G模块到货 | 第3周 | -| 仓控主板PCB打样 | PCB工厂 | 仓控板PCB | 第4周 | -| 柜控主板PCB打样 | PCB工厂 | 柜控板PCB | 第4周 | -| 仓控主板焊接调试 | 硬件工程师 | 仓控板6块(功能正常) | 第5周 | -| 柜控主板焊接调试 | 硬件工程师 | 柜控板1块(功能正常) | 第5周 | -| 继电器通断测试 | 硬件工程师 | 测试报告 | 第5周 | -| 光耦隔离效果测试 | 硬件工程师 | 测试报告 | 第5周 | -| 485通讯调试 | 硬件工程师 | 通讯功能正常 | 第5周 | -| 温控接口调试 | 硬件工程师 | 6路温控采集正常 | 第5周 | -| 仓控板拨码开关ID测试 | 硬件工程师 | ID识别准确 | 第5-6周 | -| 仓控板门锁/门磁调试 | 硬件工程师 | 6路门锁驱动+门磁检测正常 | 第6周 | -| 仓控-电源独立通讯调试 | 嵌入式工程师 | 通讯链路正常 | 第6周 | -| 4G模块调试 | 嵌入式工程师 | 4G通讯正常 | 第6周 | -| 烟感/浸水接口调试 | 硬件工程师 | 开关量检测正常 | 第6周 | -| 喷淋电磁阀调试 | 硬件工程师 | 烟感联动开启正常 | 第6周 | -| 环境温控传感器调试 | 硬件工程师 | 温度采集正常 | 第6周 | -| 加热风扇继电器调试 | 硬件工程师 | 独立通断正常 | 第6周 | -| 电磁泵继电器调试 | 硬件工程师 | 通断控制正常 | 第6周 | -| 指示灯接线与调试 | 硬件工程师 | 灯光状态正常 | 第6周 | -| 整机装配 | 工厂 | 样机完成 | 第6周 | - -#### 2.2 软件开发 - -| 任务 | 负责人 | 输出物 | 时间 | -|------|--------|--------|------| -| 仓控板固件-485从站通讯 | 嵌入式工程师 | 代码+单元测试 | 第3-4周 | -| 仓控板固件-BMS通讯模块(Modbus-RTU) | 嵌入式工程师 | 代码+单元测试 | 第3-5周 | -| 仓控板固件-BMS通讯模块(天能协议) | 嵌入式工程师 | 代码+单元测试 | 第4-5周 | -| 仓控板固件-无协议充电模块 | 嵌入式工程师 | 代码+单元测试 | 第5-6周 | -| 仓控板固件-电源通讯模块(1TN协议) | 嵌入式工程师 | 代码+单元测试 | 第4-5周 | -| 仓控板固件-继电器控制 | 嵌入式工程师 | 代码+单元测试 | 第4周 | -| 仓控板固件-门锁控制 | 嵌入式工程师 | 代码+单元测试 | 第4-5周 | -| 仓控板固件-门磁监测 | 嵌入式工程师 | 代码+单元测试 | 第4-5周 | -| 仓控板固件-指示灯控制 | 嵌入式工程师 | 代码+单元测试 | 第4周 | -| 仓控板固件-温控采集 | 嵌入式工程师 | 代码+单元测试 | 第4-5周 | -| 仓控板固件-ID管理(拨码开关) | 嵌入式工程师 | 代码+单元测试 | 第4周 | -| 仓控板固件-数据上传/指令接收 | 嵌入式工程师 | 代码+单元测试 | 第5周 | -| 仓控板固件-OTA升级 | 嵌入式工程师 | 代码+单元测试 | 第5-6周 | -| 仓控板固件-BMS协议适配层 | 嵌入式工程师 | 代码+单元测试 | 第5-6周 | -| 柜控板固件-485主站通讯(ID寻址) | 嵌入式工程师 | 代码+单元测试 | 第4-5周 | -| 柜控板固件-4G通讯模块 | 嵌入式工程师 | 代码+单元测试 | 第4-6周 | -| 柜控板固件-数据处理与存储 | 嵌入式工程师 | 代码+单元测试 | 第5-6周 | -| 柜控板固件-充电控制逻辑(双模式) | 嵌入式工程师 | 代码+单元测试 | 第5-6周 | -| 柜控板固件-烟感/浸水监测 | 嵌入式工程师 | 代码+单元测试 | 第5周 | -| 柜控板固件-安全监测与联动 | 嵌入式工程师 | 代码+单元测试 | 第5-6周 | -| 柜控板固件-加热风扇温控 | 嵌入式工程师 | 代码+单元测试 | 第5-6周 | -| 柜控板固件-电表采集(DL/T645) | 嵌入式工程师 | 代码+单元测试 | 第5-6周 | -| 柜控板固件-电磁泵控制 | 嵌入式工程师 | 代码+单元测试 | 第5-6周 | -| 柜控板固件-喷淋电磁阀控制 | 嵌入式工程师 | 代码+单元测试 | 第5-6周 | -| 柜控板固件-门锁指令转发 | 嵌入式工程师 | 代码+单元测试 | 第5-6周 | -| 柜控板固件-OTA升级管理 | 嵌入式工程师 | 代码+单元测试 | 第5-6周 | -| 柜控板固件-BMS配置管理 | 嵌入式工程师 | 代码+单元测试 | 第5-6周 | -| 后台系统-设备管理模块 | 后端工程师 | 接口+管理界面 | 第3-5周 | -| 后台系统-仓体控制模块 | 后端工程师 | 接口+管理界面 | 第4-6周 | -| 后台系统-用户管理模块 | 后端工程师 | 接口+管理界面 | 第3-4周 | -| 后台系统-数据统计模块 | 后端工程师 | 接口+报表页面 | 第5-6周 | -| 后台系统-故障管理模块 | 后端工程师 | 接口+管理界面 | 第5-6周 | -| 后台系统-BMS协议配置 | 后端工程师 | 接口+配置界面 | 第5-6周 | -| 后台系统-无协议充电参数配置 | 后端工程师 | 接口+配置界面 | 第5-6周 | -| 后台系统-报警阈值管理 | 后端工程师 | 接口+配置界面 | 第5-6周 | -| 后台系统-OTA升级管理 | 后端工程师 | 接口+升级管理界面 | 第5-6周 | -| 后台系统-仓体禁用功能 | 后端工程师 | 接口+管理界面 | 第5-6周 | -| 后台系统-充电控制模式配置 | 后端工程师 | 接口+配置界面 | 第5-6周 | -| 后台系统-4G通讯管理 | 后端工程师 | 接口+监控界面 | 第5-6周 | -| 后台系统-4G MQTT服务端 | 后端工程师 | 服务+接口 | 第4-6周 | -| 小程序-扫码充电 | 前端工程师 | 小程序页面+接口对接 | 第4-6周 | -| 小程序-预约充电 | 前端工程师 | 小程序页面+接口对接 | 第4-6周 | -| 小程序-充电进度查询 | 前端工程师 | 小程序页面+接口对接 | 第5-6周 | -| 小程序-消息通知 | 前端工程师 | 推送功能 | 第5-6周 | -| 小程序-故障反馈 | 前端工程师 | 小程序页面+接口对接 | 第5-6周 | -| 电池握手验证程序 | 嵌入式/后端 | 代码+单元测试 | 第5-6周 | -| 指示灯控制程序联调 | 嵌入式工程师 | 功能验证 | 第6周 | -| 平台仓体禁用功能联调 | 全栈 | 功能验证 | 第6周 | -| 柜控板-仓控板ID寻址通讯程序 | 嵌入式工程师 | 代码+单元测试 | 第5-6周 | - -> **里程碑2(第6周末)**:硬件开发调试完成,软件开发完成,各模块初步实现。 - ---- - -### 第3阶段:系统集成与联合调试(第7-9周) - -| 任务 | 负责人 | 时间 | -|------|--------|------| -| 仓控板-柜控板485通讯联调 | 嵌入式工程师 | 第7周 | -| 仓控板-电源独立通讯联调 | 嵌入式工程师 | 第7周 | -| 仓控板-BMS通讯联调 | 嵌入式工程师 | 第7周 | -| 指示灯状态联动测试 | 全组 | 第7周 | -| 平台仓体禁用功能联调 | 全栈 | 第7周 | -| 电池握手验证联调 | 全组 | 第7-8周 | -| 充电双模式控制联调 | 全组 | 第7-8周 | -| 无协议充电联调 | 全组 | 第7-8周 | -| 4G数据上传联调 | 全栈 | 第7-8周 | -| 烟感/浸水+喷淋电磁阀联动联调 | 全组 | 第8周 | -| 门锁控制联调(平台开门) | 全栈 | 第8周 | -| 门磁监测+关门超时报警联调 | 全组 | 第8周 | -| BMS多协议切换联调 | 全组 | 第8周 | -| OTA固件升级联调 | 全组 | 第8周 | -| 加热风扇温控联调 | 全组 | 第8周 | -| 电磁泵远程控制联调 | 全组 | 第8周 | -| 后台管理平台联调 | 全栈 | 第8周 | -| 小程序联调 | 全栈 | 第8周 | -| 仓控板ID寻址准确性测试 | 嵌入式工程师 | 第8周 | -| 整机功能联调 | 全组 | 第8-9周 | -| 异常场景排查与修复 | 全组 | 第8-9周 | -| 性能调优 | 全组 | 第9周 | -| 安全性测试 | 全组 | 第9周 | -| 系统稳定性测试 | 全组 | 第9周 | - -> **里程碑3(第9周末)**:系统集成调试完成,所有功能正常,无重大故障。 - ---- - -### 第4阶段:试点测试(第10-11周) - -| 任务 | 负责人 | 时间 | -|------|--------|------| -| 36仓体同步运行测试 | 全组 | 第10周 | -| 指示灯状态准确性全面测试 | 测试 | 第10周 | -| 平台禁用功能可靠性测试 | 测试 | 第10周 | -| 主板运行稳定性测试 | 测试 | 第10周 | -| 电池握手与充电控制准确性测试 | 测试 | 第10-11周 | -| 仓控板ID拨码设置准确性测试 | 测试 | 第10周 | -| ID寻址通讯可靠性测试 | 测试 | 第10周 | -| 仓控-电源独立通讯稳定性测试 | 测试 | 第10-11周 | -| 4G通讯稳定性测试 | 测试 | 第10-11周 | -| 烟感/浸水告警有效性测试 | 测试 | 第11周 | -| 过充/过温/过流保护测试 | 测试 | 第11周 | -| 实际使用场景模拟测试 | 全组 | 第11周 | -| 问题收集与整改 | 全组 | 第11周 | - -> **里程碑4(第11周末)**:试点测试完成,所有问题整改完毕。 - ---- - -### 第5阶段:验收交付(第12周) - -| 任务 | 负责人 | 输出物 | 时间 | -|------|--------|--------|------| -| 开发文档整理 | 研发 | 开发文档定稿 | 第12周 | -| 测试文档整理 | 测试 | 测试报告 | 第12周 | -| 操作手册编写 | PM | 用户操作手册 | 第12周 | -| 运维手册编写 | 运维 | 运维手册 | 第12周 | -| 项目验收评审 | 全组 | 验收报告 | 第12周 | -| 项目交接 | PM | 交接清单 | 第12周 | - -> **里程碑5(第12周末)**:项目验收通过,文档交付完成,正式交接。 - ---- - -## 3. 甘特图概览 - -``` -任务\周次 | W1 | W2 | W3 | W4 | W5 | W6 | W7 | W8 | W9 | W10| W11| W12| ---------------|----|----|----|----|----|----|----|----|----|----|----|----| -需求确认 | ██ | ██ | | | | | | | | | | | -方案设计 | ██ | ██ | | | | | | | | | | | -结构设计 | | | ██ | ██ | | | | | | | | | -PCB设计打样 | | | ██ | ██ | | | | | | | | | -元器件采购 | | | ██ | | | | | | | | | | -焊接调试 | | | | ██ | ██ | ██ | | | | | | | -主机固件开发 | | | ██ | ██ | ██ | ██ | | | | | | | -后台开发 | | | ██ | ██ | ██ | ██ | | | | | | | -小程序开发 | | | | ██ | ██ | ██ | | | | | | | -系统联调 | | | | | | | ██ | ██ | ██ | | | | -性能优化 | | | | | | | | ██ | ██ | | | | -试点测试 | | | | | | | | | | ██ | ██ | | -问题整改 | | | | | | | | | | ██ | ██ | | -文档编写 | | | | | | | | | | | ██ | ██ | -验收交付 | | | | | | | | | | | | ██ | -``` - ---- - -## 4. 关键里程碑 - -| 里程碑 | 时间 | 交付物 | -|--------|------|--------| -| M1: 方案定稿 | 第2周末 | 需求规格说明书、技术方案文档、BOM清单、原理图 | -| M2: 开发完成 | 第6周末 | 硬件样机、固件代码、后台系统、小程序 | -| M3: 联调完成 | 第9周末 | 集成测试报告、功能确认书 | -| M4: 试点完成 | 第11周末 | 试点测试报告、问题整改清单 | -| M5: 验收交付 | 第12周末 | 开发文档、测试报告、操作手册、验收报告 | - ---- - -## 5. 资源需求 - -### 5.1 人员配置 - -| 角色 | 人数 | 主要职责 | -|------|------|---------| -| 项目经理(PM) | 1 | 需求管理、进度把控、资源协调 | -| 硬件工程师 | 2 | 仓控/柜控主板设计、调试、测试 | -| 嵌入式工程师 | 2 | 仓控/柜控固件开发、协议对接 | -| 后端工程师 | 2 | 后台系统开发、MQTT服务、数据库 | -| 前端工程师 | 1 | 微信小程序开发 | -| 测试工程师 | 1 | 单元/集成/系统测试 | -| 结构工程师 | 1 | 柜体结构设计 | -| 采购/生产 | 1 | 元器件采购、PCB打样、组装 | - -### 5.2 硬件清单(样机) - -| 物料 | 数量 | 说明 | -|------|------|------| -| 充电柜柜体 | 1台 | 36仓体结构 | -| 充电电源 | 6台 | 直流充电电源,1TN协议 | -| 仓控主板PCB | 10块 | 含备板 | -| 柜控主板PCB | 3块 | 含备板 | -| 4G模块 | 3个 | 含备板 | -| 直流继电器 | 50个 | 含备件 | -| 光耦隔离芯片 | 100个 | 485隔离用 | -| 温控传感器 | 15个 | 含备件 | -| 烟感传感器 | 2个 | 含备件 | -| 浸水传感器 | 2个 | 含备件 | -| 环境温控传感器 | 3个 | 含备件,用于加热风扇自动温控(NTC/DS18B20) | -| 电子门锁(电控锁) | 40个 | 含备件,12V,每仓体1个 | -| 门磁传感器 | 40个 | 含备件,每仓体1个 | -| 喷淋电磁阀 | 2个 | 含备件,烟感联动喷淋 | -| 加热风扇 | 1台 | 含独立继电器控制 | -| 电磁泵 | 1台 | 含继电器驱动 | -| 状态指示灯 | 50个 | 含备件 | -| 拨码开关 | 10个 | 仓控板ID设置 | -| 连接线束 | 1批 | 4PIN线等 | - -### 5.3 软件环境 - -| 类型 | 环境/工具 | -|------|-----------| -| 嵌入式IDE | Keil MDK / STM32CubeIDE | -| 后台开发 | Spring Boot / Node.js / Python + MySQL + Redis + InfluxDB | -| 小程序开发 | 微信开发者工具 | -| 协议测试 | Modbus Poll / 串口调试助手 | -| 版本管理 | Git | -| 项目管理 | 禅道 / Jira | - ---- - -## 6. 风险与应对 - -| 风险 | 概率 | 影响 | 应对措施 | -|------|------|------|---------| -| 星恒BMS协议版本不匹配 | 中 | 高 | 提前获取最新协议文档,预留协议适配层 | -| 电源1TN协议兼容性问题 | 中 | 高 | 提前联调电源厂商,准备协议测试环境 | -| 485通讯干扰(36舱全开) | 中 | 高 | 光耦隔离+终端电阻+合理布线 | -| 4G信号不稳定 | 中 | 中 | 柜控板本地缓存+断线重连机制 | -| 仓控板ID寻址冲突 | 低 | 高 | 拨码开关硬件防呆+上电自检+重复ID告警 | -| 元器件供货延迟 | 中 | 中 | 提前采购,备选供应商方案 | -| 温控传感器精度不足 | 低 | 中 | 选型测试验证,软件滤波处理 | -| 指示灯亮度/视角不足 | 低 | 中 | 提前打样测试确认 | -| 电池握手失败率偏高 | 中 | 高 | 增加重试机制,完善异常处理 | -| PCB设计缺陷需改版 | 中 | 高 | 预留测试点,第一版预投少量验证 | -| 加热风扇温控失效 | 低 | 中 | 增加硬件超温保护,双路温控冗余 | -| 电磁泵继电器粘连 | 低 | 中 | 选用高品质继电器,定期巡检 | -| 门锁频繁动作损坏 | 中 | 中 | 选用工业级电控锁,预留手动开门方式 | -| BMS多协议兼容性 | 中 | 高 | 协议适配层隔离,各协议独立实现,平台可远程回退 | -| 喷淋电磁阀误触发 | 低 | 中 | 硬件滤波+软件确认延时,防误喷 | - ---- - -## 7. 测试策略 - -### 7.1 测试阶段对应 - -| 阶段 | 测试类型 | 说明 | -|------|---------|------| -| 第5-6周 | 单元测试 | 硬件模块+软件模块单独测试 | -| 第7-9周 | 集成测试 | 模块间对接测试,协议联调 | -| 第10-11周 | 系统测试 | 全功能测试,模拟实际场景 | - -### 7.2 核心测试用例 - -| 编号 | 测试项 | 预期结果 | -|------|--------|---------| -| TC01 | 36路独立充电同时运行 | 各舱互不干扰,电源正常输出 | -| TC02 | 指示灯状态切换 | 各状态切换准确,延迟≤0.5s | -| TC03 | 平台禁用/解除 | 禁用后红灯常亮,禁止充电;解除后恢复绿灯常亮 | -| TC04 | BMS数据读取 | SOC/电压/温度/告警数据准确 | -| TC05 | 电源参数控制 | 电压/电流设置准确,开关控制正常 | -| TC06 | 电池握手流程 | 插入→读取→上传→校验→充电启动,流程完整 | -| TC07 | 双模式充电控制 | 平台模式/柜控模式均正常 | -| TC08 | 仓控板ID识别 | 拨码ID1-6准确识别,通讯寻址无误 | -| TC09 | 仓控-电源独立通讯 | 各仓控板与对应电源通讯稳定 | -| TC10 | 烟感触发保护 | 全局断电,红灯切换,告警上传 | -| TC11 | 浸水触发保护 | 同上 | -| TC12 | 过充保护 | SOC≥98%自动断电 | -| TC13 | 过温保护 | 温度>55℃自动断电 | -| TC14 | 4G断线重连 | 断线后缓存数据,重连后补偿上传 | -| TC15 | 电池不合规红灯闪烁 | 拔掉电池后自动恢复绿灯常亮 | -| TC16 | 加热风扇温控 | 环境温度<0°C自动启动,≥25°C自动停止 | -| TC17 | 电磁泵远程控制 | 平台下发通/断指令,继电器正确响应 | -| TC18 | 加热风扇滞回控制 | 温度在0-25°C之间保持当前状态不抖动 | -| TC19 | 扫码开门 | 平台下发开门指令,门锁动作,门磁反馈正确 | -| TC20 | 关门超时报警 | 开门后超过设置阈值未关,上传告警 | -| TC21 | 烟感联动喷淋 | 烟感触发→接触器断开+喷淋电磁阀打开+告警上传 | -| TC22 | BMS协议切换 | 平台切换星恒/天能/无协议,仓控板正确切换通讯方式 | -| TC23 | 无协议充电 | 固定电压电流输出,低于停止电流阈值自动断电 | -| TC24 | OTA固件升级 | 柜控板+仓控板固件在线升级,升级后正常运行 | -| TC25 | 高温报警阈值可配置 | 平台设置不同阈值,仓体超温触发告警 | -| TC26 | 电表耗电量采集 | 柜控板按DL/T645读取电表数据,上报平台显示正确 | diff --git a/software/api-server/Cargo.lock b/software/api-server/Cargo.lock index 6126885..f152e3b 100644 --- a/software/api-server/Cargo.lock +++ b/software/api-server/Cargo.lock @@ -32,34 +32,6 @@ dependencies = [ "libc", ] -[[package]] -name = "api-server" -version = "0.1.0" -dependencies = [ - "argon2", - "axum", - "bytes", - "chrono", - "dotenvy", - "futures", - "hex", - "jsonwebtoken", - "rand", - "redis", - "rust_xlsxwriter", - "serde", - "serde_json", - "sha2", - "sqlx", - "thiserror", - "tokio", - "tokio-util", - "tower-http", - "tracing", - "tracing-subscriber", - "uuid", -] - [[package]] name = "arbitrary" version = "1.4.2" @@ -1254,6 +1226,34 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "pms-api" +version = "0.1.0" +dependencies = [ + "argon2", + "axum", + "bytes", + "chrono", + "dotenvy", + "futures", + "hex", + "jsonwebtoken", + "rand", + "redis", + "rust_xlsxwriter", + "serde", + "serde_json", + "sha2", + "sqlx", + "thiserror", + "tokio", + "tokio-util", + "tower-http", + "tracing", + "tracing-subscriber", + "uuid", +] + [[package]] name = "potential_utf" version = "0.1.5" diff --git a/software/api-server/Cargo.toml b/software/api-server/Cargo.toml index 3311214..f680314 100644 --- a/software/api-server/Cargo.toml +++ b/software/api-server/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "api-server" +name = "pms-api" version = "0.1.0" edition = "2024" diff --git a/software/api-server/src/db/migrate.rs b/software/api-server/src/db/migrate.rs index 91983d1..e0b237c 100644 --- a/software/api-server/src/db/migrate.rs +++ b/software/api-server/src/db/migrate.rs @@ -73,8 +73,8 @@ pub async fn run(pool: &MySqlPool) -> Result<(), sqlx::Error> { cabinet_id BIGINT NOT NULL, start_time TIMESTAMP NULL COMMENT '充电开始时间', end_time TIMESTAMP NULL COMMENT '充电结束时间', - energy DECIMAL(10,2) COMMENT '充电量kWh', - max_temperature DECIMAL(5,1) COMMENT '最高温度', + energy BIGINT NOT NULL DEFAULT 0 COMMENT '充电量(Wh)', + max_temperature BIGINT NOT NULL DEFAULT 0 COMMENT '最高温度(0.1°C)', status TINYINT DEFAULT 0 COMMENT '0正常 1异常 2手动停止', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (compartment_id) REFERENCES compartments(id), @@ -94,7 +94,7 @@ pub async fn run(pool: &MySqlPool) -> Result<(), sqlx::Error> { id BIGINT AUTO_INCREMENT PRIMARY KEY, cabinet_id BIGINT NOT NULL, stat_date DATE NOT NULL, - total_energy DECIMAL(10,2) COMMENT '总能耗kWh', + total_energy BIGINT NOT NULL DEFAULT 0 COMMENT '总能耗(Wh)', charge_count INT DEFAULT 0 COMMENT '充电次数', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (cabinet_id) REFERENCES cabinets(id), diff --git a/software/api-server/src/routes/auth.rs b/software/api-server/src/routes/auth.rs index f66ee62..52723d5 100644 --- a/software/api-server/src/routes/auth.rs +++ b/software/api-server/src/routes/auth.rs @@ -31,15 +31,15 @@ pub async fn login( return Err(AppError::BadRequest("手机号和密码不能为空".into())); } - // 查询用户(含密码比对) - let user_row: Option<(i64, Option, String, String, i32, i32)> = sqlx::query_as( - "SELECT id, name, phone, password, role, status FROM users WHERE phone = ?" + // 查询用户(含角色和组织信息) + let user_row: Option<(i64, Option, String, String, i32, i32, Option)> = sqlx::query_as( + "SELECT id, name, phone, password, status, role, organization_id FROM users WHERE phone = ?", ) .bind(&input.phone) .fetch_optional(pool) .await?; - let (id, name, phone, stored_password, role, status) = + let (id, name, phone, stored_password, status, role_level, organization_id) = user_row.ok_or_else(|| AppError::Unauthorized("手机号或密码错误".into()))?; // 检查账户状态 @@ -69,7 +69,8 @@ pub async fn login( "id": id, "name": name, "phone": phone, - "role": role, + "role_level": role_level, + "organization_id": organization_id, "permissions": perm_list, } }, diff --git a/software/api-server/src/routes/cabinets.rs b/software/api-server/src/routes/cabinets.rs index 1e8ee8e..0712114 100644 --- a/software/api-server/src/routes/cabinets.rs +++ b/software/api-server/src/routes/cabinets.rs @@ -5,6 +5,7 @@ use axum::extract::{Path, Query, State}; use axum::Json; +use redis; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use sqlx::MySqlPool; @@ -403,7 +404,7 @@ pub async fn get_cabinet_detail( user: CurrentUser, State(state): State, Path(id): Path, -) -> Result, AppError> { +) -> Result, AppError> { auth::check_permission(&user, "device:view")?; let db = &state.mysql; @@ -429,7 +430,9 @@ pub async fn get_cabinet_detail( .fetch_all(db) .await?; - let mut cabin_boards = Vec::new(); + // 查询仓控板+仓体,同时构建 Value 以便注入 SOC + let mut cabin_boards: Vec = Vec::new(); + let mut comp_ids: Vec = Vec::new(); for board in boards { let compartments: Vec = sqlx::query_as( r#"SELECT id, cabin_board_id, channel_id, status @@ -439,23 +442,91 @@ pub async fn get_cabinet_detail( .fetch_all(db) .await?; - cabin_boards.push(CabinBoardWithCompartments { - id: board.id, - board_id: board.board_id, - status: board.status, - compartments, - }); + for c in &compartments { + comp_ids.push(c.id); + } + + let comp_values: Vec = compartments.into_iter().map(|c| { + json!({ + "id": c.id, + "channel_id": c.channel_id, + "status": c.status, + }) + }).collect(); + + cabin_boards.push(json!({ + "id": board.id, + "board_id": board.board_id, + "status": board.status, + "compartments": comp_values, + })); } - Ok(Json(CabinetDetail { - id: cabinet.id, - project_id: cabinet.project_id, - abstract_id: cabinet.abstract_id, - imei: cabinet.imei, - name: cabinet.name, - status: cabinet.status, - cabin_boards, - })) + // 从 Redis 批量读取实时状态(RSSI/电源/SOC) + let (rssi, pow_fail_dc, pow_fail_ac, mut soc_map) = if let Ok(client) = redis::Client::open(state.redis_url.as_str()) { + if let Ok(mut conn) = client.get_async_connection().await { + let key = format!("device:{}", cabinet.imei); + let mut pipe = redis::pipe(); + pipe.cmd("HGET").arg(&key).arg("rssi") + .cmd("HGET").arg(&key).arg("pow_fail_dc") + .cmd("HGET").arg(&key).arg("pow_fail_ac"); + for cid in &comp_ids { + pipe.cmd("GET").arg(format!("compartment:status:{}", cid)); + } + let vals: Vec> = pipe.query_async(&mut conn).await.unwrap_or_default(); + let rssi: Option = vals.get(0).and_then(|v| v.as_ref()).and_then(|v| v.parse().ok()); + let dc = vals.get(1).and_then(|v| v.as_ref()).map(|v| v == "1"); + let ac = vals.get(2).and_then(|v| v.as_ref()).map(|v| v == "1"); + let mut soc_map = std::collections::HashMap::new(); + for (i, cid) in comp_ids.iter().enumerate() { + if let Some(Some(data)) = vals.get(3 + i) { + if let Ok(v) = serde_json::from_str::(data) { + let soc = v.get("soc").and_then(|s| s.as_i64()); + let bid = v.get("battery_id").and_then(|s| s.as_str()).map(|s| s.to_string()); + soc_map.insert(*cid, (soc, bid)); + } + } + } + (rssi, dc, ac, soc_map) + } else { (None, None, None, std::collections::HashMap::new()) } + } else { (None, None, None, std::collections::HashMap::new()) }; + + // 注入 SOC 到每个仓体 + for board in &mut cabin_boards { + if let Some(comps) = board.get_mut("compartments").and_then(|c| c.as_array_mut()) { + for comp in comps { + if let Some(id) = comp.get("id").and_then(|i| i.as_i64()) { + if let Some((soc, bid)) = soc_map.get(&id) { + if let Some(s) = soc { + comp["soc"] = json!(s); + } + if let Some(b) = bid { + comp["battery_id"] = json!(b); + } + } + } + } + } + } + + Ok(Json(json!({ + "code": 0, + "data": { + "id": cabinet.id, + "project_id": cabinet.project_id, + "abstract_id": cabinet.abstract_id, + "imei": cabinet.imei, + "iccid": cabinet.iccid, + "name": cabinet.name, + "status": cabinet.status, + "rssi": rssi, + "pow_fail_dc": pow_fail_dc, + "pow_fail_ac": pow_fail_ac, + "address": cabinet.address, + "cabin_boards": cabin_boards, + }, + "message": "ok" + }))) } // --------------------------------------------------------------------------- diff --git a/software/api-server/src/routes/charge_records.rs b/software/api-server/src/routes/charge_records.rs index fae117e..cb6dc7d 100644 --- a/software/api-server/src/routes/charge_records.rs +++ b/software/api-server/src/routes/charge_records.rs @@ -43,8 +43,8 @@ pub struct ChargeRecordItem { pub channel_id: i8, pub start_time: Option, pub end_time: Option, - pub energy: Option, - pub max_temperature: Option, + pub energy: Option, + pub max_temperature: Option, pub status: i8, pub status_text: String, } @@ -62,7 +62,7 @@ fn status_text(status: i8) -> &'static str { } } -/// 数据库行映射 — DECIMAL 列通过 CAST 转为 f64 +/// 数据库行映射 #[derive(Debug, sqlx::FromRow)] struct ChargeRecordRow { id: i64, @@ -74,8 +74,8 @@ struct ChargeRecordRow { channel_id: Option, start_time: Option, end_time: Option, - energy: Option, - max_temperature: Option, + energy: Option, + max_temperature: Option, status: i8, } @@ -134,12 +134,12 @@ async fn fetch_charge_rows( }; let sql = format!( - "SELECT cr.id, cr.cabinet_id, c.name AS cabinet_name, \ - cr.compartment_id, cb.id AS cabin_board_id, cb.board_id, comp.channel_id, \ - cr.start_time, cr.end_time, \ - CAST(cr.energy AS DOUBLE) AS energy, \ - CAST(cr.max_temperature AS DOUBLE) AS max_temperature, \ - cr.status \ + "SELECT cr.id, cr.cabinet_id, c.name AS cabinet_name, \ + cr.compartment_id, cb.id AS cabin_board_id, cb.board_id, comp.channel_id, \ + cr.start_time, cr.end_time, \ + cr.energy, \ + cr.max_temperature, \ + cr.status \ FROM charge_records cr \ LEFT JOIN cabinets c ON cr.cabinet_id = c.id \ LEFT JOIN compartments comp ON cr.compartment_id = comp.id \ @@ -351,7 +351,7 @@ async fn generate_charge_records_excel( let headers = [ "ID", "柜子", "仓板", "通道", "开始时间", "结束时间", - "充电量(kWh)", "最高温度(°C)", "状态", + "充电量(Wh)", "最高温度(0.1°C)", "状态", ]; for (col, header) in headers.iter().enumerate() { sheet.write_string(0, col as u16, *header)?; @@ -371,8 +371,8 @@ async fn generate_charge_records_excel( row, 5, record.end_time.map(|t| t.to_string()).as_deref().unwrap_or("-"), )?; - sheet.write_number(row, 6, record.energy.unwrap_or(0.0))?; - sheet.write_number(row, 7, record.max_temperature.unwrap_or(0.0))?; + sheet.write_number(row, 6, record.energy.unwrap_or(0) as f64)?; + sheet.write_number(row, 7, record.max_temperature.unwrap_or(0) as f64)?; sheet.write_string(row, 8, status_text(record.status))?; } diff --git a/software/api-server/src/routes/energy_stats.rs b/software/api-server/src/routes/energy_stats.rs index c17190f..f19ca8a 100644 --- a/software/api-server/src/routes/energy_stats.rs +++ b/software/api-server/src/routes/energy_stats.rs @@ -1,7 +1,7 @@ //! 能耗管理 API — 总览、排行、趋势、导出 //! //! 基于 energy_stats 表按日期聚合的能耗数据进行统计分析。 -//! 电费估算按 ¥0.80/kWh 计算(可配置)。 +//! 电费估算按 ¥0.80/kWh 计算(可配置),energy 单位为 Wh。 use axum::extract::{Query, State}; use axum::Json; @@ -11,8 +11,8 @@ use serde_json::{json, Value}; use crate::error::AppError; use crate::middleware::auth::{self, CurrentUser}; -/// 电费单价(元/kWh) -const COST_PER_KWH: f64 = 0.80; +/// 电费单价(元/Wh),energy 单位为 Wh,0.80 元/kWh = 0.0008 元/Wh +const COST_PER_WH: f64 = 0.0008; /// 趋势查询参数 #[derive(Debug, Deserialize)] @@ -40,8 +40,8 @@ struct CabinetRankRow { id: i64, abstract_id: String, name: Option, - today_energy: Option, - month_energy: Option, + today_energy: Option, + month_energy: Option, } /// 项目统计行 @@ -50,15 +50,15 @@ struct ProjectStatRow { id: i64, name: String, cabinet_count: i64, - today_energy: Option, - month_energy: Option, + today_energy: Option, + month_energy: Option, } /// 趋势数据行 #[derive(Debug, sqlx::FromRow)] struct TrendRow { period: String, // 日期或周/月标识 - energy: Option, + energy: Option, charge_count: Option, } @@ -69,7 +69,7 @@ struct EnergyDetailRow { cabinet_id: i64, cabinet_name: Option, project_name: Option, - total_energy: Option, + total_energy: Option, charge_count: Option, } @@ -100,22 +100,22 @@ pub async fn get_summary( // 今日总能耗 let today_sql = format!( - "SELECT COALESCE(SUM(total_energy), 0) FROM energy_stats es WHERE stat_date = CURDATE(){}", + "SELECT CAST(COALESCE(SUM(total_energy), 0) AS SIGNED) FROM energy_stats es WHERE stat_date = CURDATE(){}", org_filter ); - let mut today_q = sqlx::query_scalar::<_, f64>(&today_sql); + let mut today_q = sqlx::query_scalar::<_, i64>(&today_sql); for v in &org_binds { today_q = today_q.bind(v); } - let today_energy: f64 = today_q.fetch_one(pool).await?; + let today_energy: i64 = today_q.fetch_one(pool).await?; // 本月总能耗 let month_sql = format!( - "SELECT COALESCE(SUM(total_energy), 0) FROM energy_stats es \ + "SELECT CAST(COALESCE(SUM(total_energy), 0) AS SIGNED) FROM energy_stats es \ WHERE stat_date >= DATE_FORMAT(CURDATE(), '%Y-%m-01'){}", org_filter ); - let mut month_q = sqlx::query_scalar::<_, f64>(&month_sql); + let mut month_q = sqlx::query_scalar::<_, i64>(&month_sql); for v in &org_binds { month_q = month_q.bind(v); } - let month_energy: f64 = month_q.fetch_one(pool).await?; + let month_energy: i64 = month_q.fetch_one(pool).await?; // 柜子总数(含组织过滤) let cabinet_count: i64 = if user.role_level >= 2 { @@ -145,15 +145,12 @@ pub async fn get_summary( 0 }; - let today = today_energy; - let month = month_energy; - Ok(Json(json!({ "code": 0, "data": { - "today_energy": (today * 100.0).round() / 100.0, - "month_energy": (month * 100.0).round() / 100.0, - "estimated_cost": ((month * COST_PER_KWH) * 100.0).round() / 100.0, + "today_energy": today_energy, + "month_energy": month_energy, + "estimated_cost": ((month_energy as f64) * COST_PER_WH * 100.0).round() / 100.0, "cabinet_count": cabinet_count, "online_count": online_count, }, @@ -205,8 +202,8 @@ pub async fn get_cabinet_ranking( "cabinet_id": r.id, "abstract_id": r.abstract_id, "name": r.name, - "today_energy": (r.today_energy.unwrap_or(0.0) * 100.0).round() / 100.0, - "month_energy": (r.month_energy.unwrap_or(0.0) * 100.0).round() / 100.0, + "today_energy": r.today_energy.unwrap_or(0), + "month_energy": r.month_energy.unwrap_or(0), }) }).collect(); @@ -238,8 +235,8 @@ pub async fn get_project_stats( let sql = format!( "SELECT p.id, p.name, \ COUNT(DISTINCT c.id) AS cabinet_count, \ - COALESCE(SUM(es_today.total_energy), 0) AS today_energy, \ - COALESCE(SUM(es_month.total_energy), 0) AS month_energy \ + CAST(COALESCE(SUM(es_today.total_energy), 0) AS SIGNED) AS today_energy, \ + CAST(COALESCE(SUM(es_month.total_energy), 0) AS SIGNED) AS month_energy \ FROM projects p \ LEFT JOIN cabinets c ON p.id = c.project_id \ LEFT JOIN energy_stats es_today \ @@ -261,8 +258,8 @@ pub async fn get_project_stats( "project_id": r.id, "name": r.name, "cabinet_count": r.cabinet_count, - "today_energy": (r.today_energy.unwrap_or(0.0) * 100.0).round() / 100.0, - "month_energy": (r.month_energy.unwrap_or(0.0) * 100.0).round() / 100.0, + "today_energy": r.today_energy.unwrap_or(0), + "month_energy": r.month_energy.unwrap_or(0), }) }).collect(); @@ -301,7 +298,7 @@ pub async fn get_trend( "week" => { let sql = format!( "SELECT DATE_FORMAT(stat_date, '%Y-%u') AS period, \ - SUM(total_energy) AS energy, SUM(charge_count) AS charge_count \ + CAST(SUM(total_energy) AS SIGNED) AS energy, CAST(SUM(charge_count) AS SIGNED) AS charge_count \ FROM energy_stats es \ WHERE stat_date >= DATE_SUB(CURDATE(), INTERVAL ? DAY){} \ GROUP BY period ORDER BY period ASC", @@ -314,7 +311,7 @@ pub async fn get_trend( "month" => { let sql = format!( "SELECT DATE_FORMAT(stat_date, '%Y-%m') AS period, \ - SUM(total_energy) AS energy, SUM(charge_count) AS charge_count \ + CAST(SUM(total_energy) AS SIGNED) AS energy, CAST(SUM(charge_count) AS SIGNED) AS charge_count \ FROM energy_stats es \ WHERE stat_date >= DATE_SUB(CURDATE(), INTERVAL ? DAY){} \ GROUP BY period ORDER BY period ASC", @@ -343,7 +340,7 @@ pub async fn get_trend( let list: Vec = rows.iter().map(|r| { json!({ "period": r.period, - "energy": (r.energy.unwrap_or(0.0) * 100.0).round() / 100.0, + "energy": r.energy.unwrap_or(0), "charge_count": r.charge_count.unwrap_or(0), }) }).collect(); @@ -487,10 +484,10 @@ async fn generate_energy_excel( // 查询能耗明细 let sql = format!( - "SELECT es.stat_date, es.cabinet_id, c.name AS cabinet_name, \ - p.name AS project_name, \ - CAST(es.total_energy AS DOUBLE) AS total_energy, \ - es.charge_count \ + "SELECT es.stat_date, es.cabinet_id, c.name AS cabinet_name, \ + p.name AS project_name, \ + es.total_energy, \ + es.charge_count \ FROM energy_stats es \ LEFT JOIN cabinets c ON es.cabinet_id = c.id \ LEFT JOIN projects p ON c.project_id = p.id \ @@ -516,7 +513,7 @@ async fn generate_energy_excel( sheet.set_name("能耗统计")?; let headers = [ - "日期", "柜子ID", "柜子名称", "所属项目", "能耗(kWh)", "充电次数", + "日期", "柜子ID", "柜子名称", "所属项目", "能耗(Wh)", "充电次数", ]; for (col, header) in headers.iter().enumerate() { sheet.write_string(0, col as u16, *header)?; @@ -528,7 +525,7 @@ async fn generate_energy_excel( sheet.write_number(row, 1, record.cabinet_id as f64)?; sheet.write_string(row, 2, record.cabinet_name.as_deref().unwrap_or("-"))?; sheet.write_string(row, 3, record.project_name.as_deref().unwrap_or("-"))?; - sheet.write_number(row, 4, record.total_energy.unwrap_or(0.0))?; + sheet.write_number(row, 4, record.total_energy.unwrap_or(0) as f64)?; sheet.write_number(row, 5, record.charge_count.unwrap_or(0) as f64)?; } diff --git a/software/api-server/src/routes/h5.rs b/software/api-server/src/routes/h5.rs index 646587c..ef93d67 100644 --- a/software/api-server/src/routes/h5.rs +++ b/software/api-server/src/routes/h5.rs @@ -1,19 +1,19 @@ //! H5 用户端 API //! //! 提供微信扫码充电的 H5 页面所需的全部后端接口: -//! - POST /api/h5/auth/login — H5 手机号登录 -//! - GET /api/h5/dashboard — 首页仪表盘数据 -//! - GET /api/h5/projects — 项目列表 -//! - GET /api/h5/cabinets — 设备列表(按项目过滤) -//! - GET /api/h5/cabinets/:id — 设备详情(含仓板+仓体状态) -//! - GET /api/h5/cabinets/abstract/:aid — 通过抽象ID查设备(扫码入口) -//! - GET /api/h5/compartments/:id — 仓体/通道详情 -//! - POST /api/h5/charge/start — 开始充电 -//! - POST /api/h5/charge/stop — 停止充电 -//! - POST /api/h5/door/open — 开门 +//! - GET /api/dashboard — 首页仪表盘数据 +//! - GET /api/cabinets — 设备列表(按项目过滤) +//! - GET /api/cabinets/:id — 设备详情(含仓板+仓体状态) +//! - GET /api/cabinets/abstract/:aid — 通过抽象ID查设备(扫码入口,免认证) +//! - GET /api/compartments/:id — 仓体/通道详情 +//! - POST /api/charge/start — 开始充电 +//! - POST /api/charge/stop — 停止充电 +//! - POST /api/door/open — 开门 +//! - GET /api/cabinets/scan/:abstract_id — 扫码验证 use axum::extract::{Path, Query, State}; use axum::Json; +use redis; use serde::Deserialize; use serde_json::{json, Value}; @@ -25,13 +25,6 @@ use crate::middleware::auth::{self, CurrentUser}; // 请求体定义 // --------------------------------------------------------------------------- -/// H5 登录请求体 -#[derive(Debug, Deserialize)] -pub(crate) struct H5LoginInput { - phone: String, - password: String, -} - /// 充电控制请求体(开始/停止充电、开门共用) #[derive(Debug, Deserialize)] pub(crate) struct CompartmentActionInput { @@ -42,6 +35,13 @@ pub(crate) struct CompartmentActionInput { #[derive(Debug, Deserialize)] pub(crate) struct CabinetListQuery { project_id: Option, + org_id: Option, +} + +/// 仪表盘查询参数 +#[derive(Debug, Deserialize)] +pub(crate) struct DashboardQuery { + org_id: Option, } // --------------------------------------------------------------------------- @@ -55,7 +55,7 @@ struct ProjectRow { name: String, } -/// 柜子列表行 +/// 柜子列表行(含通道统计) #[derive(Debug, sqlx::FromRow)] struct H5CabinetRow { id: i64, @@ -63,6 +63,12 @@ struct H5CabinetRow { name: Option, status: i8, project_id: Option, + imei: Option, + channel_count: i64, + idle_count: i64, + charging_count: i64, + full_count: i64, + fault_count: i64, } /// 仓控板行 @@ -96,58 +102,6 @@ struct CabinetImeiRow { imei: String, } -// --------------------------------------------------------------------------- -// H5 登录 -// --------------------------------------------------------------------------- - -/// POST /api/h5/auth/login — H5 用户登录(复用 users 表) -pub async fn h5_login( - State(state): State, - Json(input): Json, -) -> Result, AppError> { - let pool = &state.mysql; - - if input.phone.is_empty() || input.password.is_empty() { - return Err(AppError::BadRequest("手机号和密码不能为空".into())); - } - - // 查询用户 - let user_row: Option<(i64, Option, String, String, i32)> = sqlx::query_as( - "SELECT id, name, phone, password, status FROM users WHERE phone = ?", - ) - .bind(&input.phone) - .fetch_optional(pool) - .await?; - - let (id, name, phone, stored_password, status) = - user_row.ok_or_else(|| AppError::Unauthorized("手机号或密码错误".into()))?; - - if status == 0 { - return Err(AppError::Forbidden("账户已被禁用".into())); - } - - let valid = auth::verify_password(&input.password, &stored_password) - .map_err(|_| AppError::Internal("密码验证失败".into()))?; - if !valid { - return Err(AppError::Unauthorized("手机号或密码错误".into())); - } - - let token = auth::generate_token(id, &phone)?; - - Ok(Json(json!({ - "code": 0, - "data": { - "token": token, - "user": { - "id": id, - "phone": phone, - "name": name, - } - }, - "message": "登录成功" - }))) -} - // --------------------------------------------------------------------------- // 仪表盘 // --------------------------------------------------------------------------- @@ -156,15 +110,19 @@ pub async fn h5_login( pub async fn get_dashboard( user: CurrentUser, State(state): State, + Query(query): Query, ) -> Result, AppError> { let pool = &state.mysql; // 根据角色确定组织过滤条件 - let org_id = user.organization_id; - let is_super = user.role_level >= 2; + // 超级管理员可通过 org_id 参数切换组织 + let is_super = user.role_level >= 2 && query.org_id.is_none(); + let org_id = query.org_id.or(user.organization_id); // 统计在线柜子数(带组织隔离) let online_cabinets = count_online_cabinets(pool, is_super, org_id).await?; + // 统计总通道数(带组织隔离) + let total_channels = count_total_compartments(pool, is_super, org_id).await?; // 统计充电中仓体数(带组织隔离) let charging_count = count_compartments_by_status(pool, is_super, org_id, 1).await?; @@ -175,6 +133,12 @@ pub async fn get_dashboard( // 统计故障仓体数(带组织隔离) let fault_channels = count_compartments_by_status(pool, is_super, org_id, 2).await?; + // 统计充满仓体数(status=2,带组织隔离) + let full_count = count_compartments_by_status(pool, is_super, org_id, 2).await?; + + // 统计故障数(status=3,带组织隔离) + let fault_count = count_compartments_by_status(pool, is_super, org_id, 3).await?; + // 今日充电次数(带组织隔离) let today_charge_count = count_today_charges(pool, is_super, org_id).await?; @@ -194,9 +158,12 @@ pub async fn get_dashboard( "code": 0, "data": { "online_cabinets": online_cabinets, + "total_channels": total_channels, "charging_count": charging_count, "idle_channels": idle_channels, + "full_count": full_count, "fault_channels": fault_channels, + "fault_count": fault_count, "today_charge_count": today_charge_count, "today_energy_kwh": today_energy, "today_charge_hours": today_charge_hours, @@ -233,6 +200,39 @@ async fn count_online_cabinets( Ok(count) } +/// 统计总仓体数(带组织隔离) +async fn count_total_compartments( + pool: &sqlx::MySqlPool, + is_super: bool, + org_id: Option, +) -> Result { + if !is_super && org_id.is_none() { + return Ok(0); + } + let count: i64 = if is_super { + sqlx::query_scalar( + r#"SELECT COUNT(*) FROM compartments comp + JOIN cabin_boards cb ON comp.cabin_board_id = cb.id + JOIN cabinets c ON cb.cabinet_id = c.id + WHERE c.status = 1"#, + ) + .fetch_one(pool) + .await? + } else { + sqlx::query_scalar( + r#"SELECT COUNT(*) FROM compartments comp + JOIN cabin_boards cb ON comp.cabin_board_id = cb.id + JOIN cabinets c ON cb.cabinet_id = c.id + JOIN projects p ON c.project_id = p.id + WHERE c.status = 1 AND p.organization_id = ?"#, + ) + .bind(org_id.unwrap()) + .fetch_one(pool) + .await? + }; + Ok(count) +} + /// 按状态统计仓体数(带组织隔离,通过 cabin_boards → cabinets → projects 链路) async fn count_compartments_by_status( pool: &sqlx::MySqlPool, @@ -304,13 +304,13 @@ async fn sum_today_energy( } let energy: Option = if is_super { sqlx::query_scalar( - "SELECT COALESCE(CAST(SUM(energy) AS UNSIGNED), 0) FROM charge_records WHERE DATE(start_time) = CURDATE()", + "SELECT CAST(COALESCE(SUM(energy), 0) AS SIGNED) FROM charge_records WHERE DATE(start_time) = CURDATE()", ) .fetch_one(pool) .await? } else { sqlx::query_scalar( - r#"SELECT COALESCE(CAST(SUM(cr.energy) AS UNSIGNED), 0) FROM charge_records cr + r#"SELECT CAST(COALESCE(SUM(cr.energy), 0) AS SIGNED) FROM charge_records cr INNER JOIN cabinets cab ON cr.cabinet_id = cab.id INNER JOIN projects p ON cab.project_id = p.id WHERE DATE(cr.start_time) = CURDATE() AND p.organization_id = ?"#, @@ -334,18 +334,18 @@ async fn sum_today_charge_hours( // 计算总秒数(未结束的记录用 NOW() 代替 end_time) let total_seconds: Option = if is_super { sqlx::query_scalar( - r#"SELECT COALESCE(SUM( + r#"SELECT CAST(COALESCE(SUM( TIMESTAMPDIFF(SECOND, start_time, COALESCE(end_time, NOW())) - ), 0) FROM charge_records + ), 0) AS SIGNED) FROM charge_records WHERE DATE(start_time) = CURDATE()"#, ) .fetch_one(pool) .await? } else { sqlx::query_scalar( - r#"SELECT COALESCE(SUM( + r#"SELECT CAST(COALESCE(SUM( TIMESTAMPDIFF(SECOND, cr.start_time, COALESCE(cr.end_time, NOW())) - ), 0) FROM charge_records cr + ), 0) AS SIGNED) FROM charge_records cr INNER JOIN cabinets cab ON cr.cabinet_id = cab.id INNER JOIN projects p ON cab.project_id = p.id WHERE DATE(cr.start_time) = CURDATE() AND p.organization_id = ?"#, @@ -483,44 +483,6 @@ async fn fetch_project_summaries( Ok(result) } -// --------------------------------------------------------------------------- -// 项目列表 -// --------------------------------------------------------------------------- - -/// GET /api/h5/projects — H5 项目列表(简要) -pub async fn get_projects( - user: CurrentUser, - State(state): State, -) -> Result, AppError> { - let pool = &state.mysql; - - let projects: Vec = if user.role_level >= 2 { - sqlx::query_as::<_, ProjectRow>("SELECT id, name FROM projects ORDER BY id") - .fetch_all(pool) - .await? - } else if let Some(org_id) = user.organization_id { - sqlx::query_as::<_, ProjectRow>( - "SELECT id, name FROM projects WHERE organization_id = ? ORDER BY id", - ) - .bind(org_id) - .fetch_all(pool) - .await? - } else { - vec![] - }; - - let list: Vec = projects - .into_iter() - .map(|p| json!({ "id": p.id, "name": p.name })) - .collect(); - - Ok(Json(json!({ - "code": 0, - "data": list, - "message": "ok" - }))) -} - // --------------------------------------------------------------------------- // 设备列表 // --------------------------------------------------------------------------- @@ -540,16 +502,28 @@ pub async fn get_cabinets( // 按项目过滤(同时验证组织权限) if user.role_level >= 2 { sqlx::query_as::<_, H5CabinetRow>( - "SELECT id, abstract_id, name, status, project_id FROM cabinets WHERE project_id = ?", + r#"SELECT c.id, c.abstract_id, c.name, c.status, c.imei, c.project_id, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id) as channel_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 0) as idle_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 1) as charging_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 2) as full_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 3) as fault_count + FROM cabinets c WHERE c.project_id = ?"#, ) .bind(pid) .fetch_all(pool) .await? } else if let Some(org_id) = user.organization_id { sqlx::query_as::<_, H5CabinetRow>( - r#"SELECT id, abstract_id, name, status, project_id FROM cabinets cab - INNER JOIN projects p ON cab.project_id = p.id - WHERE cab.project_id = ? AND p.organization_id = ?"#, + r#"SELECT c.id, c.abstract_id, c.name, c.status, c.imei, c.project_id, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id) as channel_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 0) as idle_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 1) as charging_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 2) as full_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 3) as fault_count + FROM cabinets c + INNER JOIN projects p ON c.project_id = p.id + WHERE c.project_id = ? AND p.organization_id = ?"#, ) .bind(pid) .bind(org_id) @@ -559,17 +533,46 @@ pub async fn get_cabinets( vec![] } } else if user.role_level >= 2 { - // 总管理员看全部 - sqlx::query_as::<_, H5CabinetRow>( - "SELECT id, abstract_id, name, status, project_id FROM cabinets", - ) - .fetch_all(pool) - .await? + // 总管理员:按组织过滤或看全部 + if let Some(oid) = query.org_id { + sqlx::query_as::<_, H5CabinetRow>( + r#"SELECT c.id, c.abstract_id, c.name, c.status, c.imei, c.project_id, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id) as channel_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 0) as idle_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 1) as charging_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 2) as full_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 3) as fault_count + FROM cabinets c + INNER JOIN projects p ON c.project_id = p.id + WHERE p.organization_id = ?"#, + ) + .bind(oid) + .fetch_all(pool) + .await? + } else { + sqlx::query_as::<_, H5CabinetRow>( + r#"SELECT c.id, c.abstract_id, c.name, c.status, c.imei, c.project_id, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id) as channel_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 0) as idle_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 1) as charging_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 2) as full_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 3) as fault_count + FROM cabinets c"#, + ) + .fetch_all(pool) + .await? + } } else if let Some(org_id) = user.organization_id { // 企业管理员只看本组织 sqlx::query_as::<_, H5CabinetRow>( - r#"SELECT id, abstract_id, name, status, project_id FROM cabinets cab - INNER JOIN projects p ON cab.project_id = p.id + r#"SELECT c.id, c.abstract_id, c.name, c.status, c.imei, c.project_id, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id) as channel_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 0) as idle_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 1) as charging_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 2) as full_count, + (SELECT COUNT(*) FROM compartments comp JOIN cabin_boards cb ON comp.cabin_board_id = cb.id WHERE cb.cabinet_id = c.id AND comp.status = 3) as fault_count + FROM cabinets c + INNER JOIN projects p ON c.project_id = p.id WHERE p.organization_id = ?"#, ) .bind(org_id) @@ -579,15 +582,44 @@ pub async fn get_cabinets( vec![] }; + // 从Redis批量查RSSI + let mut rssi_map: std::collections::HashMap> = std::collections::HashMap::new(); + if let Ok(client) = redis::Client::open(state.redis_url.as_str()) { + if let Ok(mut conn) = client.get_async_connection().await { + let mut pipe = redis::pipe(); + for r in &rows { + if let Some(ref imei) = r.imei { + pipe.cmd("HGET").arg(&format!("device:{imei}")).arg("rssi"); + } + } + if !rows.is_empty() { + let results: Vec> = pipe.query_async(&mut conn).await.unwrap_or_default(); + for (i, r) in rows.iter().enumerate() { + if r.imei.is_some() { + let val = results.get(i).and_then(|v| v.as_ref()).and_then(|v| v.parse::().ok()); + rssi_map.insert(r.imei.clone().unwrap_or_default(), val); + } + } + } + } + } + let list: Vec = rows .into_iter() .map(|r| { + let rssi = r.imei.as_ref().and_then(|imei| rssi_map.get(imei).copied()).flatten(); json!({ "id": r.id, "abstract_id": r.abstract_id, "name": r.name, "status": r.status, "project_id": r.project_id, + "channel_count": r.channel_count, + "idle_count": r.idle_count, + "charging_count": r.charging_count, + "full_count": r.full_count, + "fault_count": r.fault_count, + "rssi": rssi, }) }) .collect(); @@ -600,100 +632,22 @@ pub async fn get_cabinets( } // --------------------------------------------------------------------------- -// 设备详情 +// 设备扫码(免认证) // --------------------------------------------------------------------------- - -/// GET /api/h5/cabinets/:id — H5 设备详情(含仓控板+仓体,带权限校验) -pub async fn get_cabinet_detail( - user: CurrentUser, - State(state): State, - Path(id): Path, -) -> Result, AppError> { - let pool = &state.mysql; - - // 权限校验 - auth::check_permission(&user, "device:view")?; - - // 组织隔离:验证用户是否有权访问该设备 - verify_cabinet_access(&user, id, pool).await?; - - // 查询柜子基本信息 - let cab: Option<(i64, String, String, Option, i8)> = sqlx::query_as( - "SELECT id, abstract_id, imei, name, status FROM cabinets WHERE id = ?", - ) - .bind(id) - .fetch_optional(pool) - .await?; - - let (cab_id, abstract_id, imei, name, status) = - cab.ok_or_else(|| AppError::NotFound("设备不存在".into()))?; - - // 查询仓控板 - let boards: Vec = sqlx::query_as::<_, H5CabinBoardRow>( - "SELECT id, board_id, status FROM cabin_boards WHERE cabinet_id = ? ORDER BY board_id", - ) - .bind(cab_id) - .fetch_all(pool) - .await?; - - // 查询每个仓控板下的仓体 - let mut cabin_boards = Vec::with_capacity(boards.len()); - for board in &boards { - let comps: Vec = sqlx::query_as::<_, H5CompartmentRow>( - "SELECT id, channel_id, status FROM compartments WHERE cabin_board_id = ? ORDER BY channel_id", - ) - .bind(board.id) - .fetch_all(pool) - .await?; - - let compartments: Vec = comps - .into_iter() - .map(|c| { - json!({ - "id": c.id, - "channel_id": c.channel_id, - "status": c.status, - }) - }) - .collect(); - - cabin_boards.push(json!({ - "id": board.id, - "board_id": board.board_id, - "status": board.status, - "compartments": compartments, - })); - } - - Ok(Json(json!({ - "code": 0, - "data": { - "id": cab_id, - "abstract_id": abstract_id, - "imei": imei, - "name": name, - "status": status, - "cabin_boards": cabin_boards, - }, - "message": "ok" - }))) -} - -/// GET /api/h5/cabinets/abstract/:abstract_id — 通过抽象ID查设备(扫码入口,免认证) pub async fn get_cabinet_by_abstract_id( State(state): State, Path(abstract_id): Path, ) -> Result, AppError> { let pool = &state.mysql; - let cab: Option<(i64, String, String, Option, i8)> = sqlx::query_as( - "SELECT id, abstract_id, imei, name, status FROM cabinets WHERE abstract_id = ?", + let cab: Option<(i64, String, String, Option, Option, i8)> = sqlx::query_as( + "SELECT id, abstract_id, imei, iccid, name, status FROM cabinets WHERE abstract_id = ?", ) .bind(&abstract_id) .fetch_optional(pool) .await?; - let (cab_id, abs_id, imei, name, status) = + let (cab_id, abs_id, imei, iccid, name, status) = cab.ok_or_else(|| AppError::NotFound("设备不存在".into()))?; // 查询仓控板+仓体 @@ -732,6 +686,7 @@ pub async fn get_cabinet_by_abstract_id( "id": cab_id, "abstract_id": abs_id, "imei": imei, + "iccid": iccid, "name": name, "status": status, "cabin_boards": cabin_boards, @@ -779,17 +734,13 @@ pub async fn get_compartment_detail( } // 从 Redis 获取实时数据 - let redis_key = format!("compartment:status:{}", comp_id); - let mut conn = state.redis.clone(); - let realtime_data: Option = redis::cmd("GET") - .arg(&redis_key) - .query_async(&mut conn) - .await - .unwrap_or(None); - - let realtime: Value = realtime_data - .and_then(|s| serde_json::from_str(&s).ok()) - .unwrap_or(json!({})); + let realtime: Value = if let Ok(client) = redis::Client::open(state.redis_url.as_str()) { + if let Ok(mut conn) = client.get_async_connection().await { + let key = format!("compartment:status:{}", comp_id); + let val: Option = redis::cmd("GET").arg(&key).query_async(&mut conn).await.unwrap_or(None); + val.and_then(|s| serde_json::from_str(&s).ok()).unwrap_or(json!({})) + } else { json!({}) } + } else { json!({}) }; // 查询最近充电记录 let records: Vec = sqlx::query_as::<_, ChargeRecordBriefRow>( @@ -837,6 +788,48 @@ pub async fn get_compartment_detail( }))) } +// --------------------------------------------------------------------------- +// 扫码验证 +// --------------------------------------------------------------------------- + +/// GET /api/h5/scan/:abstract_id — 扫码验证设备访问权限 +/// +/// 根据二维码中的抽象ID查找设备,验证当前用户是否有权访问。 +/// 有权限返回设备ID和基本信息,无权限返回403。 +pub async fn scan_cabinet( + user: CurrentUser, + State(state): State, + Path(abstract_id): Path, +) -> Result, AppError> { + let pool = &state.mysql; + + // 查找设备 + let cab: Option<(i64, String, String, Option, i8)> = sqlx::query_as( + "SELECT id, abstract_id, imei, name, status FROM cabinets WHERE abstract_id = ?", + ) + .bind(&abstract_id) + .fetch_optional(pool) + .await?; + + let (cab_id, abs_id, imei, name, status) = + cab.ok_or_else(|| AppError::NotFound("设备不存在".into()))?; + + // 验证用户是否有权限访问该设备 + verify_cabinet_access(&user, cab_id, pool).await?; + + Ok(Json(json!({ + "code": 0, + "data": { + "id": cab_id, + "abstract_id": abs_id, + "imei": imei, + "name": name, + "status": status, + }, + "message": "ok" + }))) +} + // --------------------------------------------------------------------------- // 充电控制 // --------------------------------------------------------------------------- diff --git a/software/api-server/src/routes/mod.rs b/software/api-server/src/routes/mod.rs index 27c2a54..f302e37 100644 --- a/software/api-server/src/routes/mod.rs +++ b/software/api-server/src/routes/mod.rs @@ -94,24 +94,22 @@ pub fn build(state: AppState) -> Router { .route("/api/users/{id}/status", put(users::set_user_status)) // 操作日志 .route("/api/operation-logs", get(operation_logs::list_operation_logs)) - // H5 用户端接口(需认证) - .route("/api/h5/dashboard", get(h5::get_dashboard)) - .route("/api/h5/projects", get(h5::get_projects)) - .route("/api/h5/cabinets", get(h5::get_cabinets)) - .route("/api/h5/cabinets/{id}", get(h5::get_cabinet_detail)) - .route("/api/h5/compartments/{id}", get(h5::get_compartment_detail)) - .route("/api/h5/charge/start", post(h5::start_charge)) - .route("/api/h5/charge/stop", post(h5::stop_charge)) - .route("/api/h5/door/open", post(h5::open_door)) + // H5 用户端接口(与 PC 共用同一套 API 路由) + .route("/api/dashboard", get(h5::get_dashboard)) + .route("/api/cabinets", get(h5::get_cabinets)) + .route("/api/compartments/{id}", get(h5::get_compartment_detail)) + .route("/api/charge/start", post(h5::start_charge)) + .route("/api/charge/stop", post(h5::stop_charge)) + .route("/api/door/open", post(h5::open_door)) + .route("/api/cabinets/scan/{abstract_id}", get(h5::scan_cabinet)) // 认证中间件层 .layer(middleware::from_fn_with_state(state.clone(), auth_middleware)); Router::::new() .route("/api/health", get(health::health_check)) .route("/api/auth/login", post(auth::login)) - .route("/api/h5/auth/login", post(h5::h5_login)) // 扫码入口(免认证) - .route("/api/h5/cabinets/abstract/{abstract_id}", get(h5::get_cabinet_by_abstract_id)) + .route("/api/cabinets/abstract/{abstract_id}", get(h5::get_cabinet_by_abstract_id)) .merge(protected) .with_state(state) } diff --git a/software/api-server/src/routes/organizations.rs b/software/api-server/src/routes/organizations.rs index 1c4c730..73efbad 100644 --- a/software/api-server/src/routes/organizations.rs +++ b/software/api-server/src/routes/organizations.rs @@ -148,8 +148,15 @@ pub struct CabinetDetail { #[serde(skip_serializing_if = "Option::is_none")] pub abstract_id: Option, pub imei: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub iccid: Option, pub name: Option, pub status: i8, + pub rssi: Option, + pub pow_fail_dc: Option, + pub pow_fail_ac: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub address: Option, pub cabin_boards: Vec, } diff --git a/software/api-server/src/routes/users.rs b/software/api-server/src/routes/users.rs index f162d36..82bd3ec 100644 --- a/software/api-server/src/routes/users.rs +++ b/software/api-server/src/routes/users.rs @@ -37,6 +37,7 @@ struct UserRow { organization_id: Option, status: i32, created_at: NaiveDateTime, + updated_at: NaiveDateTime, } /// 创建用户请求体 @@ -169,7 +170,7 @@ pub async fn list_users( // 查询用户列表 let rows: Vec = if has_keyword { let mut q = sqlx::query_as::<_, UserRow>( - r#"SELECT u.id, u.phone, u.name, u.role, u.organization_id, u.status, u.created_at + r#"SELECT u.id, u.phone, u.name, u.role, u.organization_id, u.status, u.created_at, u.updated_at FROM users u WHERE u.phone LIKE ? OR u.name LIKE ? ORDER BY u.id DESC @@ -181,7 +182,7 @@ pub async fn list_users( .bind(offset); if let Some(org_id) = org_filter { q = sqlx::query_as::<_, UserRow>( - r#"SELECT u.id, u.phone, u.name, u.role, u.organization_id, u.status, u.created_at + r#"SELECT u.id, u.phone, u.name, u.role, u.organization_id, u.status, u.created_at, u.updated_at FROM users u WHERE (u.phone LIKE ? OR u.name LIKE ?) AND u.organization_id = ? ORDER BY u.id DESC @@ -196,7 +197,7 @@ pub async fn list_users( q.fetch_all(pool).await? } else if let Some(org_id) = org_filter { sqlx::query_as::<_, UserRow>( - r#"SELECT u.id, u.phone, u.name, u.role, u.organization_id, u.status, u.created_at + r#"SELECT u.id, u.phone, u.name, u.role, u.organization_id, u.status, u.created_at, u.updated_at FROM users u WHERE u.organization_id = ? ORDER BY u.id DESC @@ -209,7 +210,7 @@ pub async fn list_users( .await? } else { sqlx::query_as::<_, UserRow>( - r#"SELECT u.id, u.phone, u.name, u.role, u.organization_id, u.status, u.created_at + r#"SELECT u.id, u.phone, u.name, u.role, u.organization_id, u.status, u.created_at, u.updated_at FROM users u ORDER BY u.id DESC LIMIT ? OFFSET ?"#, @@ -256,6 +257,7 @@ pub async fn list_users( "organization_name": org_name, "status": r.status, "created_at": r.created_at.to_string(), + "updated_at": r.updated_at.to_string(), }) }) .collect(); diff --git a/software/h5-uniapp/.agents/skills/pinia/GENERATION.md b/software/h5-uniapp/.agents/skills/pinia/GENERATION.md new file mode 100644 index 0000000..a2c0f71 --- /dev/null +++ b/software/h5-uniapp/.agents/skills/pinia/GENERATION.md @@ -0,0 +1,5 @@ +# Generation Info + +- **Source:** `sources/pinia` +- **Git SHA:** `55dbfc5c20d4461748996aa74d8c0913e89fb98e` +- **Generated:** 2026-01-28 diff --git a/software/h5-uniapp/.agents/skills/pinia/LICENSE.md b/software/h5-uniapp/.agents/skills/pinia/LICENSE.md new file mode 100644 index 0000000..29f64e0 --- /dev/null +++ b/software/h5-uniapp/.agents/skills/pinia/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025-PRESENT Anthony Fu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/software/h5-uniapp/.agents/skills/pinia/SKILL.md b/software/h5-uniapp/.agents/skills/pinia/SKILL.md new file mode 100644 index 0000000..89cfa7d --- /dev/null +++ b/software/h5-uniapp/.agents/skills/pinia/SKILL.md @@ -0,0 +1,59 @@ +--- +name: pinia +description: Pinia official Vue state management library, type-safe and extensible. Use when defining stores, working with state/getters/actions, or implementing store patterns in Vue apps. +metadata: + author: Anthony Fu + version: "2026.1.28" + source: Generated from https://github.com/vuejs/pinia, scripts located at https://github.com/antfu/skills +--- + +# Pinia + +Pinia is the official state management library for Vue, designed to be intuitive and type-safe. It supports both Options API and Composition API styles, with first-class TypeScript support and devtools integration. + +> The skill is based on Pinia v3.0.4, generated at 2026-01-28. + +## Core References + +| Topic | Description | Reference | +|-------|-------------|-----------| +| Stores | Defining stores, state, getters, actions, storeToRefs, subscriptions | [core-stores](references/core-stores.md) | + +## Features + +### Extensibility + +| Topic | Description | Reference | +|-------|-------------|-----------| +| Plugins | Extend stores with custom properties, state, and behavior | [features-plugins](references/features-plugins.md) | + +### Composability + +| Topic | Description | Reference | +|-------|-------------|-----------| +| Composables | Using Vue composables within stores (VueUse, etc.) | [features-composables](references/features-composables.md) | +| Composing Stores | Store-to-store communication, avoiding circular dependencies | [features-composing-stores](references/features-composing-stores.md) | + +## Best Practices + +| Topic | Description | Reference | +|-------|-------------|-----------| +| Testing | Unit testing with @pinia/testing, mocking, stubbing | [best-practices-testing](references/best-practices-testing.md) | +| Outside Components | Using stores in navigation guards, plugins, middlewares | [best-practices-outside-component](references/best-practices-outside-component.md) | + +## Advanced + +| Topic | Description | Reference | +|-------|-------------|-----------| +| SSR | Server-side rendering, state hydration | [advanced-ssr](references/advanced-ssr.md) | +| Nuxt | Nuxt integration, auto-imports, SSR best practices | [advanced-nuxt](references/advanced-nuxt.md) | +| HMR | Hot module replacement for development | [advanced-hmr](references/advanced-hmr.md) | + +## Key Recommendations + +- **Prefer Setup Stores** for complex logic, composables, and watchers +- **Use `storeToRefs()`** when destructuring state/getters to preserve reactivity +- **Actions can be destructured directly** - they're bound to the store +- **Call stores inside functions** not at module scope, especially for SSR +- **Add HMR support** to each store for better development experience +- **Use `@pinia/testing`** for component tests with mocked stores diff --git a/software/h5-uniapp/.agents/skills/pinia/SYNC.md b/software/h5-uniapp/.agents/skills/pinia/SYNC.md new file mode 100644 index 0000000..622ef75 --- /dev/null +++ b/software/h5-uniapp/.agents/skills/pinia/SYNC.md @@ -0,0 +1,5 @@ +# Sync Info + +- **Source:** `vendor/antfu/skills/pinia` +- **Git SHA:** `4086f59fa12d308fb05592ca8ce1704fbb940aa3` +- **Synced:** 2026-01-30 diff --git a/software/h5-uniapp/.agents/skills/pinia/references/advanced-hmr.md b/software/h5-uniapp/.agents/skills/pinia/references/advanced-hmr.md new file mode 100644 index 0000000..3eef5c7 --- /dev/null +++ b/software/h5-uniapp/.agents/skills/pinia/references/advanced-hmr.md @@ -0,0 +1,61 @@ +--- +name: hot-module-replacement +description: Enable HMR to preserve store state during development +--- + +# Hot Module Replacement (HMR) + +Pinia supports HMR to edit stores without page reload, preserving existing state. + +## Setup + +Add this snippet after each store definition: + +```ts +import { defineStore, acceptHMRUpdate } from 'pinia' + +export const useAuth = defineStore('auth', { + // store options... +}) + +if (import.meta.hot) { + import.meta.hot.accept(acceptHMRUpdate(useAuth, import.meta.hot)) +} +``` + +## Setup Store Example + +```ts +import { defineStore, acceptHMRUpdate } from 'pinia' + +export const useCounterStore = defineStore('counter', () => { + const count = ref(0) + const increment = () => count.value++ + return { count, increment } +}) + +if (import.meta.hot) { + import.meta.hot.accept(acceptHMRUpdate(useCounterStore, import.meta.hot)) +} +``` + +## Bundler Support + +- **Vite:** Officially supported via `import.meta.hot` +- **Webpack:** Uses `import.meta.webpackHot` +- Any bundler implementing the `import.meta.hot` spec should work + +## Nuxt + +With `@pinia/nuxt`, `acceptHMRUpdate` is auto-imported but you still need to add the HMR snippet manually. + +## Benefits + +- Edit store logic without losing state +- Add/remove state, actions, and getters on the fly +- Faster development iteration + + diff --git a/software/h5-uniapp/.agents/skills/pinia/references/advanced-nuxt.md b/software/h5-uniapp/.agents/skills/pinia/references/advanced-nuxt.md new file mode 100644 index 0000000..569da63 --- /dev/null +++ b/software/h5-uniapp/.agents/skills/pinia/references/advanced-nuxt.md @@ -0,0 +1,119 @@ +--- +name: nuxt-integration +description: Using Pinia with Nuxt - auto-imports, SSR, and best practices +--- + +# Nuxt Integration + +Pinia works seamlessly with Nuxt 3/4, handling SSR, serialization, and XSS protection automatically. + +## Installation + +```bash +npx nuxi@latest module add pinia +``` + +This installs both `@pinia/nuxt` and `pinia`. If `pinia` isn't installed, add it manually. + +> **npm users:** If you get `ERESOLVE unable to resolve dependency tree`, add to `package.json`: +> ```json +> "overrides": { "vue": "latest" } +> ``` + +## Configuration + +```ts +// nuxt.config.ts +export default defineNuxtConfig({ + modules: ['@pinia/nuxt'], +}) +``` + +## Auto Imports + +These are automatically available: +- `usePinia()` - get pinia instance +- `defineStore()` - define stores +- `storeToRefs()` - extract reactive refs +- `acceptHMRUpdate()` - HMR support + +**All stores in `app/stores/` (Nuxt 4) or `stores/` are auto-imported.** + +### Custom Store Directories + +```ts +// nuxt.config.ts +export default defineNuxtConfig({ + modules: ['@pinia/nuxt'], + pinia: { + storesDirs: ['./stores/**', './custom-folder/stores/**'], + }, +}) +``` + +## Fetching Data in Pages + +Use `callOnce()` for SSR-friendly data fetching: + +```vue + +``` + +### Refetch on Navigation + +```vue + +``` + +## Using Stores Outside Components + +In navigation guards, middlewares, or other stores, pass the `pinia` instance: + +```ts +// middleware/auth.ts +export default defineNuxtRouteMiddleware((to) => { + const nuxtApp = useNuxtApp() + const store = useStore(nuxtApp.$pinia) + + if (to.meta.requiresAuth && !store.isLoggedIn) { + return navigateTo('/login') + } +}) +``` + +Most of the time, you don't need this - just use stores in components or other injection-aware contexts. + +## Pinia Plugins with Nuxt + +Create a Nuxt plugin: + +```ts +// plugins/myPiniaPlugin.ts +import { PiniaPluginContext } from 'pinia' + +function MyPiniaPlugin({ store }: PiniaPluginContext) { + store.$subscribe((mutation) => { + console.log(`[🍍 ${mutation.storeId}]: ${mutation.type}`) + }) + return { creationTime: new Date() } +} + +export default defineNuxtPlugin(({ $pinia }) => { + $pinia.use(MyPiniaPlugin) +}) +``` + + diff --git a/software/h5-uniapp/.agents/skills/pinia/references/advanced-ssr.md b/software/h5-uniapp/.agents/skills/pinia/references/advanced-ssr.md new file mode 100644 index 0000000..2972f3a --- /dev/null +++ b/software/h5-uniapp/.agents/skills/pinia/references/advanced-ssr.md @@ -0,0 +1,121 @@ +--- +name: server-side-rendering +description: SSR setup, state hydration, and avoiding cross-request state pollution +--- + +# Server Side Rendering (SSR) + +Pinia works with SSR when stores are called at the top of `setup`, getters, or actions. + +> **Using Nuxt?** See the [Nuxt integration](advanced-nuxt.md) instead. + +## Basic Usage + +```vue + +``` + +## Using Store Outside setup() + +Pass the `pinia` instance explicitly: + +```ts +const pinia = createPinia() +const app = createApp(App) +app.use(router) +app.use(pinia) + +router.beforeEach((to) => { + // ✅ Pass pinia for correct SSR context + const main = useMainStore(pinia) + + if (to.meta.requiresAuth && !main.isLoggedIn) { + return '/login' + } +}) +``` + +## serverPrefetch() + +Access pinia via `this.$pinia`: + +```ts +export default { + serverPrefetch() { + const store = useStore(this.$pinia) + return store.fetchData() + }, +} +``` + +## onServerPrefetch() + +Works normally: + +```vue + +``` + +## State Hydration + +Serialize state on server and hydrate on client. + +### Server Side + +Use [devalue](https://github.com/Rich-Harris/devalue) for XSS-safe serialization: + +```ts +import devalue from 'devalue' +import { createPinia } from 'pinia' + +const pinia = createPinia() +const app = createApp(App) +app.use(router) +app.use(pinia) + +// After rendering, state is available +const serializedState = devalue(pinia.state.value) +// Inject into HTML as global variable +``` + +### Client Side + +Hydrate before any `useStore()` call: + +```ts +const pinia = createPinia() +const app = createApp(App) +app.use(pinia) + +// Hydrate from serialized state (e.g., from window.__pinia) +if (typeof window !== 'undefined') { + pinia.state.value = JSON.parse(window.__pinia) +} +``` + +## SSR Examples + +- [Vitesse template](https://github.com/antfu/vitesse/blob/main/src/modules/pinia.ts) +- [vite-plugin-ssr](https://vite-plugin-ssr.com/pinia) + +## Key Points + +1. Call stores inside functions, not at module scope +2. Pass `pinia` instance when using stores outside components in SSR +3. Hydrate state before calling any `useStore()` +4. Use `devalue` or similar for safe serialization +5. Avoid cross-request state pollution by creating fresh pinia per request + + diff --git a/software/h5-uniapp/.agents/skills/pinia/references/best-practices-outside-component.md b/software/h5-uniapp/.agents/skills/pinia/references/best-practices-outside-component.md new file mode 100644 index 0000000..126f7a6 --- /dev/null +++ b/software/h5-uniapp/.agents/skills/pinia/references/best-practices-outside-component.md @@ -0,0 +1,115 @@ +--- +name: using-stores-outside-components +description: Correctly using stores in navigation guards, plugins, and other non-component contexts +--- + +# Using Stores Outside Components + +Stores need the `pinia` instance, which is automatically injected in components. Outside components, you may need to provide it manually. + +## Single Page Applications + +Call stores **after** pinia is installed: + +```ts +import { useUserStore } from '@/stores/user' +import { createPinia } from 'pinia' +import { createApp } from 'vue' +import App from './App.vue' + +// ❌ Fails - pinia not created yet +const userStore = useUserStore() + +const pinia = createPinia() +const app = createApp(App) +app.use(pinia) + +// ✅ Works - pinia is active +const userStore = useUserStore() +``` + +## Navigation Guards + +**Wrong:** Call at module level + +```ts +import { createRouter } from 'vue-router' +const router = createRouter({ /* ... */ }) + +// ❌ May fail depending on import order +const store = useUserStore() + +router.beforeEach((to) => { + if (store.isLoggedIn) { /* ... */ } +}) +``` + +**Correct:** Call inside the guard + +```ts +router.beforeEach((to) => { + // ✅ Called after pinia is installed + const store = useUserStore() + + if (to.meta.requiresAuth && !store.isLoggedIn) { + return '/login' + } +}) +``` + +## SSR Applications + +Always pass the `pinia` instance to `useStore()`: + +```ts +const pinia = createPinia() +const app = createApp(App) +app.use(router) +app.use(pinia) + +router.beforeEach((to) => { + // ✅ Pass pinia instance + const main = useMainStore(pinia) + + if (to.meta.requiresAuth && !main.isLoggedIn) { + return '/login' + } +}) +``` + +## serverPrefetch() + +Access pinia via `this.$pinia`: + +```ts +export default { + serverPrefetch() { + const store = useStore(this.$pinia) + return store.fetchData() + }, +} +``` + +## onServerPrefetch() + +Works normally in ` +``` + +## Key Takeaway + +Defer `useStore()` calls to functions that run after pinia is installed, rather than calling at module scope. + + diff --git a/software/h5-uniapp/.agents/skills/pinia/references/best-practices-testing.md b/software/h5-uniapp/.agents/skills/pinia/references/best-practices-testing.md new file mode 100644 index 0000000..7227cd4 --- /dev/null +++ b/software/h5-uniapp/.agents/skills/pinia/references/best-practices-testing.md @@ -0,0 +1,212 @@ +--- +name: testing +description: Unit testing stores and components with @pinia/testing +--- + +# Testing Stores + +## Unit Testing Stores + +Create a fresh pinia instance for each test: + +```ts +import { setActivePinia, createPinia } from 'pinia' +import { useCounterStore } from '../src/stores/counter' + +describe('Counter Store', () => { + beforeEach(() => { + setActivePinia(createPinia()) + }) + + it('increments', () => { + const counter = useCounterStore() + expect(counter.n).toBe(0) + counter.increment() + expect(counter.n).toBe(1) + }) +}) +``` + +### With Plugins + +```ts +import { setActivePinia, createPinia } from 'pinia' +import { createApp } from 'vue' +import { somePlugin } from '../src/stores/plugin' + +const app = createApp({}) + +beforeEach(() => { + const pinia = createPinia().use(somePlugin) + app.use(pinia) + setActivePinia(pinia) +}) +``` + +## Testing Components + +Install `@pinia/testing`: + +```bash +npm i -D @pinia/testing +``` + +Use `createTestingPinia()`: + +```ts +import { mount } from '@vue/test-utils' +import { createTestingPinia } from '@pinia/testing' +import { useSomeStore } from '@/stores/myStore' + +const wrapper = mount(Counter, { + global: { + plugins: [createTestingPinia()], + }, +}) + +const store = useSomeStore() + +// Manipulate state directly +store.name = 'new name' +store.$patch({ name: 'new name' }) + +// Actions are stubbed by default +store.someAction() +expect(store.someAction).toHaveBeenCalledTimes(1) +``` + +## Initial State + +Set initial state for tests: + +```ts +const wrapper = mount(Counter, { + global: { + plugins: [ + createTestingPinia({ + initialState: { + counter: { n: 20 }, // Store name → initial state + }, + }), + ], + }, +}) +``` + +## Action Stubbing + +### Execute Real Actions + +```ts +createTestingPinia({ stubActions: false }) +``` + +### Selective Stubbing + +```ts +// Only stub specific actions +createTestingPinia({ + stubActions: ['increment', 'reset'], +}) + +// Or use a function +createTestingPinia({ + stubActions: (actionName, store) => { + if (actionName.startsWith('set')) return true + return false + }, +}) +``` + +### Mock Action Return Values + +```ts +import type { Mock } from 'vitest' + +// After getting store +store.someAction.mockResolvedValue('mocked value') +``` + +## Mocking Getters + +Getters are writable in tests: + +```ts +const pinia = createTestingPinia() +const counter = useCounterStore(pinia) + +counter.double = 3 // Override computed value + +// Reset to default behavior +counter.double = undefined +counter.double // Now computed normally +``` + +## Custom Spy Function + +If not using Jest/Vitest with globals: + +```ts +import { vi } from 'vitest' + +createTestingPinia({ + createSpy: vi.fn, +}) +``` + +With Sinon: + +```ts +import sinon from 'sinon' + +createTestingPinia({ + createSpy: sinon.spy, +}) +``` + +## Pinia Plugins in Tests + +Pass plugins to `createTestingPinia()`: + +```ts +import { somePlugin } from '../src/stores/plugin' + +createTestingPinia({ + stubActions: false, + plugins: [somePlugin], +}) +``` + +**Don't use** `testingPinia.use(MyPlugin)` - pass plugins in options. + +## Type-Safe Mocked Store + +```ts +import type { Mock } from 'vitest' +import type { Store, StoreDefinition } from 'pinia' + +function mockedStore unknown>( + useStore: TStoreDef +): TStoreDef extends StoreDefinition + ? Store, { + [K in keyof Actions]: Actions[K] extends (...args: any[]) => any + ? Mock + : Actions[K] + }> + : ReturnType { + return useStore() as any +} + +// Usage +const store = mockedStore(useSomeStore) +store.someAction.mockResolvedValue('value') // Typed! +``` + +## E2E Tests + +No special handling needed - Pinia works normally. + + diff --git a/software/h5-uniapp/.agents/skills/pinia/references/core-stores.md b/software/h5-uniapp/.agents/skills/pinia/references/core-stores.md new file mode 100644 index 0000000..ea6a72b --- /dev/null +++ b/software/h5-uniapp/.agents/skills/pinia/references/core-stores.md @@ -0,0 +1,389 @@ +--- +name: stores +description: Defining stores, state, getters, and actions in Pinia +--- + +# Pinia Stores + +Stores are defined using `defineStore()` with a unique name. Each store has three core concepts: **state**, **getters**, and **actions**. + +## Defining Stores + +### Option Stores + +Similar to Vue's Options API: + +```ts +import { defineStore } from 'pinia' + +export const useCounterStore = defineStore('counter', { + state: () => ({ + count: 0, + name: 'Eduardo', + }), + getters: { + doubleCount: (state) => state.count * 2, + }, + actions: { + increment() { + this.count++ + }, + }, +}) +``` + +Think of `state` as `data`, `getters` as `computed`, and `actions` as `methods`. + +### Setup Stores (Recommended) + +Uses Composition API syntax - more flexible and powerful: + +```ts +import { ref, computed } from 'vue' +import { defineStore } from 'pinia' + +export const useCounterStore = defineStore('counter', () => { + const count = ref(0) + const name = ref('Eduardo') + const doubleCount = computed(() => count.value * 2) + + function increment() { + count.value++ + } + + return { count, name, doubleCount, increment } +}) +``` + +In Setup Stores: `ref()` → state, `computed()` → getters, `function()` → actions. + +**Important:** You must return all state properties for Pinia to track them. + +### Using Stores + +```vue + +``` + +### Destructuring with storeToRefs + +```vue + +``` + +--- + +## State + +State is defined as a function returning the initial state. + +### TypeScript + +Type inference works automatically. For complex types: + +```ts +interface UserInfo { + name: string + age: number +} + +export const useUserStore = defineStore('user', { + state: () => ({ + userList: [] as UserInfo[], + user: null as UserInfo | null, + }), +}) +``` + +Or use an interface for the return type: + +```ts +interface State { + userList: UserInfo[] + user: UserInfo | null +} + +export const useUserStore = defineStore('user', { + state: (): State => ({ + userList: [], + user: null, + }), +}) +``` + +### Accessing and Modifying + +```ts +const store = useStore() +store.count++ +``` + +```vue + +``` + +### Mutating with $patch + +Apply multiple changes at once: + +```ts +// Object syntax +store.$patch({ + count: store.count + 1, + name: 'DIO', +}) + +// Function syntax (for complex mutations) +store.$patch((state) => { + state.items.push({ name: 'shoes', quantity: 1 }) + state.hasChanged = true +}) +``` + +### Resetting State + +Option Stores have built-in `$reset()`. For Setup Stores, implement your own: + +```ts +export const useCounterStore = defineStore('counter', () => { + const count = ref(0) + + function $reset() { + count.value = 0 + } + + return { count, $reset } +}) +``` + +### Subscribing to State Changes + +```ts +cartStore.$subscribe((mutation, state) => { + mutation.type // 'direct' | 'patch object' | 'patch function' + mutation.storeId // 'cart' + mutation.payload // patch object (only for 'patch object') + + localStorage.setItem('cart', JSON.stringify(state)) +}) + +// Options +cartStore.$subscribe(callback, { flush: 'sync' }) // Immediate +cartStore.$subscribe(callback, { detached: true }) // Keep after unmount +``` + +--- + +## Getters + +Getters are computed values, equivalent to Vue's `computed()`. + +### Basic Getters + +```ts +getters: { + doubleCount: (state) => state.count * 2, +} +``` + +### Accessing Other Getters + +Use `this` with explicit return type: + +```ts +getters: { + doubleCount: (state) => state.count * 2, + doublePlusOne(): number { + return this.doubleCount + 1 + }, +}, +``` + +### Getters with Arguments + +Return a function (note: loses caching): + +```ts +getters: { + getUserById: (state) => { + return (userId: string) => state.users.find((user) => user.id === userId) + }, +}, +``` + +Cache within parameterized getters: + +```ts +getters: { + getActiveUserById(state) { + const activeUsers = state.users.filter((user) => user.active) + return (userId: string) => activeUsers.find((user) => user.id === userId) + }, +}, +``` + +### Accessing Other Stores in Getters + +```ts +import { useOtherStore } from './other-store' + +getters: { + combined(state) { + const otherStore = useOtherStore() + return state.localData + otherStore.data + }, +}, +``` + +--- + +## Actions + +Actions are methods for business logic. Unlike getters, they can be asynchronous. + +### Defining Actions + +```ts +actions: { + increment() { + this.count++ + }, + randomizeCounter() { + this.count = Math.round(100 * Math.random()) + }, +}, +``` + +### Async Actions + +```ts +actions: { + async registerUser(login: string, password: string) { + try { + this.userData = await api.post({ login, password }) + } catch (error) { + return error + } + }, +}, +``` + +### Accessing Other Stores in Actions + +```ts +import { useAuthStore } from './auth-store' + +actions: { + async fetchUserPreferences() { + const auth = useAuthStore() + if (auth.isAuthenticated) { + this.preferences = await fetchPreferences() + } + }, +}, +``` + +**SSR:** Call all `useStore()` before any `await`: + +```ts +async orderCart() { + // ✅ Call stores before await + const user = useUserStore() + + await apiOrderCart(user.token, this.items) + // ❌ Don't call useStore() after await in SSR +} +``` + +### Subscribing to Actions + +```ts +const unsubscribe = someStore.$onAction( + ({ name, store, args, after, onError }) => { + const startTime = Date.now() + console.log(`Start "${name}" with params [${args.join(', ')}]`) + + after((result) => { + console.log(`Finished "${name}" after ${Date.now() - startTime}ms`) + }) + + onError((error) => { + console.warn(`Failed "${name}": ${error}`) + }) + } +) + +unsubscribe() // Cleanup +``` + +Keep subscription after component unmount: + +```ts +someStore.$onAction(callback, true) +``` + +--- + +## Options API Helpers + +```ts +import { mapState, mapWritableState, mapActions } from 'pinia' +import { useCounterStore } from '../stores/counter' + +export default { + computed: { + // Readonly state/getters + ...mapState(useCounterStore, ['count', 'doubleCount']), + // Writable state + ...mapWritableState(useCounterStore, ['count']), + }, + methods: { + ...mapActions(useCounterStore, ['increment']), + }, +} +``` + +--- + +## Accessing Global Providers in Setup Stores + +```ts +import { inject } from 'vue' +import { useRoute } from 'vue-router' +import { defineStore } from 'pinia' + +export const useSearchFilters = defineStore('search-filters', () => { + const route = useRoute() + const appProvided = inject('appProvided') + + // Don't return these - access them directly in components + return { /* ... */ } +}) +``` + + diff --git a/software/h5-uniapp/.agents/skills/pinia/references/features-composables.md b/software/h5-uniapp/.agents/skills/pinia/references/features-composables.md new file mode 100644 index 0000000..79f7d94 --- /dev/null +++ b/software/h5-uniapp/.agents/skills/pinia/references/features-composables.md @@ -0,0 +1,114 @@ +--- +name: composables-in-stores +description: Using Vue composables within Pinia stores +--- + +# Composables in Stores + +Pinia stores can leverage Vue composables for reusable stateful logic. + +## Option Stores + +Call composables inside the `state` property, but only those returning writable refs: + +```ts +import { defineStore } from 'pinia' +import { useLocalStorage } from '@vueuse/core' + +export const useAuthStore = defineStore('auth', { + state: () => ({ + user: useLocalStorage('pinia/auth/login', 'bob'), + }), +}) +``` + +**Works:** Composables returning `ref()`: +- `useLocalStorage` +- `useAsyncState` + +**Doesn't work in Option Stores:** +- Composables exposing functions +- Composables exposing readonly data + +## Setup Stores + +More flexible - can use almost any composable: + +```ts +import { defineStore } from 'pinia' +import { useMediaControls } from '@vueuse/core' +import { ref } from 'vue' + +export const useVideoPlayer = defineStore('video', () => { + const videoElement = ref() + const src = ref('/data/video.mp4') + const { playing, volume, currentTime, togglePictureInPicture } = + useMediaControls(videoElement, { src }) + + function loadVideo(element: HTMLVideoElement, newSrc: string) { + videoElement.value = element + src.value = newSrc + } + + return { + src, + playing, + volume, + currentTime, + loadVideo, + togglePictureInPicture, + } +}) +``` + +**Note:** Don't return non-serializable DOM refs like `videoElement` - they're internal implementation details. + +## SSR Considerations + +### Option Stores with hydrate() + +Define a `hydrate()` function to handle client-side hydration: + +```ts +import { defineStore } from 'pinia' +import { useLocalStorage } from '@vueuse/core' + +export const useAuthStore = defineStore('auth', { + state: () => ({ + user: useLocalStorage('pinia/auth/login', 'bob'), + }), + + hydrate(state, initialState) { + // Ignore server state, read from browser + state.user = useLocalStorage('pinia/auth/login', 'bob') + }, +}) +``` + +### Setup Stores with skipHydrate() + +Mark state that shouldn't hydrate from server: + +```ts +import { defineStore, skipHydrate } from 'pinia' +import { useEyeDropper, useLocalStorage } from '@vueuse/core' + +export const useColorStore = defineStore('colors', () => { + const { isSupported, open, sRGBHex } = useEyeDropper() + const lastColor = useLocalStorage('lastColor', sRGBHex) + + return { + // Skip hydration for client-only state + lastColor: skipHydrate(lastColor), + open, // Function - no hydration needed + isSupported, // Boolean - not reactive + } +}) +``` + +`skipHydrate()` only applies to state properties (refs), not functions or non-reactive values. + + diff --git a/software/h5-uniapp/.agents/skills/pinia/references/features-composing-stores.md b/software/h5-uniapp/.agents/skills/pinia/references/features-composing-stores.md new file mode 100644 index 0000000..948f8b5 --- /dev/null +++ b/software/h5-uniapp/.agents/skills/pinia/references/features-composing-stores.md @@ -0,0 +1,134 @@ +--- +name: composing-stores +description: Store-to-store communication and avoiding circular dependencies +--- + +# Composing Stores + +Stores can use each other for shared state and logic. + +## Rule: Avoid Circular Dependencies + +Two stores cannot directly read each other's state during setup: + +```ts +// ❌ Infinite loop +const useX = defineStore('x', () => { + const y = useY() + y.name // Don't read here! + return { name: ref('X') } +}) + +const useY = defineStore('y', () => { + const x = useX() + x.name // Don't read here! + return { name: ref('Y') } +}) +``` + +**Solution:** Read in getters, computed, or actions: + +```ts +const useX = defineStore('x', () => { + const y = useY() + + // ✅ Read in computed/actions + function doSomething() { + const yName = y.name + } + + return { name: ref('X'), doSomething } +}) +``` + +## Setup Stores: Use Store at Top + +```ts +import { defineStore } from 'pinia' +import { useUserStore } from './user' + +export const useCartStore = defineStore('cart', () => { + const user = useUserStore() + const list = ref([]) + + const summary = computed(() => { + return `Hi ${user.name}, you have ${list.value.length} items` + }) + + function purchase() { + return apiPurchase(user.id, list.value) + } + + return { list, summary, purchase } +}) +``` + +## Shared Getters + +Call `useStore()` inside a getter: + +```ts +import { useUserStore } from './user' + +export const useCartStore = defineStore('cart', { + getters: { + summary(state) { + const user = useUserStore() + return `Hi ${user.name}, you have ${state.list.length} items` + }, + }, +}) +``` + +## Shared Actions + +Call `useStore()` inside an action: + +```ts +import { useUserStore } from './user' +import { apiOrderCart } from './api' + +export const useCartStore = defineStore('cart', { + actions: { + async orderCart() { + const user = useUserStore() + + try { + await apiOrderCart(user.token, this.items) + this.emptyCart() + } catch (err) { + displayError(err) + } + }, + }, +}) +``` + +## SSR: Call Stores Before Await + +In async actions, call all stores before any `await`: + +```ts +actions: { + async orderCart() { + // ✅ All useStore() calls before await + const user = useUserStore() + const analytics = useAnalyticsStore() + + try { + await apiOrderCart(user.token, this.items) + // ❌ Don't call useStore() after await (SSR issue) + // const otherStore = useOtherStore() + } catch (err) { + displayError(err) + } + }, +} +``` + +This ensures the correct Pinia instance is used during SSR. + + diff --git a/software/h5-uniapp/.agents/skills/pinia/references/features-plugins.md b/software/h5-uniapp/.agents/skills/pinia/references/features-plugins.md new file mode 100644 index 0000000..c4355ac --- /dev/null +++ b/software/h5-uniapp/.agents/skills/pinia/references/features-plugins.md @@ -0,0 +1,203 @@ +--- +name: plugins +description: Extend stores with custom properties, methods, and behavior +--- + +# Plugins + +Plugins extend all stores with custom properties, methods, or behavior. + +## Basic Plugin + +```ts +import { createPinia } from 'pinia' + +function SecretPiniaPlugin() { + return { secret: 'the cake is a lie' } +} + +const pinia = createPinia() +pinia.use(SecretPiniaPlugin) + +// In any store +const store = useStore() +store.secret // 'the cake is a lie' +``` + +## Plugin Context + +Plugins receive a context object: + +```ts +import { PiniaPluginContext } from 'pinia' + +export function myPiniaPlugin(context: PiniaPluginContext) { + context.pinia // pinia instance + context.app // Vue app instance + context.store // store being augmented + context.options // store definition options +} +``` + +## Adding Properties + +Return an object to add properties (tracked in devtools): + +```ts +pinia.use(() => ({ hello: 'world' })) +``` + +Or set directly on store: + +```ts +pinia.use(({ store }) => { + store.hello = 'world' + // For devtools visibility in dev mode + if (process.env.NODE_ENV === 'development') { + store._customProperties.add('hello') + } +}) +``` + +## Adding State + +Add to both `store` and `store.$state` for SSR/devtools: + +```ts +import { toRef, ref } from 'vue' + +pinia.use(({ store }) => { + if (!store.$state.hasOwnProperty('hasError')) { + const hasError = ref(false) + store.$state.hasError = hasError + } + store.hasError = toRef(store.$state, 'hasError') +}) +``` + +## Adding External Properties + +Wrap non-reactive objects with `markRaw()`: + +```ts +import { markRaw } from 'vue' +import { router } from './router' + +pinia.use(({ store }) => { + store.router = markRaw(router) +}) +``` + +## Custom Store Options + +Define custom options consumed by plugins: + +```ts +// Store definition +defineStore('search', { + actions: { + searchContacts() { /* ... */ }, + }, + debounce: { + searchContacts: 300, + }, +}) + +// Plugin reads custom option +import debounce from 'lodash/debounce' + +pinia.use(({ options, store }) => { + if (options.debounce) { + return Object.keys(options.debounce).reduce((acc, action) => { + acc[action] = debounce(store[action], options.debounce[action]) + return acc + }, {}) + } +}) +``` + +For Setup Stores, pass options as third argument: + +```ts +defineStore( + 'search', + () => { /* ... */ }, + { + debounce: { searchContacts: 300 }, + } +) +``` + +## TypeScript Augmentation + +### Custom Properties + +```ts +import 'pinia' +import type { Router } from 'vue-router' + +declare module 'pinia' { + export interface PiniaCustomProperties { + router: Router + hello: string + } +} +``` + +### Custom State + +```ts +declare module 'pinia' { + export interface PiniaCustomStateProperties { + hasError: boolean + } +} +``` + +### Custom Options + +```ts +declare module 'pinia' { + export interface DefineStoreOptionsBase { + debounce?: Partial, number>> + } +} +``` + +## Subscribe in Plugins + +```ts +pinia.use(({ store }) => { + store.$subscribe(() => { + // React to state changes + }) + store.$onAction(() => { + // React to actions + }) +}) +``` + +## Nuxt Plugin + +Create a Nuxt plugin to add Pinia plugins: + +```ts +// plugins/myPiniaPlugin.ts +import { PiniaPluginContext } from 'pinia' + +function MyPiniaPlugin({ store }: PiniaPluginContext) { + store.$subscribe((mutation) => { + console.log(`[🍍 ${mutation.storeId}]: ${mutation.type}`) + }) + return { creationTime: new Date() } +} + +export default defineNuxtPlugin(({ $pinia }) => { + $pinia.use(MyPiniaPlugin) +}) +``` + + diff --git a/software/h5-uniapp/.agents/skills/uni-app/GENERATION.md b/software/h5-uniapp/.agents/skills/uni-app/GENERATION.md new file mode 100644 index 0000000..be7a721 --- /dev/null +++ b/software/h5-uniapp/.agents/skills/uni-app/GENERATION.md @@ -0,0 +1,54 @@ +# Generation Info + +- **Source:** `sources/uniapp` +- **Git SHA:** `91423a240a5b0714fc5e7e13bd3f93a79b8cc8d7` +- **Generated:** 2026-01-30 + +## Generated Skills + +| Category | File | Description | +|----------|------|-------------| +| Core | core-framework.md | Framework basics, platform support, condition compilation | +| Core | core-view-components.md | Container components: view, scroll-view, swiper, movable-area | +| Core | core-form-components.md | Form controls: input, textarea, picker, checkbox, radio | +| Features | feature-media-components.md | Media: image, video, camera, map, live-player | +| Features | feature-navigation.md | Navigation: navigator, routing APIs | +| Features | feature-network.md | Network: request, upload, download, WebSocket | +| Features | feature-storage.md | Storage: localStorage, file system | +| Features | feature-system-info.md | System: device info, network, screen, vibration | +| Features | feature-file-operations.md | Files: image/video selection, file operations | +| Features | feature-location.md | Location: geolocation, map component | +| Features | feature-ui-feedback.md | UI: toast, modal, loading, action sheet | +| Features | feature-lifecycle.md | Lifecycle: app and page hooks | +| Config | config-pages.md | pages.json configuration | +| Config | config-manifest.md | manifest.json configuration | + +## Source Documentation Structure + +``` +sources/uniapp/docs/ +├── README.md # Framework overview +├── api/ # API documentation +│ ├── README.md # API overview +│ ├── request/ # Network APIs +│ ├── storage/ # Storage APIs +│ ├── media/ # Media APIs +│ ├── file/ # File APIs +│ ├── location/ # Location APIs +│ ├── ui/ # UI APIs +│ └── system/ # System APIs +├── component/ # Component documentation +├── collocation/ # Configuration +│ ├── pages.md # pages.json +│ ├── manifest.md # manifest.json +│ └── manifest-app.md # App-specific manifest +├── tutorial/ # Tutorials +└── quickstart.md # Getting started +``` + +## Notes + +- Skills focus on practical usage patterns and code examples +- Platform-specific differences are highlighted where relevant +- Configuration files include common use cases for each platform +- API skills include both callback and Promise usage patterns diff --git a/software/h5-uniapp/.agents/skills/uni-app/SKILL.md b/software/h5-uniapp/.agents/skills/uni-app/SKILL.md new file mode 100644 index 0000000..f2bae87 --- /dev/null +++ b/software/h5-uniapp/.agents/skills/uni-app/SKILL.md @@ -0,0 +1,113 @@ +--- +name: uni-app +description: Comprehensive skill reference for uni-app cross-platform development framework +metadata: + author: FlippeDround + version: "2026.1.30" + source: Generated from https://gitcode.com/dcloud/unidocs-zh, skills located at https://github.com/antfu/skills +--- + +> The skill is based on uni-app documentation, generated at 2026-01-30. + +uni-app is a Vue.js-based cross-platform framework for developing applications that run on iOS, Android, HarmonyOS, Web (responsive), and various mini-program platforms (WeChat/Alipay/Baidu/Douyin/Feishu/QQ/Kuaishou/DingTalk/Taobao/Jingdong/Xiaohongshu). + +## Core + +| Topic | Description | Reference | +|-------|-------------|-----------| +| Core Framework | Project structure, platform support, condition compilation | [core-framework](references/core-framework.md) | +| View Components | view, scroll-view, swiper, movable-area, cover-view | [core-view-components](references/core-view-components.md) | +| Form Components | input, textarea, picker, checkbox, radio, switch, slider | [core-form-components](references/core-form-components.md) | + +## Features + +### UI Components + +| Topic | Description | Reference | +|-------|-------------|-----------| +| Media Components | image, video, camera, live-player, map | [feature-media-components](references/feature-media-components.md) | +| Navigation | navigator, routing, page navigation | [feature-navigation](references/feature-navigation.md) | +| UI Feedback | toast, modal, loading, action sheet, pull refresh | [feature-ui-feedback](references/feature-ui-feedback.md) | + +### APIs + +| Topic | Description | Reference | +|-------|-------------|-----------| +| Network | HTTP requests, file upload/download, WebSocket | [feature-network](references/feature-network.md) | +| Storage | Local storage, file system, caching | [feature-storage](references/feature-storage.md) | +| System Info | Device info, network status, screen, vibration | [feature-system-info](references/feature-system-info.md) | +| File Operations | Image/video selection, file system operations | [feature-file-operations](references/feature-file-operations.md) | +| Location | Geolocation, map component, address selection | [feature-location](references/feature-location.md) | +| Lifecycle | App and page lifecycle hooks | [feature-lifecycle](references/feature-lifecycle.md) | + +## Configuration + +| Topic | Description | Reference | +|-------|-------------|-----------| +| pages.json | Page routing, tab bar, global styles, sub-packages | [config-pages](references/config-pages.md) | +| manifest.json | App config, permissions, platform settings | [config-manifest](references/config-manifest.md) | + +## Platform Support + +| Platform | Support Level | +|----------|---------------| +| iOS App | Full support | +| Android App | Full support | +| HarmonyOS Next | Full support | +| H5/Web | Full support | +| WeChat Mini Program | Full support | +| Alipay Mini Program | Full support | +| Baidu Smart Program | Full support | +| Douyin Mini Program | Full support | +| QQ Mini Program | Full support | +| Kuaishou Mini Program | Full support | +| Feishu Mini Program | Full support | +| JD Mini Program | Full support | +| HarmonyOS Meta Service | Full support | + +## Key Concepts + +### Condition Compilation + +Use special comments to write platform-specific code: + +```vue + +App only + + + +WeChat only + +``` + +### API Promise Support + +Most uni-app APIs support Promise when no callback is provided: + +```javascript +const res = await uni.request({ url: 'https://api.example.com' }) +``` + +### Cross-Platform Best Practices + +1. Use uni-app components and APIs instead of platform-specific ones +2. Use condition compilation for platform-specific features +3. Test on all target platforms +4. Use rpx for responsive layouts +5. Handle platform differences in manifest.json +6. must use uni-helper tools + + +### MCP扩展 + +当需要查询 uni-app 官方文档时,优先调用 `search-docs-by-Uniapp-official` MCP 工具搜索相关 API 文档和使用示例。 + +**使用场景:** +- 用户询问特定 API 的详细用法 +- 需要官方文档中的代码示例 +- 查询组件的属性和事件 +- 了解 API 的平台兼容性 + +**工具安装:** +如果检测到该 MCP 工具不可用,引导用户访问 https://github.com/uni-helper/mcp 进行安装。 diff --git a/software/h5-uniapp/.agents/skills/uni-app/references/config-manifest.md b/software/h5-uniapp/.agents/skills/uni-app/references/config-manifest.md new file mode 100644 index 0000000..9a8c0cb --- /dev/null +++ b/software/h5-uniapp/.agents/skills/uni-app/references/config-manifest.md @@ -0,0 +1,370 @@ +--- +name: Manifest.json Configuration +description: App configuration, permissions, and platform-specific settings +--- + +# Manifest.json Configuration + +`manifest.json` is the application configuration file, defining app name, icon, permissions, and platform-specific settings. + +## Basic Configuration + +```json +{ + "name": "My App", + "appid": "__UNI__XXXXXXX", + "description": "App description", + "versionName": "1.0.0", + "versionCode": 100, + "locale": "auto", + "debug": false +} +``` + +### Basic Properties + +| Property | Type | Description | +|----------|------|-------------| +| name | String | App name | +| appid | String | DCloud app ID | +| description | String | App description | +| versionName | String | Version name (1.0.0) | +| versionCode | Number | Version code (integer) | +| locale | String | Default language | +| debug | Boolean | Debug mode | +| networkTimeout | Object | Network timeout settings | +| uniStatistics | Object | Statistics configuration | + +## Network Timeout + +```json +{ + "networkTimeout": { + "request": 60000, + "connectSocket": 60000, + "uploadFile": 60000, + "downloadFile": 60000 + } +} +``` + +## App Configuration (app-plus) + +```json +{ + "app-plus": { + "splashscreen": { + "alwaysShowBeforeRender": true, + "autoclose": true, + "waiting": true + }, + "screenOrientation": ["portrait-primary"], + "modules": { + "OAuth": {}, + "Payment": {}, + "Push": {} + }, + "distribute": { + "android": { + "permissions": [ + "" + ], + "minSdkVersion": 21, + "targetSdkVersion": 30 + }, + "ios": { + "capabilities": { + "entitlements": { + "com.apple.developer.push": true + } + } + }, + "sdkConfigs": { + "payment": { + "alipay": {}, + "weixin": {} + } + } + }, + "optimization": { + "subPackages": true + } + } +} +``` + +### Splash Screen + +| Property | Type | Description | +|----------|------|-------------| +| alwaysShowBeforeRender | Boolean | Show until first page renders | +| autoclose | Boolean | Auto close splash screen | +| waiting | Boolean | Show loading indicator | + +## Mini-Program Configuration + +### WeChat (mp-weixin) + +```json +{ + "mp-weixin": { + "appid": "wx1234567890", + "setting": { + "urlCheck": false, + "es6": true, + "postcss": true, + "minified": true + }, + "usingComponents": true, + "permission": { + "scope.userLocation": { + "desc": "Your location is needed" + } + }, + "requiredPrivateInfos": [ + "getLocation" + ] + } +} +``` + +### Alipay (mp-alipay) + +```json +{ + "mp-alipay": { + "appid": "2021...", + "allowsAlignRight": true, + "component2": true + } +} +``` + +### Baidu (mp-baidu) + +```json +{ + "mp-baidu": { + "appid": "12345678", + "navigationBarForceEnable": true + } +} +``` + +### Douyin (mp-toutiao) + +```json +{ + "mp-toutiao": { + "appid": "tt...", + "setting": { + "es6": true, + "minified": true + } + } +} +``` + +## H5 Configuration + +```json +{ + "h5": { + "title": "My App", + "template": "index.html", + "router": { + "mode": "hash", + "base": "./" + }, + "optimization": { + "treeShaking": { + "enable": true + } + }, + "publicPath": "./", + "devServer": { + "port": 8080, + "disableHostCheck": true + }, + "sdkConfigs": { + "maps": { + "qqmap": { + "key": "..." + } + } + } + } +} +``` + +## Vue Configuration + +### Vue 2 + +```json +{ + "vueVersion": "2", + "sassImplementationName": "dart-sass" +} +``` + +### Vue 3 + +```json +{ + "vueVersion": "3" +} +``` + +## Permission Configuration + +### Android Permissions + +```json +{ + "app-plus": { + "distribute": { + "android": { + "permissions": [ + "", + "", + "", + "", + "" + ] + } + } + } +} +``` + +### iOS Privacy Descriptions + +```json +{ + "app-plus": { + "distribute": { + "ios": { + "privacyDescription": { + "NSCameraUsageDescription": "Camera access is needed for scanning QR codes", + "NSPhotoLibraryUsageDescription": "Photo access is needed for uploading images", + "NSLocationWhenInUseUsageDescription": "Location is needed for finding nearby stores" + } + } + } + } +} +``` + +## Common Module Configuration + +### OAuth (Login) + +```json +{ + "app-plus": { + "modules": { + "OAuth": {} + }, + "distribute": { + "sdkConfigs": { + "oauth": { + "weixin": { + "appid": "wx...", + "appsecret": "...", + "UniversalLinks": "https://..." + } + } + } + } + } +} +``` + +### Payment + +```json +{ + "app-plus": { + "modules": { + "Payment": {} + }, + "distribute": { + "sdkConfigs": { + "payment": { + "alipay": {}, + "weixin": { + "appid": "wx..." + } + } + } + } + } +} +``` + +### Push Notifications + +```json +{ + "app-plus": { + "modules": { + "Push": {} + }, + "distribute": { + "sdkConfigs": { + "push": { + "unipush": {} + } + } + } + } +} +``` + +### Share + +```json +{ + "app-plus": { + "modules": { + "Share": {} + }, + "distribute": { + "sdkConfigs": { + "share": { + "weixin": { + "appid": "wx..." + } + } + } + } + } +} +``` + +## Statistics Configuration + +```json +{ + "uniStatistics": { + "enable": true + }, + "app-plus": { + "uniStatistics": { + "enable": true + } + }, + "mp-weixin": { + "uniStatistics": { + "enable": true + } + } +} +``` + + diff --git a/software/h5-uniapp/.agents/skills/uni-app/references/config-pages.md b/software/h5-uniapp/.agents/skills/uni-app/references/config-pages.md new file mode 100644 index 0000000..a117feb --- /dev/null +++ b/software/h5-uniapp/.agents/skills/uni-app/references/config-pages.md @@ -0,0 +1,270 @@ +--- +name: Pages.json Configuration +description: Page routing, tab bar, and global style configuration +--- + +# Pages.json Configuration + +`pages.json` is the global configuration file for uni-app, defining page routes, window styles, native navigation bar, and tab bar. + +## Basic Structure + +```json +{ + "pages": [ + { + "path": "pages/index/index", + "style": { + "navigationBarTitleText": "Home" + } + } + ], + "globalStyle": { + "navigationBarTextStyle": "black", + "navigationBarBackgroundColor": "#F8F8F8" + }, + "tabBar": { + "list": [ + { "pagePath": "pages/index/index", "text": "Home" } + ] + } +} +``` + +## Root Properties + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| pages | Array | Yes | Page routes | +| globalStyle | Object | No | Default window style | +| tabBar | Object | No | Tab bar configuration | +| condition | Object | No | Launch mode (dev only) | +| subPackages | Array | No | Sub-packages | +| preloadRule | Object | No | Preload rules (MP) | +| easycom | Object | No | Auto component import | +| leftWindow/topWindow/rightWindow | Object | No | Multi-window (H5) | + +## Pages Configuration + +```json +{ + "pages": [ + { + "path": "pages/index/index", + "style": { + "navigationBarTitleText": "Home", + "enablePullDownRefresh": true + } + }, + { + "path": "pages/detail/detail", + "style": { + "navigationBarTitleText": "Detail", + "navigationStyle": "custom" + } + } + ] +} +``` + +### Page Style Options + +| Property | Type | Description | +|----------|------|-------------| +| navigationBarTitleText | String | Navigation title | +| navigationBarTextStyle | String | Title color: black/white | +| navigationBarBackgroundColor | HexColor | Nav background | +| navigationStyle | String | default/custom | +| enablePullDownRefresh | Boolean | Enable pull refresh | +| backgroundColor | HexColor | Background color | +| backgroundTextStyle | String | dark/light | +| onReachBottomDistance | Number | Bottom distance (px) | +| disableScroll | Boolean | Disable scroll (MP) | +| usingComponents | Object | Using custom components | + +## Global Style + +Applied to all pages unless overridden. + +```json +{ + "globalStyle": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "My App", + "navigationBarBackgroundColor": "#F8F8F8", + "backgroundColor": "#F8F8F8", + "backgroundTextStyle": "dark", + "enablePullDownRefresh": false, + "onReachBottomDistance": 50, + "rpxCalcMaxDeviceWidth": 960, + "rpxCalcBaseDeviceWidth": 375, + "rpxCalcIncludeWidth": 750 + } +} +``` + +## Tab Bar Configuration + +```json +{ + "tabBar": { + "color": "#7A7E83", + "selectedColor": "#3cc51f", + "backgroundColor": "#ffffff", + "borderStyle": "black", + "list": [ + { + "pagePath": "pages/index/index", + "text": "Home", + "iconPath": "static/image/home.png", + "selectedIconPath": "static/image/home-active.png" + }, + { + "pagePath": "pages/category/category", + "text": "Category", + "iconPath": "static/image/cat.png", + "selectedIconPath": "static/image/cat-active.png" + }, + { + "pagePath": "pages/cart/cart", + "text": "Cart", + "iconPath": "static/image/cart.png", + "selectedIconPath": "static/image/cart-active.png" + }, + { + "pagePath": "pages/user/user", + "text": "User", + "iconPath": "static/image/user.png", + "selectedIconPath": "static/image/user-active.png" + } + ] + } +} +``` + +### Tab Bar Properties + +| Property | Type | Description | +|----------|------|-------------| +| color | HexColor | Unselected text color | +| selectedColor | HexColor | Selected text color | +| backgroundColor | HexColor | Background color | +| borderStyle | String | black/white | +| list | Array | Tab items (2-5 items) | +| position | String | bottom/top | + +### Tab Item Properties + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| pagePath | String | Yes | Page path | +| text | String | Yes | Tab text | +| iconPath | String | No | Icon path (81x81px) | +| selectedIconPath | String | No | Selected icon | + +### Custom Tab Bar (WeChat/QQ/Douyin) + +```json +{ + "tabBar": { + "custom": true, + "list": [ + { "pagePath": "pages/index/index", "text": "Home" } + ] + } +} +``` + +Create `custom-tab-bar/index` component in root. + +## Sub-packages + +Split app into smaller chunks for faster loading. + +```json +{ + "subPackages": [ + { + "root": "packageA", + "pages": [ + { "path": "pages/cat/cat" }, + { "path": "pages/dog/dog" } + ] + }, + { + "root": "packageB", + "pages": [ + { "path": "pages/apple/apple" }, + { "path": "pages/banana/banana" } + ] + } + ], + "preloadRule": { + "pages/index/index": { + "network": "all", + "packages": ["packageA"] + } + } +} +``` + +## EasyCom (Auto Component Import) + +```json +{ + "easycom": { + "autoscan": true, + "custom": { + "^u--(.*)": "uview-plus/components/u-$1/u-$1.vue", + "^up-(.*)": "uview-plus/components/u-$1/u-$1.vue", + "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue" + } + } +} +``` + +With this config, components are auto-imported without registration. + +## Conditional Compilation in Config + +```json +{ + "pages": [ + { + "path": "pages/index/index", + "style": { + "navigationBarTitleText": "Home" + } + } + ], + "globalStyle": { + "navigationBarTextStyle": "black" + }, + "condition": { + "current": 0, + "list": [ + { + "name": "test", + "path": "pages/test/test", + "query": "id=1" + } + ] + }, + "mp-weixin": { + "appid": "wx...", + "setting": { + "urlCheck": false + } + }, + "app-plus": { + "splashscreen": { + "alwaysShowBeforeRender": true + } + } +} +``` + + diff --git a/software/h5-uniapp/.agents/skills/uni-app/references/core-form-components.md b/software/h5-uniapp/.agents/skills/uni-app/references/core-form-components.md new file mode 100644 index 0000000..6c51dca --- /dev/null +++ b/software/h5-uniapp/.agents/skills/uni-app/references/core-form-components.md @@ -0,0 +1,250 @@ +--- +name: Form Components +description: Input, selection, and form control components +--- + +# Form Components + +## input + +Single-line text input. + +```vue + +``` + +**Properties:** + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| value | String | | Input value | +| type | String | text | Input type: text/number/idcard/digit/tel | +| password | Boolean | false | Password input | +| placeholder | String | | Placeholder text | +| placeholder-style | String | | Placeholder inline styles | +| placeholder-class | String | | Placeholder CSS class | +| maxlength | Number | 140 | Max character length | +| cursor-spacing | Number | 0 | Cursor distance from keyboard bottom (px) | +| focus | Boolean | false | Auto focus | +| confirm-type | String | done | Return key type: send/search/next/go/done | +| confirm-hold | Boolean | false | Keep keyboard open after confirm | + +**Events:** +- `@input` - Input value change +- `@focus` - Input focused +- `@blur` - Input blurred +- `@confirm` - Confirm button clicked +- `@keyboardheightchange` - Keyboard height changed + +## textarea + +Multi-line text input. + +```vue + +``` + +**Correct:** +```html + + + +``` + +```html + + +``` + +## Reference +- [Vue.js Form Input Bindings - Multiline text](https://vuejs.org/guide/essentials/forms.html#multiline-text) diff --git a/software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-animate-transform-opacity.md b/software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-animate-transform-opacity.md new file mode 100644 index 0000000..9d05d4e --- /dev/null +++ b/software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-animate-transform-opacity.md @@ -0,0 +1,186 @@ +--- +title: Prefer Animating Transform and Opacity for Performance +impact: MEDIUM +impactDescription: Animating layout-triggering properties like height or margin causes expensive reflows and janky animations +type: best-practice +tags: [vue3, transition, animation, performance, css, transform, opacity] +--- + +# Prefer Animating Transform and Opacity for Performance + +**Impact: MEDIUM** - When creating Vue transitions, you should prefer animating `transform` and `opacity` properties. These are GPU-accelerated and do not trigger expensive CSS layout recalculations. Animating properties like `height`, `width`, `margin`, `padding`, or `top`/`left` causes the browser to recalculate layout on every frame, resulting in janky, low-performance animations. + +## Task Checklist + +- [ ] Use `transform` for position and size animations (translate, scale, rotate) +- [ ] Use `opacity` for fade effects +- [ ] Avoid animating `height`, `width`, `margin`, `padding`, `top`, `left`, `right`, `bottom` +- [ ] If you must animate height, consider using `max-height` with a generous value or JavaScript-based solutions +- [ ] Use `will-change` sparingly and only when needed + +**Problematic Code:** +```css +/* BAD: Animating height triggers layout recalculation every frame */ +.slide-enter-active, +.slide-leave-active { + transition: height 0.3s ease; +} + +.slide-enter-from, +.slide-leave-to { + height: 0; +} + +.slide-enter-to, +.slide-leave-from { + height: 200px; +} +``` + +```css +/* BAD: Animating margin causes layout thrashing */ +.slide-enter-active, +.slide-leave-active { + transition: margin-top 0.3s ease; +} + +.slide-enter-from, +.slide-leave-to { + margin-top: -100%; +} +``` + +**Correct Code:** +```css +/* GOOD: Using transform and opacity - GPU accelerated */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.3s ease; +} + +.fade-enter-from, +.fade-leave-to { + opacity: 0; +} +``` + +```css +/* GOOD: Using transform for slide animations */ +.slide-enter-active, +.slide-leave-active { + transition: transform 0.3s ease, opacity 0.3s ease; +} + +.slide-enter-from { + transform: translateX(-100%); + opacity: 0; +} + +.slide-leave-to { + transform: translateX(100%); + opacity: 0; +} +``` + +```css +/* GOOD: Using scale instead of width/height */ +.scale-enter-active, +.scale-leave-active { + transition: transform 0.3s ease, opacity 0.3s ease; +} + +.scale-enter-from, +.scale-leave-to { + transform: scale(0); + opacity: 0; +} +``` + +## When You Must Animate Height + +If you absolutely need to animate height (e.g., accordion), consider these alternatives: + +```vue + + + +``` + +```vue + + + +``` + +## Performance Comparison + +| Property | Layout | Paint | Composite | Performance | +|----------|--------|-------|-----------|-------------| +| `transform` | No | No | Yes | Excellent | +| `opacity` | No | No | Yes | Excellent | +| `background-color` | No | Yes | Yes | Good | +| `width`/`height` | Yes | Yes | Yes | Poor | +| `margin`/`padding` | Yes | Yes | Yes | Poor | +| `top`/`left` | Yes | Yes | Yes | Poor | + +## Reference +- [Vue.js Transition Documentation](https://vuejs.org/guide/built-ins/transition.html) +- [CSS Triggers](https://csstriggers.com/) - Reference for which properties trigger layout/paint +- [High Performance Animations](https://web.dev/animations-guide/) diff --git a/software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-group-flip-inline-elements.md b/software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-group-flip-inline-elements.md new file mode 100644 index 0000000..f2c0e8d --- /dev/null +++ b/software/h5-uniapp/.agents/skills/vue-best-practices/reference/transition-group-flip-inline-elements.md @@ -0,0 +1,152 @@ +--- +title: TransitionGroup FLIP Animations Do Not Work With Inline Elements +impact: MEDIUM +impactDescription: Move animations silently fail on inline elements, causing items to jump +type: gotcha +tags: [vue3, transition-group, animation, flip, css, display, inline-block] +--- + +# TransitionGroup FLIP Animations Do Not Work With Inline Elements + +**Impact: MEDIUM** - The FLIP (First, Last, Invert, Play) animation technique that Vue uses for `` move transitions does not work with elements that have `display: inline`. The move animation will silently fail, and items will jump to their new positions instead of smoothly transitioning. + +This is a CSS limitation, not a Vue bug. CSS transforms (which FLIP uses internally) do not apply to inline elements per the CSS specification. + +## Task Checklist + +- [ ] Ensure list items are not `display: inline` elements +- [ ] Use `display: inline-block` or `display: block` for list items +- [ ] Use flexbox or grid containers which make children block-level +- [ ] Check if inherited styles are setting `display: inline` + +**Incorrect - Inline elements break move animations:** +```vue + + + +``` + +**Correct - Use inline-block:** +```vue + + + +``` + +**Correct - Use flexbox container:** +```vue + + + +``` + +**Correct - Use block elements:** +```vue + + + +``` + +## Why Inline Elements Don't Work + +Per CSS specifications, the `transform` property does not apply to inline boxes. Since FLIP animations use CSS transforms to animate element positions: + +```css +/* Vue internally applies something like this during move */ +.item { + transform: translateX(-50px) translateY(-20px); + /* Then transitions to transform: none */ +} +``` + +This transform is ignored on inline elements, so no animation occurs. + +## Elements That Are Inline by Default + +Be aware of these common inline elements that need `display: inline-block`: + +- `` +- `` +- ``, ``, ``, `` +- ``, `` +- `