diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index b214862..0000000 --- a/package-lock.json +++ /dev/null @@ -1,420 +0,0 @@ -{ - "name": "yunzer_go", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "docx-preview": "^0.3.7", - "vue3-pdf-app": "^1.0.3" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/types": "^7.28.5" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "license": "MIT", - "peer": true - }, - "node_modules/@vue/compiler-core": { - "version": "3.5.22", - "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.22.tgz", - "integrity": "sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/parser": "^7.28.4", - "@vue/shared": "3.5.22", - "entities": "^4.5.0", - "estree-walker": "^2.0.2", - "source-map-js": "^1.2.1" - } - }, - "node_modules/@vue/compiler-dom": { - "version": "3.5.22", - "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.22.tgz", - "integrity": "sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@vue/compiler-core": "3.5.22", - "@vue/shared": "3.5.22" - } - }, - "node_modules/@vue/compiler-sfc": { - "version": "3.5.22", - "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.22.tgz", - "integrity": "sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/parser": "^7.28.4", - "@vue/compiler-core": "3.5.22", - "@vue/compiler-dom": "3.5.22", - "@vue/compiler-ssr": "3.5.22", - "@vue/shared": "3.5.22", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.19", - "postcss": "^8.5.6", - "source-map-js": "^1.2.1" - } - }, - "node_modules/@vue/compiler-ssr": { - "version": "3.5.22", - "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.22.tgz", - "integrity": "sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==", - "license": "MIT", - "peer": true, - "dependencies": { - "@vue/compiler-dom": "3.5.22", - "@vue/shared": "3.5.22" - } - }, - "node_modules/@vue/reactivity": { - "version": "3.5.22", - "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.22.tgz", - "integrity": "sha512-f2Wux4v/Z2pqc9+4SmgZC1p73Z53fyD90NFWXiX9AKVnVBEvLFOWCEgJD3GdGnlxPZt01PSlfmLqbLYzY/Fw4A==", - "license": "MIT", - "peer": true, - "dependencies": { - "@vue/shared": "3.5.22" - } - }, - "node_modules/@vue/runtime-core": { - "version": "3.5.22", - "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.22.tgz", - "integrity": "sha512-EHo4W/eiYeAzRTN5PCextDUZ0dMs9I8mQ2Fy+OkzvRPUYQEyK9yAjbasrMCXbLNhF7P0OUyivLjIy0yc6VrLJQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@vue/reactivity": "3.5.22", - "@vue/shared": "3.5.22" - } - }, - "node_modules/@vue/runtime-dom": { - "version": "3.5.22", - "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.22.tgz", - "integrity": "sha512-Av60jsryAkI023PlN7LsqrfPvwfxOd2yAwtReCjeuugTJTkgrksYJJstg1e12qle0NarkfhfFu1ox2D+cQotww==", - "license": "MIT", - "peer": true, - "dependencies": { - "@vue/reactivity": "3.5.22", - "@vue/runtime-core": "3.5.22", - "@vue/shared": "3.5.22", - "csstype": "^3.1.3" - } - }, - "node_modules/@vue/server-renderer": { - "version": "3.5.22", - "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.22.tgz", - "integrity": "sha512-gXjo+ao0oHYTSswF+a3KRHZ1WszxIqO7u6XwNHqcqb9JfyIL/pbWrrh/xLv7jeDqla9u+LK7yfZKHih1e1RKAQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@vue/compiler-ssr": "3.5.22", - "@vue/shared": "3.5.22" - }, - "peerDependencies": { - "vue": "3.5.22" - } - }, - "node_modules/@vue/shared": { - "version": "3.5.22", - "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.22.tgz", - "integrity": "sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==", - "license": "MIT", - "peer": true - }, - "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/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT", - "peer": true - }, - "node_modules/docx-preview": { - "version": "0.3.7", - "resolved": "https://registry.npmmirror.com/docx-preview/-/docx-preview-0.3.7.tgz", - "integrity": "sha512-Lav69CTA/IYZPJTsKH7oYeoZjyg96N0wEJMNslGJnZJ+dMUZK85Lt5ASC79yUlD48ecWjuv+rkcmFt6EVPV0Xg==", - "license": "Apache-2.0", - "dependencies": { - "jszip": ">=3.0.0" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "peer": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT", - "peer": true - }, - "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/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/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "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/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC", - "peer": true - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "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/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/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/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/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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/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/vue": { - "version": "3.5.22", - "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.22.tgz", - "integrity": "sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@vue/compiler-dom": "3.5.22", - "@vue/compiler-sfc": "3.5.22", - "@vue/runtime-dom": "3.5.22", - "@vue/server-renderer": "3.5.22", - "@vue/shared": "3.5.22" - }, - "peerDependencies": { - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/vue3-pdf-app": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/vue3-pdf-app/-/vue3-pdf-app-1.0.3.tgz", - "integrity": "sha512-qegWTIF4wYKiocZ3KreB70wRXhqSdXWbdERDyyKzT7d5PbjKbS9tD6vaKkCqh3PzTM84NyKPYrQ3iuwJb60YPQ==", - "license": "MIT", - "peerDependencies": { - "vue": "^3.0.0" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 4bfae15..0000000 --- a/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "dependencies": { - "docx-preview": "^0.3.7", - "vue3-pdf-app": "^1.0.3" - } -} diff --git a/pc/docs/dictionary-usage.md b/pc/docs/dictionary-usage.md new file mode 100644 index 0000000..f028f78 --- /dev/null +++ b/pc/docs/dictionary-usage.md @@ -0,0 +1,548 @@ +# 字典系统使用指南 + +## 概述 + +字典(Dictionary)系统是一个用于管理应用中常用的枚举值和标签化数据的功能。通过字典系统,你可以在后台统一管理这些数据,而无需修改代码和重新编译部署。 + +### 字典的核心概念 + +- **字典类型(Dict Type)**:用来分类管理字典项,例如 `user_status`(用户状态)、`gender`(性别)等 +- **字典编码(Dict Code)**:字典类型的唯一标识,用于前端查询,例如 `user_status`、`gender` +- **字典项(Dict Item)**:具体的字典值和标签,包括: + - `dict_value`:存储在数据库中的实际值(例如 `1`、`0`) + - `dict_label`:显示给用户的标签文本(例如 "启用"、"禁用") + +### 数据库表结构 + +```sql +-- 字典类型表 +CREATE TABLE sys_dict_type ( + id BIGINT, + dict_code VARCHAR(50) -- 字典编码(唯一) + dict_name VARCHAR(100) -- 字典名称 + status TINYINT -- 是否启用 + ... +) + +-- 字典项表 +CREATE TABLE sys_dict_item ( + id BIGINT, + dict_type_id BIGINT -- 关联的字典类型ID + dict_label VARCHAR(100) -- 显示标签(如 "启用") + dict_value VARCHAR(100) -- 存储值(如 "1") + status TINYINT -- 是否启用 + ... +) +``` + +--- + +## 后端 API 接口 + +### 1. 获取字典项(最常用) + +**根据字典编码获取字典项列表:** + +```http +GET /api/dict/items/code/:code?include_disabled=0 +``` + +**请求示例:** +```bash +curl -X GET "http://localhost:8080/api/dict/items/code/user_status?include_disabled=0" +``` + +**响应示例:** +```json +{ + "code": 0, + "message": "success", + "data": [ + { + "id": 1, + "dict_type_id": 101, + "dict_label": "启用", + "dict_value": "1", + "status": 1, + "sort": 1, + ... + }, + { + "id": 2, + "dict_type_id": 101, + "dict_label": "禁用", + "dict_value": "0", + "status": 1, + "sort": 2, + ... + } + ] +} +``` + +**参数说明:** +- `code` (string, 必需):字典编码,例如 `user_status` +- `include_disabled` (int, 可选):是否包含禁用项,0 = 不包含(默认),1 = 包含 + +--- + +### 2. 字典管理接口 + +#### 获取字典类型列表 +```http +GET /api/dict/types?parentId=&status= +``` + +#### 添加字典类型 +```http +POST /api/dict/types +{ + "dict_code": "user_status", + "dict_name": "用户状态", + "status": 1 +} +``` + +#### 添加字典项 +```http +POST /api/dict/items +{ + "dict_type_id": 101, + "dict_label": "启用", + "dict_value": "1", + "status": 1, + "sort": 1 +} +``` + +#### 更新/删除字典项 +```http +PUT /api/dict/items/:id +DELETE /api/dict/items/:id +``` + +--- + +## 前端 API(Vue/JavaScript) + +### 前端 API 文件位置 + +``` +pc/src/api/dict.js +``` + +### 可用函数 + +#### getDictItemsByCode(code, includeDisabled = false) + +**最常用的函数**,根据字典编码获取字典项列表。 + +**参数:** +- `code` (string):字典编码,例如 `'user_status'` +- `includeDisabled` (boolean):是否包含禁用项,默认 `false` + +**返回:** Promise,返回字典项数组 + +**示例:** +```javascript +import { getDictItemsByCode } from '@/api/dict' + +// 获取用户状态字典 +const statusItems = await getDictItemsByCode('user_status') +console.log(statusItems) +// 输出: +// [ +// { dict_label: '启用', dict_value: '1', ... }, +// { dict_label: '禁用', dict_value: '0', ... } +// ] +``` + +--- + +#### 其他可用函数 + +```javascript +// 获取字典类型列表 +getDictTypes(params) + +// 根据ID获取字典类型 +getDictTypeById(id) + +// 添加字典类型 +addDictType(data) + +// 更新字典类型 +updateDictType(id, data) + +// 删除字典类型 +deleteDictType(id) + +// 获取字典项列表(需传入参数过滤) +getDictItems(params) + +// 根据ID获取字典项 +getDictItemById(id) + +// 添加字典项 +addDictItem(data) + +// 更新字典项 +updateDictItem(id, data) + +// 删除字典项 +deleteDictItem(id) + +// 批量更新字典项排序 +batchUpdateDictItemSort(data) +``` + +--- + +## 前端组件中的使用示例 + +### 示例 1:在用户管理页面显示用户状态 + +**场景**:在用户列表中,根据用户的 `status` 字段显示对应的状态标签。 + +**文件**:`pc/src/views/system/users/index.vue` + +**实现步骤**: + +#### 1. 导入字典 API +```javascript +import { getDictItemsByCode } from '@/api/dict' +``` + +#### 2. 定义状态字典数据和加载函数 +```javascript + +``` + +#### 3. 定义辅助函数 +```javascript +// 根据状态值获取字典标签 +const getStatusLabel = (status: any) => { + const sval = status !== undefined && status !== null ? String(status) : '' + + // 查找匹配的字典项 + const item = statusDict.value.find( + (d: any) => String(d.dict_value) === sval || d.dict_value === status + ) + + if (item && item.dict_label) { + return item.dict_label + } + + // 兼容旧逻辑:如果没有匹配的字典项 + if (status === 1 || sval === '1' || sval === 'active') return '启用' + return '禁用' +} + +// 根据标签确定 el-tag 的样式类型 +const getStatusTagType = (status: any) => { + const label = getStatusLabel(status) + if (!label) return 'info' + + const l = label.toString() + if (l.includes('启用') || l.includes('正常') || l.includes('active')) return 'success' + if (l.includes('禁用') || l.includes('停用') || l.includes('inactive')) return 'danger' + + return 'info' +} +``` + +#### 4. 在模板中使用 +```vue + +``` + +--- + +### 示例 2:在下拉选择中使用字典 + +**场景**:在"添加/编辑用户"对话框中,用字典项填充状态下拉选择。 + +```vue + + + +``` + +--- + +### 示例 3:多个字典项的场景 + +**场景**:同时加载多个字典(用户状态、性别、部门类型等)。 + +```javascript + +``` + +--- + +## 最佳实践 + +### 1. 缓存字典数据 + +避免在每个组件中都调用字典 API。建议在全局 store 中缓存字典数据: + +**文件**:`pc/src/stores/dict.ts` + +```typescript +import { defineStore } from 'pinia' +import { ref } from 'vue' +import { getDictItemsByCode } from '@/api/dict' + +export const useDictStore = defineStore('dict', () => { + const dicts = ref>({}) + + const fetchDict = async (code: string) => { + if (dicts.value[code]) { + return dicts.value[code] + } + + try { + const res = await getDictItemsByCode(code) + const items = res?.data || res || [] + dicts.value[code] = Array.isArray(items) ? items : [] + return dicts.value[code] + } catch (err) { + console.error(`Failed to fetch dict ${code}:`, err) + return [] + } + } + + return { dicts, fetchDict } +}) +``` + +**在组件中使用:** + +```javascript +import { useDictStore } from '@/stores/dict' + +const dictStore = useDictStore() +const statusDict = await dictStore.fetchDict('user_status') +``` + +### 2. 创建字典枚举文件 + +建议为常用字典创建枚举文件,便于维护: + +**文件**:`pc/src/constants/dicts.ts` + +```typescript +// 字典编码常量 +export const DICT_CODES = { + USER_STATUS: 'user_status', + GENDER: 'gender', + DEPT_TYPE: 'dept_type', + POSITION_LEVEL: 'position_level', +} as const + +// 用于 TypeScript 类型 +export type DictCode = typeof DICT_CODES[keyof typeof DICT_CODES] +``` + +**在组件中使用:** + +```javascript +import { DICT_CODES } from '@/constants/dicts' +import { useDictStore } from '@/stores/dict' + +const dictStore = useDictStore() +const statusDict = await dictStore.fetchDict(DICT_CODES.USER_STATUS) +``` + +### 3. 处理不同的数据值类型 + +字典值可能是数字、字符串或其他类型。确保比较时进行正确的类型转换: + +```javascript +const getStatusLabel = (status: any) => { + // 转换为字符串便于比较 + const statusStr = String(status) + + const item = statusDict.value.find((d: any) => { + // 支持多种比较方式 + return ( + String(d.dict_value) === statusStr || + d.dict_value === status || + d.dict_value == status // 宽松比较 + ) + }) + + return item?.dict_label || '未知' +} +``` + +### 4. 错误处理 + +始终为字典加载添加适当的错误处理和回退机制: + +```javascript +const fetchStatusDict = async () => { + try { + const res = await getDictItemsByCode('user_status') + statusDict.value = res?.data || [] + } catch (err) { + console.error('Failed to fetch status dict:', err) + // 使用默认的回退数据 + statusDict.value = [ + { dict_label: '启用', dict_value: '1' }, + { dict_label: '禁用', dict_value: '0' }, + ] + } +} +``` + +--- + +## 常见问题 + +### Q1: 字典数据在页面刷新后丢失了? + +**A:** 这是正常的,字典数据只在组件的生命周期内存在。建议使用全局 store(Pinia)来缓存字典数据,或在每个需要的组件中通过 `onMounted` 加载。 + +### Q2: 后端添加了新的字典项,前端不显示新数据? + +**A:** 前端缓存了字典数据。有两种解决方案: +1. 刷新浏览器页面,重新加载字典 +2. 在后端修改字典后,调用缓存清除接口(如果有的话),或手动清除前端 store 中的字典缓存 + +### Q3: 字典编码对应的字典类型不存在? + +**A:** 确保: +1. 后端已在 `sys_dict_type` 表中添加了对应的字典类型记录 +2. 字典类型的状态(`status`)为启用(通常为 1) +3. 至少添加了一项字典项(`sys_dict_item` 表中有数据) +4. 字典编码(`dict_code`)完全匹配(区分大小写) + +### Q4: 如何在后台管理系统中管理字典? + +**A:** 通常在系统设置或配置模块中有"字典管理"功能,可以: +- 添加/编辑/删除字典类型 +- 管理字典项(标签、值、排序、启用/禁用等) + +具体路径取决于你的后台管理系统设计。 + +--- + +## 总结 + +使用字典系统的核心步骤: + +1. **后端准备**:在 `sys_dict_type` 和 `sys_dict_item` 表中添加字典数据 +2. **前端导入**:`import { getDictItemsByCode } from '@/api/dict'` +3. **加载字典**:在 `onMounted` 或其他合适位置调用 API 加载 +4. **使用字典**:通过 `dict_value` 和 `dict_label` 来显示和存储数据 +5. **最优化**:使用 Pinia store 缓存字典,避免重复请求 + +通过字典系统,你可以灵活地管理应用中的枚举值和标签数据,而无需修改代码。 diff --git a/pc/src/views/system/dict/components/DictItemEdit.vue b/pc/src/views/system/dict/components/DictItemEdit.vue new file mode 100644 index 0000000..8dc7994 --- /dev/null +++ b/pc/src/views/system/dict/components/DictItemEdit.vue @@ -0,0 +1,335 @@ + + + + + + + \ No newline at end of file diff --git a/pc/src/views/system/dict/components/DictItemEditDialog.vue b/pc/src/views/system/dict/components/DictItemEditDialog.vue new file mode 100644 index 0000000..47b3691 --- /dev/null +++ b/pc/src/views/system/dict/components/DictItemEditDialog.vue @@ -0,0 +1,316 @@ + + + + + + + \ No newline at end of file diff --git a/pc/src/views/system/dict/components/DictItemList.vue b/pc/src/views/system/dict/components/DictItemList.vue new file mode 100644 index 0000000..730c27e --- /dev/null +++ b/pc/src/views/system/dict/components/DictItemList.vue @@ -0,0 +1,422 @@ + + + + + + + \ No newline at end of file diff --git a/pc/src/views/system/dict/components/DictTypeEdit.vue b/pc/src/views/system/dict/components/DictTypeEdit.vue new file mode 100644 index 0000000..9305a49 --- /dev/null +++ b/pc/src/views/system/dict/components/DictTypeEdit.vue @@ -0,0 +1,222 @@ + + + + + + + \ No newline at end of file diff --git a/pc/src/views/system/dict/components/DictTypeEditDialog.vue b/pc/src/views/system/dict/components/DictTypeEditDialog.vue new file mode 100644 index 0000000..e69de29 diff --git a/pc/src/views/system/dict/components/DictTypeList.vue b/pc/src/views/system/dict/components/DictTypeList.vue new file mode 100644 index 0000000..8886c11 --- /dev/null +++ b/pc/src/views/system/dict/components/DictTypeList.vue @@ -0,0 +1,316 @@ + + + + + + + \ No newline at end of file diff --git a/pc/src/views/system/dict/index.vue b/pc/src/views/system/dict/index.vue index 2ab407a..0c336fa 100644 --- a/pc/src/views/system/dict/index.vue +++ b/pc/src/views/system/dict/index.vue @@ -1,12 +1,8 @@ - - diff --git a/pc/src/views/system/tenant/components/detail.vue b/pc/src/views/system/tenant/components/detail.vue index e346204..0a9a115 100644 --- a/pc/src/views/system/tenant/components/detail.vue +++ b/pc/src/views/system/tenant/components/detail.vue @@ -32,8 +32,8 @@ - - {{ tenantData.status === 'enabled' ? '启用' : '禁用' }} + + {{ tenantData.status === 1 ? '启用' : '禁用' }} diff --git a/pc/src/views/system/tenant/components/edit.vue b/pc/src/views/system/tenant/components/edit.vue index 8dc82a9..9078dd0 100644 --- a/pc/src/views/system/tenant/components/edit.vue +++ b/pc/src/views/system/tenant/components/edit.vue @@ -29,8 +29,8 @@ - - + + @@ -103,7 +103,7 @@ const tenantForm = reactive({ owner: '', phone: '', email: '', - status: 'enabled', + status: 1, capacity: 0, remark: '', }); @@ -131,7 +131,7 @@ function resetForm() { tenantForm.owner = ''; tenantForm.phone = ''; tenantForm.email = ''; - tenantForm.status = 'enabled'; + tenantForm.status = 1; tenantForm.capacity = 0; tenantForm.remark = ''; tenantFormRef.value?.resetFields(); @@ -146,7 +146,7 @@ function initFormData() { tenantForm.owner = props.tenant.owner || ''; tenantForm.phone = props.tenant.phone || ''; tenantForm.email = props.tenant.email || ''; - tenantForm.status = props.tenant.status || 'enabled'; + tenantForm.status = props.tenant.status != null ? Number(props.tenant.status) : 1; // capacity 后端返回的是 MB,直接使用 tenantForm.capacity = props.tenant.capacity != null ? Number(props.tenant.capacity) : 0; tenantForm.remark = props.tenant.remark || ''; @@ -168,7 +168,7 @@ async function handleSubmit() { owner: tenantForm.owner, phone: tenantForm.phone || '', email: tenantForm.email || '', - status: tenantForm.status, + status: Number(tenantForm.status), // capacity 前后端都使用 MB capacity: tenantForm.capacity || 0, remark: tenantForm.remark || '', diff --git a/pc/src/views/system/tenant/index.vue b/pc/src/views/system/tenant/index.vue index 93e3158..b2fcb61 100644 --- a/pc/src/views/system/tenant/index.vue +++ b/pc/src/views/system/tenant/index.vue @@ -70,8 +70,8 @@ diff --git a/pc/src/views/system/users/components/ChangePassword.vue b/pc/src/views/system/users/components/ChangePassword.vue new file mode 100644 index 0000000..73f0f89 --- /dev/null +++ b/pc/src/views/system/users/components/ChangePassword.vue @@ -0,0 +1,210 @@ + + + + + diff --git a/pc/src/views/system/users/components/UserEdit.vue b/pc/src/views/system/users/components/UserEdit.vue new file mode 100644 index 0000000..3efc5e3 --- /dev/null +++ b/pc/src/views/system/users/components/UserEdit.vue @@ -0,0 +1,402 @@ + + + + + diff --git a/pc/src/views/system/users/components/UserList.vue b/pc/src/views/system/users/components/UserList.vue new file mode 100644 index 0000000..9533975 --- /dev/null +++ b/pc/src/views/system/users/components/UserList.vue @@ -0,0 +1,358 @@ + + + + + diff --git a/pc/src/views/system/users/index.vue b/pc/src/views/system/users/index.vue index a758c91..95f0a4a 100644 --- a/pc/src/views/system/users/index.vue +++ b/pc/src/views/system/users/index.vue @@ -7,15 +7,16 @@ 添加用户 - - - 刷新 - + + + 刷新 + + - + @@ -93,6 +94,8 @@ + +
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {{ role.roleName }} - - ({{ role.roleCode }}) - - - - - - - - - - - - - + + + + + diff --git a/server/controllers/user.go b/server/controllers/user.go index 9bdb5e0..c42775f 100644 --- a/server/controllers/user.go +++ b/server/controllers/user.go @@ -202,14 +202,16 @@ func (c *UserController) GetUserInfo() { "code": 0, "message": "查询成功", "data": map[string]interface{}{ - "id": user.Id, - "username": user.Username, - "email": user.Email, - "avatar": user.Avatar, - "nickname": user.Nickname, - "tenant_id": user.TenantId, - "role": user.Role, - "status": user.Status, + "id": user.Id, + "username": user.Username, + "email": user.Email, + "avatar": user.Avatar, + "nickname": user.Nickname, + "tenant_id": user.TenantId, + "role": user.Role, + "status": user.Status, + "department_id": user.DepartmentId, + "position_id": user.PositionId, }, } c.ServeJSON() diff --git a/server/models/dict.go b/server/models/dict.go index 1292dce..3879e4a 100644 --- a/server/models/dict.go +++ b/server/models/dict.go @@ -2,8 +2,6 @@ package models import ( "time" - - "github.com/beego/beego/v2/client/orm" ) // DictType 字典类型模型 @@ -51,8 +49,3 @@ type DictItem struct { func (d *DictItem) TableName() string { return "sys_dict_item" } - -func init() { - orm.RegisterModel(new(DictType)) - orm.RegisterModel(new(DictItem)) -} diff --git a/server/models/tenant.go b/server/models/tenant.go index 67137e7..e6b89ad 100644 --- a/server/models/tenant.go +++ b/server/models/tenant.go @@ -14,7 +14,7 @@ type Tenant struct { Owner string `orm:"size(50)" json:"owner"` Phone string `orm:"size(20);null" json:"phone"` Email string `orm:"size(100);null" json:"email"` - Status string `orm:"size(20);default(enabled)" json:"status"` + Status int `orm:"default(1)" json:"status"` AuditStatus string `orm:"size(20);default(pending)" json:"audit_status"` AuditComment string `orm:"type(text);null" json:"audit_comment"` AuditBy string `orm:"size(50);null" json:"audit_by"` diff --git a/server/services/user.go b/server/services/user.go index 4f6240c..8355021 100644 --- a/server/services/user.go +++ b/server/services/user.go @@ -79,7 +79,7 @@ func ValidateUser(username, password string, tenantName string) (*models.User, * // 1. 根据租户名称查询租户(只查询未删除的) var tenant struct { Id int - Status string + Status int DeleteTime interface{} // 使用 interface{} 来处理 NULL 值 } err := o.Raw("SELECT id, status, delete_time FROM yz_tenants WHERE name = ? AND delete_time IS NULL", tenantName).QueryRow(&tenant) @@ -91,13 +91,13 @@ func ValidateUser(username, password string, tenantName string) (*models.User, * return nil, nil, fmt.Errorf("查询租户失败: %v", err) } - // 检查租户状态 - if tenant.Status == "disabled" { + // 检查租户状态(0=禁用,1=启用) + if tenant.Status == 0 { return nil, nil, errors.New("租户已被禁用") } - if tenant.Status != "enabled" { - return nil, nil, fmt.Errorf("租户状态异常: %s", tenant.Status) + if tenant.Status != 1 { + return nil, nil, fmt.Errorf("租户状态异常: %d", tenant.Status) } tenantId := tenant.Id @@ -127,7 +127,7 @@ func AddUser(username, password, email, nickname, avatar string, tenantId, role, // 1. 验证租户是否存在且有效 o := orm.NewOrm() var tenantExists bool - err := o.Raw("SELECT EXISTS(SELECT 1 FROM yz_tenants WHERE id = ? AND delete_time IS NULL AND status = 'enabled')", tenantId).QueryRow(&tenantExists) + err := o.Raw("SELECT EXISTS(SELECT 1 FROM yz_tenants WHERE id = ? AND delete_time IS NULL AND status = 1)", tenantId).QueryRow(&tenantExists) if err != nil { return nil, fmt.Errorf("验证租户失败: %v", err) }