feat: 优化表格逻辑
parent
aac4047c9a
commit
6a000652b1
|
|
@ -40,6 +40,7 @@
|
||||||
"unocss": "^0.49.8",
|
"unocss": "^0.49.8",
|
||||||
"unplugin-auto-import": "^0.13.0",
|
"unplugin-auto-import": "^0.13.0",
|
||||||
"unplugin-vue-components": "^0.23.0",
|
"unplugin-vue-components": "^0.23.0",
|
||||||
|
"unplugin-vue-router": "^0.7.0",
|
||||||
"vite": "^4.4.9",
|
"vite": "^4.4.9",
|
||||||
"vite-plugin-pages": "^0.28.0",
|
"vite-plugin-pages": "^0.28.0",
|
||||||
"vite-plugin-style-import": "^2.0.0",
|
"vite-plugin-style-import": "^2.0.0",
|
||||||
|
|
|
||||||
273
pnpm-lock.yaml
273
pnpm-lock.yaml
|
|
@ -85,6 +85,9 @@ devDependencies:
|
||||||
unplugin-vue-components:
|
unplugin-vue-components:
|
||||||
specifier: ^0.23.0
|
specifier: ^0.23.0
|
||||||
version: 0.23.0(vue@3.3.4)
|
version: 0.23.0(vue@3.3.4)
|
||||||
|
unplugin-vue-router:
|
||||||
|
specifier: ^0.7.0
|
||||||
|
version: 0.7.0(vue-router@4.2.4)(vue@3.3.4)
|
||||||
vite:
|
vite:
|
||||||
specifier: ^4.4.9
|
specifier: ^4.4.9
|
||||||
version: 4.4.9(less@4.2.0)
|
version: 4.4.9(less@4.2.0)
|
||||||
|
|
@ -170,10 +173,10 @@ packages:
|
||||||
'@babel/helper-compilation-targets': 7.22.15
|
'@babel/helper-compilation-targets': 7.22.15
|
||||||
'@babel/helper-module-transforms': 7.22.17(@babel/core@7.22.17)
|
'@babel/helper-module-transforms': 7.22.17(@babel/core@7.22.17)
|
||||||
'@babel/helpers': 7.22.15
|
'@babel/helpers': 7.22.15
|
||||||
'@babel/parser': 7.22.16
|
'@babel/parser': 7.23.6
|
||||||
'@babel/template': 7.22.15
|
'@babel/template': 7.22.15
|
||||||
'@babel/traverse': 7.22.17
|
'@babel/traverse': 7.22.17
|
||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.6
|
||||||
convert-source-map: 1.9.0
|
convert-source-map: 1.9.0
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
gensync: 1.0.0-beta.2
|
gensync: 1.0.0-beta.2
|
||||||
|
|
@ -187,7 +190,7 @@ packages:
|
||||||
resolution: {integrity: sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==}
|
resolution: {integrity: sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.6
|
||||||
'@jridgewell/gen-mapping': 0.3.3
|
'@jridgewell/gen-mapping': 0.3.3
|
||||||
'@jridgewell/trace-mapping': 0.3.19
|
'@jridgewell/trace-mapping': 0.3.19
|
||||||
jsesc: 2.5.2
|
jsesc: 2.5.2
|
||||||
|
|
@ -197,7 +200,7 @@ packages:
|
||||||
resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==}
|
resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.6
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@babel/helper-compilation-targets@7.22.15:
|
/@babel/helper-compilation-targets@7.22.15:
|
||||||
|
|
@ -239,28 +242,28 @@ packages:
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/template': 7.22.15
|
'@babel/template': 7.22.15
|
||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.6
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@babel/helper-hoist-variables@7.22.5:
|
/@babel/helper-hoist-variables@7.22.5:
|
||||||
resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
|
resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.6
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@babel/helper-member-expression-to-functions@7.22.15:
|
/@babel/helper-member-expression-to-functions@7.22.15:
|
||||||
resolution: {integrity: sha512-qLNsZbgrNh0fDQBCPocSL8guki1hcPvltGDv/NxvUoABwFq7GkKSu1nRXeJkVZc+wJvne2E0RKQz+2SQrz6eAA==}
|
resolution: {integrity: sha512-qLNsZbgrNh0fDQBCPocSL8guki1hcPvltGDv/NxvUoABwFq7GkKSu1nRXeJkVZc+wJvne2E0RKQz+2SQrz6eAA==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.6
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@babel/helper-module-imports@7.22.15:
|
/@babel/helper-module-imports@7.22.15:
|
||||||
resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
|
resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.6
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@babel/helper-module-transforms@7.22.17(@babel/core@7.22.17):
|
/@babel/helper-module-transforms@7.22.17(@babel/core@7.22.17):
|
||||||
|
|
@ -281,7 +284,7 @@ packages:
|
||||||
resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==}
|
resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.6
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@babel/helper-plugin-utils@7.22.5:
|
/@babel/helper-plugin-utils@7.22.5:
|
||||||
|
|
@ -305,21 +308,21 @@ packages:
|
||||||
resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
|
resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.6
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@babel/helper-skip-transparent-expression-wrappers@7.22.5:
|
/@babel/helper-skip-transparent-expression-wrappers@7.22.5:
|
||||||
resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==}
|
resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.6
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@babel/helper-split-export-declaration@7.22.6:
|
/@babel/helper-split-export-declaration@7.22.6:
|
||||||
resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
|
resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.6
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@babel/helper-string-parser@7.22.5:
|
/@babel/helper-string-parser@7.22.5:
|
||||||
|
|
@ -327,6 +330,11 @@ packages:
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@babel/helper-string-parser@7.23.4:
|
||||||
|
resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@babel/helper-validator-identifier@7.22.20:
|
/@babel/helper-validator-identifier@7.22.20:
|
||||||
resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
|
resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
@ -343,7 +351,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/template': 7.22.15
|
'@babel/template': 7.22.15
|
||||||
'@babel/traverse': 7.22.17
|
'@babel/traverse': 7.22.17
|
||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.6
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
@ -365,6 +373,14 @@ packages:
|
||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@babel/parser@7.23.6:
|
||||||
|
resolution: {integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
'@babel/types': 7.23.6
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.22.17):
|
/@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.22.17):
|
||||||
resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==}
|
resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
@ -408,8 +424,8 @@ packages:
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/code-frame': 7.22.13
|
'@babel/code-frame': 7.22.13
|
||||||
'@babel/parser': 7.22.16
|
'@babel/parser': 7.23.6
|
||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.6
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@babel/traverse@7.22.17:
|
/@babel/traverse@7.22.17:
|
||||||
|
|
@ -422,8 +438,8 @@ packages:
|
||||||
'@babel/helper-function-name': 7.22.5
|
'@babel/helper-function-name': 7.22.5
|
||||||
'@babel/helper-hoist-variables': 7.22.5
|
'@babel/helper-hoist-variables': 7.22.5
|
||||||
'@babel/helper-split-export-declaration': 7.22.6
|
'@babel/helper-split-export-declaration': 7.22.6
|
||||||
'@babel/parser': 7.22.16
|
'@babel/parser': 7.23.6
|
||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.6
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
globals: 11.12.0
|
globals: 11.12.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
|
@ -439,6 +455,15 @@ packages:
|
||||||
to-fast-properties: 2.0.0
|
to-fast-properties: 2.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@babel/types@7.23.6:
|
||||||
|
resolution: {integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==}
|
||||||
|
engines: {node: '>=6.9.0'}
|
||||||
|
dependencies:
|
||||||
|
'@babel/helper-string-parser': 7.23.4
|
||||||
|
'@babel/helper-validator-identifier': 7.22.20
|
||||||
|
to-fast-properties: 2.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@esbuild-kit/cjs-loader@2.4.2:
|
/@esbuild-kit/cjs-loader@2.4.2:
|
||||||
resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==}
|
resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -993,6 +1018,20 @@ packages:
|
||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@rollup/pluginutils@5.1.0:
|
||||||
|
resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
rollup:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@types/estree': 1.0.1
|
||||||
|
estree-walker: 2.0.2
|
||||||
|
picomatch: 2.3.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@sindresorhus/is@5.6.0:
|
/@sindresorhus/is@5.6.0:
|
||||||
resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==}
|
resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==}
|
||||||
engines: {node: '>=14.16'}
|
engines: {node: '>=14.16'}
|
||||||
|
|
@ -1106,7 +1145,7 @@ packages:
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ampproject/remapping': 2.2.1
|
'@ampproject/remapping': 2.2.1
|
||||||
'@rollup/pluginutils': 5.0.5
|
'@rollup/pluginutils': 5.1.0
|
||||||
'@unocss/config': 0.49.8
|
'@unocss/config': 0.49.8
|
||||||
'@unocss/core': 0.49.8
|
'@unocss/core': 0.49.8
|
||||||
'@unocss/preset-uno': 0.49.8
|
'@unocss/preset-uno': 0.49.8
|
||||||
|
|
@ -1236,7 +1275,7 @@ packages:
|
||||||
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0
|
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ampproject/remapping': 2.2.1
|
'@ampproject/remapping': 2.2.1
|
||||||
'@rollup/pluginutils': 5.0.5
|
'@rollup/pluginutils': 5.1.0
|
||||||
'@unocss/config': 0.49.8
|
'@unocss/config': 0.49.8
|
||||||
'@unocss/core': 0.49.8
|
'@unocss/core': 0.49.8
|
||||||
'@unocss/inspector': 0.49.8
|
'@unocss/inspector': 0.49.8
|
||||||
|
|
@ -1295,6 +1334,26 @@ packages:
|
||||||
'@volar/language-core': 1.10.1
|
'@volar/language-core': 1.10.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@vue-macros/common@1.10.0(vue@3.3.4):
|
||||||
|
resolution: {integrity: sha512-4DZsPeQA/nBQDw2RkYAmH7KrFjJVrMdAhJhO1JCl1bbbFXCGeoGjXfkg9wHPppj47s2HpAB3GrqNwqVGbi12NQ==}
|
||||||
|
engines: {node: '>=16.14.0'}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^2.7.0 || ^3.2.25
|
||||||
|
peerDependenciesMeta:
|
||||||
|
vue:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@babel/types': 7.23.6
|
||||||
|
'@rollup/pluginutils': 5.1.0
|
||||||
|
'@vue/compiler-sfc': 3.3.11
|
||||||
|
ast-kit: 0.11.3
|
||||||
|
local-pkg: 0.5.0
|
||||||
|
magic-string-ast: 0.3.0
|
||||||
|
vue: 3.3.4
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- rollup
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@vue/babel-helper-vue-transform-on@1.1.5:
|
/@vue/babel-helper-vue-transform-on@1.1.5:
|
||||||
resolution: {integrity: sha512-SgUymFpMoAyWeYWLAY+MkCK3QEROsiUnfaw5zxOVD/M64KQs8D/4oK6Q5omVA2hnvEOE0SCkH2TZxs/jnnUj7w==}
|
resolution: {integrity: sha512-SgUymFpMoAyWeYWLAY+MkCK3QEROsiUnfaw5zxOVD/M64KQs8D/4oK6Q5omVA2hnvEOE0SCkH2TZxs/jnnUj7w==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
@ -1309,7 +1368,7 @@ packages:
|
||||||
'@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.17)
|
'@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.17)
|
||||||
'@babel/template': 7.22.15
|
'@babel/template': 7.22.15
|
||||||
'@babel/traverse': 7.22.17
|
'@babel/traverse': 7.22.17
|
||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.6
|
||||||
'@vue/babel-helper-vue-transform-on': 1.1.5
|
'@vue/babel-helper-vue-transform-on': 1.1.5
|
||||||
camelcase: 6.3.0
|
camelcase: 6.3.0
|
||||||
html-tags: 3.3.1
|
html-tags: 3.3.1
|
||||||
|
|
@ -1318,15 +1377,31 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@vue/compiler-core@3.3.11:
|
||||||
|
resolution: {integrity: sha512-h97/TGWBilnLuRaj58sxNrsUU66fwdRKLOLQ9N/5iNDfp+DZhYH9Obhe0bXxhedl8fjAgpRANpiZfbgWyruQ0w==}
|
||||||
|
dependencies:
|
||||||
|
'@babel/parser': 7.23.6
|
||||||
|
'@vue/shared': 3.3.11
|
||||||
|
estree-walker: 2.0.2
|
||||||
|
source-map-js: 1.0.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@vue/compiler-core@3.3.4:
|
/@vue/compiler-core@3.3.4:
|
||||||
resolution: {integrity: sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==}
|
resolution: {integrity: sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/parser': 7.22.16
|
'@babel/parser': 7.23.6
|
||||||
'@vue/shared': 3.3.4
|
'@vue/shared': 3.3.4
|
||||||
estree-walker: 2.0.2
|
estree-walker: 2.0.2
|
||||||
source-map-js: 1.0.2
|
source-map-js: 1.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@vue/compiler-dom@3.3.11:
|
||||||
|
resolution: {integrity: sha512-zoAiUIqSKqAJ81WhfPXYmFGwDRuO+loqLxvXmfUdR5fOitPoUiIeFI9cTTyv9MU5O1+ZZglJVTusWzy+wfk5hw==}
|
||||||
|
dependencies:
|
||||||
|
'@vue/compiler-core': 3.3.11
|
||||||
|
'@vue/shared': 3.3.11
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@vue/compiler-dom@3.3.4:
|
/@vue/compiler-dom@3.3.4:
|
||||||
resolution: {integrity: sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==}
|
resolution: {integrity: sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -1334,21 +1409,43 @@ packages:
|
||||||
'@vue/shared': 3.3.4
|
'@vue/shared': 3.3.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@vue/compiler-sfc@3.3.11:
|
||||||
|
resolution: {integrity: sha512-U4iqPlHO0KQeK1mrsxCN0vZzw43/lL8POxgpzcJweopmqtoYy9nljJzWDIQS3EfjiYhfdtdk9Gtgz7MRXnz3GA==}
|
||||||
|
dependencies:
|
||||||
|
'@babel/parser': 7.23.6
|
||||||
|
'@vue/compiler-core': 3.3.11
|
||||||
|
'@vue/compiler-dom': 3.3.11
|
||||||
|
'@vue/compiler-ssr': 3.3.11
|
||||||
|
'@vue/reactivity-transform': 3.3.11
|
||||||
|
'@vue/shared': 3.3.11
|
||||||
|
estree-walker: 2.0.2
|
||||||
|
magic-string: 0.30.5
|
||||||
|
postcss: 8.4.32
|
||||||
|
source-map-js: 1.0.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@vue/compiler-sfc@3.3.4:
|
/@vue/compiler-sfc@3.3.4:
|
||||||
resolution: {integrity: sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==}
|
resolution: {integrity: sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/parser': 7.22.16
|
'@babel/parser': 7.23.6
|
||||||
'@vue/compiler-core': 3.3.4
|
'@vue/compiler-core': 3.3.4
|
||||||
'@vue/compiler-dom': 3.3.4
|
'@vue/compiler-dom': 3.3.4
|
||||||
'@vue/compiler-ssr': 3.3.4
|
'@vue/compiler-ssr': 3.3.4
|
||||||
'@vue/reactivity-transform': 3.3.4
|
'@vue/reactivity-transform': 3.3.4
|
||||||
'@vue/shared': 3.3.4
|
'@vue/shared': 3.3.4
|
||||||
estree-walker: 2.0.2
|
estree-walker: 2.0.2
|
||||||
magic-string: 0.30.3
|
magic-string: 0.30.5
|
||||||
postcss: 8.4.29
|
postcss: 8.4.32
|
||||||
source-map-js: 1.0.2
|
source-map-js: 1.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@vue/compiler-ssr@3.3.11:
|
||||||
|
resolution: {integrity: sha512-Zd66ZwMvndxRTgVPdo+muV4Rv9n9DwQ4SSgWWKWkPFebHQfVYRrVjeygmmDmPewsHyznCNvJ2P2d6iOOhdv8Qg==}
|
||||||
|
dependencies:
|
||||||
|
'@vue/compiler-dom': 3.3.11
|
||||||
|
'@vue/shared': 3.3.11
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@vue/compiler-ssr@3.3.4:
|
/@vue/compiler-ssr@3.3.4:
|
||||||
resolution: {integrity: sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==}
|
resolution: {integrity: sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -1370,23 +1467,33 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@volar/language-core': 1.10.1
|
'@volar/language-core': 1.10.1
|
||||||
'@volar/source-map': 1.10.1
|
'@volar/source-map': 1.10.1
|
||||||
'@vue/compiler-dom': 3.3.4
|
'@vue/compiler-dom': 3.3.11
|
||||||
'@vue/reactivity': 3.3.4
|
'@vue/reactivity': 3.3.4
|
||||||
'@vue/shared': 3.3.4
|
'@vue/shared': 3.3.11
|
||||||
minimatch: 9.0.3
|
minimatch: 9.0.3
|
||||||
muggle-string: 0.3.1
|
muggle-string: 0.3.1
|
||||||
typescript: 4.9.5
|
typescript: 4.9.5
|
||||||
vue-template-compiler: 2.7.14
|
vue-template-compiler: 2.7.14
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@vue/reactivity-transform@3.3.11:
|
||||||
|
resolution: {integrity: sha512-fPGjH0wqJo68A0wQ1k158utDq/cRyZNlFoxGwNScE28aUFOKFEnCBsvyD8jHn+0kd0UKVpuGuaZEQ6r9FJRqCg==}
|
||||||
|
dependencies:
|
||||||
|
'@babel/parser': 7.23.6
|
||||||
|
'@vue/compiler-core': 3.3.11
|
||||||
|
'@vue/shared': 3.3.11
|
||||||
|
estree-walker: 2.0.2
|
||||||
|
magic-string: 0.30.5
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@vue/reactivity-transform@3.3.4:
|
/@vue/reactivity-transform@3.3.4:
|
||||||
resolution: {integrity: sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==}
|
resolution: {integrity: sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/parser': 7.22.16
|
'@babel/parser': 7.23.6
|
||||||
'@vue/compiler-core': 3.3.4
|
'@vue/compiler-core': 3.3.4
|
||||||
'@vue/shared': 3.3.4
|
'@vue/shared': 3.3.4
|
||||||
estree-walker: 2.0.2
|
estree-walker: 2.0.2
|
||||||
magic-string: 0.30.3
|
magic-string: 0.30.5
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@vue/reactivity@3.3.4:
|
/@vue/reactivity@3.3.4:
|
||||||
|
|
@ -1420,6 +1527,10 @@ packages:
|
||||||
vue: 3.3.4
|
vue: 3.3.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@vue/shared@3.3.11:
|
||||||
|
resolution: {integrity: sha512-u2G8ZQ9IhMWTMXaWqZycnK4UthG1fA238CD+DP4Dm4WJi5hdUKKLg0RMRaRpDPNMdkTwIDkp7WtD0Rd9BH9fLw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@vue/shared@3.3.4:
|
/@vue/shared@3.3.4:
|
||||||
resolution: {integrity: sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==}
|
resolution: {integrity: sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
@ -1616,6 +1727,28 @@ packages:
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/ast-kit@0.11.3:
|
||||||
|
resolution: {integrity: sha512-qdwwKEhckRk0XE22/xDdmU3v/60E8Edu4qFhgTLIhGGDs/PAJwLw9pQn8Rj99PitlbBZbYpx0k/lbir4kg0SuA==}
|
||||||
|
engines: {node: '>=16.14.0'}
|
||||||
|
dependencies:
|
||||||
|
'@babel/parser': 7.23.6
|
||||||
|
'@rollup/pluginutils': 5.1.0
|
||||||
|
pathe: 1.1.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- rollup
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/ast-kit@0.9.5:
|
||||||
|
resolution: {integrity: sha512-kbL7ERlqjXubdDd+szuwdlQ1xUxEz9mCz1+m07ftNVStgwRb2RWw+U6oKo08PAvOishMxiqz1mlJyLl8yQx2Qg==}
|
||||||
|
engines: {node: '>=16.14.0'}
|
||||||
|
dependencies:
|
||||||
|
'@babel/parser': 7.22.16
|
||||||
|
'@rollup/pluginutils': 5.0.5
|
||||||
|
pathe: 1.1.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- rollup
|
||||||
|
dev: true
|
||||||
|
|
||||||
/ast-types@0.13.4:
|
/ast-types@0.13.4:
|
||||||
resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==}
|
resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
@ -1623,6 +1756,16 @@ packages:
|
||||||
tslib: 2.6.2
|
tslib: 2.6.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/ast-walker-scope@0.5.0:
|
||||||
|
resolution: {integrity: sha512-NsyHMxBh4dmdEHjBo1/TBZvCKxffmZxRYhmclfu0PP6Aftre47jOHYaYaNqJcV0bxihxFXhDkzLHUwHc0ocd0Q==}
|
||||||
|
engines: {node: '>=16.14.0'}
|
||||||
|
dependencies:
|
||||||
|
'@babel/parser': 7.22.16
|
||||||
|
ast-kit: 0.9.5
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- rollup
|
||||||
|
dev: true
|
||||||
|
|
||||||
/async-retry@1.3.3:
|
/async-retry@1.3.3:
|
||||||
resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==}
|
resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -4182,6 +4325,14 @@ packages:
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/local-pkg@0.5.0:
|
||||||
|
resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
dependencies:
|
||||||
|
mlly: 1.4.2
|
||||||
|
pkg-types: 1.0.3
|
||||||
|
dev: true
|
||||||
|
|
||||||
/locate-path@2.0.0:
|
/locate-path@2.0.0:
|
||||||
resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==}
|
resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
@ -4290,6 +4441,13 @@ packages:
|
||||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/magic-string-ast@0.3.0:
|
||||||
|
resolution: {integrity: sha512-0shqecEPgdFpnI3AP90epXyxZy9g6CRZ+SZ7BcqFwYmtFEnZ1jpevcV5HoyVnlDS9gCnc1UIg3Rsvp3Ci7r8OA==}
|
||||||
|
engines: {node: '>=16.14.0'}
|
||||||
|
dependencies:
|
||||||
|
magic-string: 0.30.3
|
||||||
|
dev: true
|
||||||
|
|
||||||
/magic-string@0.25.9:
|
/magic-string@0.25.9:
|
||||||
resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
|
resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -4317,6 +4475,13 @@ packages:
|
||||||
'@jridgewell/sourcemap-codec': 1.4.15
|
'@jridgewell/sourcemap-codec': 1.4.15
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/magic-string@0.30.5:
|
||||||
|
resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/sourcemap-codec': 1.4.15
|
||||||
|
dev: true
|
||||||
|
|
||||||
/make-dir@2.1.0:
|
/make-dir@2.1.0:
|
||||||
resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
|
resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
@ -4569,8 +4734,8 @@ packages:
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/nanoid@3.3.6:
|
/nanoid@3.3.7:
|
||||||
resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
|
resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
|
||||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
@ -5281,7 +5446,16 @@ packages:
|
||||||
resolution: {integrity: sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==}
|
resolution: {integrity: sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==}
|
||||||
engines: {node: ^10 || ^12 || >=14}
|
engines: {node: ^10 || ^12 || >=14}
|
||||||
dependencies:
|
dependencies:
|
||||||
nanoid: 3.3.6
|
nanoid: 3.3.7
|
||||||
|
picocolors: 1.0.0
|
||||||
|
source-map-js: 1.0.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/postcss@8.4.32:
|
||||||
|
resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==}
|
||||||
|
engines: {node: ^10 || ^12 || >=14}
|
||||||
|
dependencies:
|
||||||
|
nanoid: 3.3.7
|
||||||
picocolors: 1.0.0
|
picocolors: 1.0.0
|
||||||
source-map-js: 1.0.2
|
source-map-js: 1.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
@ -6377,14 +6551,14 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.10.0
|
acorn: 8.10.0
|
||||||
estree-walker: 3.0.3
|
estree-walker: 3.0.3
|
||||||
magic-string: 0.30.3
|
magic-string: 0.30.5
|
||||||
unplugin: 1.5.0
|
unplugin: 1.5.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/unimport@2.2.4:
|
/unimport@2.2.4:
|
||||||
resolution: {integrity: sha512-qMgmeEGqqrrmEtm0dqxMG37J6xBtrriqxq9hILvDb+e6l2F0yTnJomLoCCp0eghLR7bYGeBsUU5Y0oyiUYhViw==}
|
resolution: {integrity: sha512-qMgmeEGqqrrmEtm0dqxMG37J6xBtrriqxq9hILvDb+e6l2F0yTnJomLoCCp0eghLR7bYGeBsUU5Y0oyiUYhViw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@rollup/pluginutils': 5.0.5
|
'@rollup/pluginutils': 5.1.0
|
||||||
escape-string-regexp: 5.0.0
|
escape-string-regexp: 5.0.0
|
||||||
fast-glob: 3.3.1
|
fast-glob: 3.3.1
|
||||||
local-pkg: 0.4.3
|
local-pkg: 0.4.3
|
||||||
|
|
@ -6402,11 +6576,11 @@ packages:
|
||||||
/unimport@3.3.0:
|
/unimport@3.3.0:
|
||||||
resolution: {integrity: sha512-3jhq3ZG5hFZzrWGDCpx83kjPzefP/EeuKkIO1T0MA4Zwj+dO/Og1mFvZ4aZ5WSDm0FVbbdVIRH1zKBG7c4wOpg==}
|
resolution: {integrity: sha512-3jhq3ZG5hFZzrWGDCpx83kjPzefP/EeuKkIO1T0MA4Zwj+dO/Og1mFvZ4aZ5WSDm0FVbbdVIRH1zKBG7c4wOpg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@rollup/pluginutils': 5.0.5
|
'@rollup/pluginutils': 5.1.0
|
||||||
escape-string-regexp: 5.0.0
|
escape-string-regexp: 5.0.0
|
||||||
fast-glob: 3.3.1
|
fast-glob: 3.3.1
|
||||||
local-pkg: 0.4.3
|
local-pkg: 0.4.3
|
||||||
magic-string: 0.30.3
|
magic-string: 0.30.5
|
||||||
mlly: 1.4.2
|
mlly: 1.4.2
|
||||||
pathe: 1.1.1
|
pathe: 1.1.1
|
||||||
pkg-types: 1.0.3
|
pkg-types: 1.0.3
|
||||||
|
|
@ -6517,6 +6691,33 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/unplugin-vue-router@0.7.0(vue-router@4.2.4)(vue@3.3.4):
|
||||||
|
resolution: {integrity: sha512-ddRreGq0t5vlSB7OMy4e4cfU1w2AwBQCwmvW3oP/0IHQiokzbx4hd3TpwBu3eIAFVuhX2cwNQwp1U32UybTVCw==}
|
||||||
|
peerDependencies:
|
||||||
|
vue-router: ^4.1.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
vue-router:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@babel/types': 7.23.0
|
||||||
|
'@rollup/pluginutils': 5.0.5
|
||||||
|
'@vue-macros/common': 1.10.0(vue@3.3.4)
|
||||||
|
ast-walker-scope: 0.5.0
|
||||||
|
chokidar: 3.5.3
|
||||||
|
fast-glob: 3.3.1
|
||||||
|
json5: 2.2.3
|
||||||
|
local-pkg: 0.4.3
|
||||||
|
mlly: 1.4.2
|
||||||
|
pathe: 1.1.1
|
||||||
|
scule: 1.0.0
|
||||||
|
unplugin: 1.5.0
|
||||||
|
vue-router: 4.2.4(vue@3.3.4)
|
||||||
|
yaml: 2.3.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- rollup
|
||||||
|
- vue
|
||||||
|
dev: true
|
||||||
|
|
||||||
/unplugin@1.4.0:
|
/unplugin@1.4.0:
|
||||||
resolution: {integrity: sha512-5x4eIEL6WgbzqGtF9UV8VEC/ehKptPXDS6L2b0mv4FRMkJxRtjaJfOWDd6a8+kYbqsjklix7yWP0N3SUepjXcg==}
|
resolution: {integrity: sha512-5x4eIEL6WgbzqGtF9UV8VEC/ehKptPXDS6L2b0mv4FRMkJxRtjaJfOWDd6a8+kYbqsjklix7yWP0N3SUepjXcg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -6546,7 +6747,7 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.22.17
|
'@babel/core': 7.22.17
|
||||||
'@babel/standalone': 7.22.17
|
'@babel/standalone': 7.22.17
|
||||||
'@babel/types': 7.23.0
|
'@babel/types': 7.23.6
|
||||||
defu: 6.1.2
|
defu: 6.1.2
|
||||||
jiti: 1.20.0
|
jiti: 1.20.0
|
||||||
mri: 1.2.0
|
mri: 1.2.0
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { env } from '@/config/env';
|
||||||
* @see src/api/instance/instance.ts
|
* @see src/api/instance/instance.ts
|
||||||
*/
|
*/
|
||||||
export const api = new Service({
|
export const api = new Service({
|
||||||
|
timeout: 2000,
|
||||||
baseURL: env.apiPrefix,
|
baseURL: env.apiPrefix,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -17,12 +18,12 @@ export const api = new Service({
|
||||||
*/
|
*/
|
||||||
addToastInterceptor(api.instance);
|
addToastInterceptor(api.instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加异常处理拦截器
|
||||||
|
*/
|
||||||
|
addExceptionInterceptor(api.instance, () => api.expireHandler?.());
|
||||||
/**
|
/**
|
||||||
* 添加登陆令牌拦截器
|
* 添加登陆令牌拦截器
|
||||||
*/
|
*/
|
||||||
addAuthInterceptor(api.instance);
|
addAuthInterceptor(api.instance);
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加异常处理拦截器
|
|
||||||
*/
|
|
||||||
addExceptionInterceptor(api.instance, () => api.expireHandler?.());
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ export function addAuthInterceptor(axios: AxiosInstance) {
|
||||||
if (userStore.accessToken) {
|
if (userStore.accessToken) {
|
||||||
config.headers.Authorization = `Bearer ${userStore.accessToken}`;
|
config.headers.Authorization = `Bearer ${userStore.accessToken}`;
|
||||||
}
|
}
|
||||||
|
// throw Error('dd');
|
||||||
return config;
|
return config;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ export function addExceptionInterceptor(axios: AxiosInstance, exipreHandler?: (.
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
|
console.log('res error', error);
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
const code = error.response.data?.code;
|
const code = error.response.data?.code;
|
||||||
if (expiredCodes.includes(code)) {
|
if (expiredCodes.includes(code)) {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ export function addToastInterceptor(axios: AxiosInstance) {
|
||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
error.config.closeToast?.();
|
error.config?.closeToast?.();
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -37,7 +37,7 @@ export function addToastInterceptor(axios: AxiosInstance) {
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
error.config.closeToast?.();
|
error.config?.closeToast?.();
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
<img src="@/assets/403.svg" alt="forbiden" class="w-[320px]" />
|
<img src="@/assets/403.svg" alt="forbiden" class="w-[320px]" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 class="text-3xl m-0 font-bold">403</h2>
|
<h2 class="text-3xl m-0 font-medium">403</h2>
|
||||||
<p class="mt-2">权限不足,如需访问请联系管理员!</p>
|
<p class="mt-3">权限不足,如需访问请联系管理员!</p>
|
||||||
<div class="space-x-3 mt-6">
|
<div class="space-x-3 mt-6">
|
||||||
<a-button type="primary" @click="router.back()">
|
<a-button type="primary" @click="router.back()">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,29 @@
|
||||||
import { Form, FormInstance } from '@arco-design/web-vue';
|
import { Form, FormInstance, Message } from '@arco-design/web-vue';
|
||||||
import { useVModel } from '@vueuse/core';
|
import { useVModel } from '@vueuse/core';
|
||||||
import { PropType } from 'vue';
|
import { ComputedRef, InjectionKey, PropType, Ref } from 'vue';
|
||||||
import { FormContextKey } from './useFormContext';
|
import { initFormItems } from '../utils/useFormItems';
|
||||||
import { useFormItems } from './useFormItems';
|
import { FormRef, useFormRef } from '../utils/useFormRef';
|
||||||
import { useFormModel } from './useFormModel';
|
|
||||||
import { useFormRef } from './useFormRef';
|
|
||||||
import { useFormSubmit } from './useFormSubmit';
|
|
||||||
import { AnFormItem, AnFormItemProps } from './FormItem';
|
import { AnFormItem, AnFormItemProps } from './FormItem';
|
||||||
|
import { cloneDeep, isFunction, isObject, merge } from 'lodash-es';
|
||||||
|
import { getModel } from '../utils/useFormModel';
|
||||||
|
|
||||||
|
const SUBMIT_ITEM = {
|
||||||
|
field: 'id',
|
||||||
|
setter: 'submit' as const,
|
||||||
|
itemProps: {
|
||||||
|
hideLabel: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FormContextInterface = FormRef & {
|
||||||
|
model: Ref<Recordable>;
|
||||||
|
items: ComputedRef<AnFormItemProps[]>;
|
||||||
|
loading: Ref<boolean>;
|
||||||
|
submitForm: any;
|
||||||
|
resetForm: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FormContextKey = Symbol('FormContextKey') as InjectionKey<FormContextInterface>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表单组件
|
* 表单组件
|
||||||
|
|
@ -50,7 +67,7 @@ export const AnForm = defineComponent({
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
submit: {
|
submit: {
|
||||||
type: [String, Function, Object] as PropType<AnFormSubmit>,
|
type: [Function, Object] as PropType<AnFormSubmit>,
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 传给Form组件的参数
|
* 传给Form组件的参数
|
||||||
|
|
@ -69,25 +86,61 @@ export const AnForm = defineComponent({
|
||||||
setup(props, { slots, emit }) {
|
setup(props, { slots, emit }) {
|
||||||
const model = useVModel(props, 'model', emit);
|
const model = useVModel(props, 'model', emit);
|
||||||
const items = computed(() => props.items);
|
const items = computed(() => props.items);
|
||||||
const formRefes = useFormRef();
|
const initModel = cloneDeep(model.value);
|
||||||
const formModel = useFormModel(model, formRefes.clearValidate);
|
const loading = ref(false);
|
||||||
const formItems = useFormItems(items, model);
|
const { formRef, ...formMethods } = useFormRef();
|
||||||
const formSubmit = useFormSubmit(props, formRefes.validate, formModel.getModel);
|
|
||||||
const context = { slots, ...formModel, ...formItems, ...formRefes, ...formSubmit };
|
|
||||||
|
|
||||||
|
const submitItem = () => {
|
||||||
|
if (!props.submit) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (isFunction(props.submit)) {
|
||||||
|
return SUBMIT_ITEM;
|
||||||
|
}
|
||||||
|
if (isObject(props.submit)) {
|
||||||
|
return merge({}, SUBMIT_ITEM, props.submit);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
model.value = cloneDeep(initModel);
|
||||||
|
formRef.value?.clearValidate();
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitForm = async () => {
|
||||||
|
if (await formRef.value?.validate()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const submit: any = typeof props.submit === 'object' ? props.submit.visible : props.submit;
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
const data = getModel(model.value);
|
||||||
|
const res = await submit?.(data, props.items);
|
||||||
|
const msg = res?.data?.message;
|
||||||
|
msg && Message.success(`提示: ${msg}`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const context = { slots, loading, resetForm, submitForm, submitItem, model, items, formRef, ...formMethods };
|
||||||
provide(FormContextKey, context);
|
provide(FormContextKey, context);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initFormItems(props.items, model.value);
|
||||||
|
});
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Form layout="vertical" {...this.$attrs} {...this.formProps} class="an-form" ref="formRef" model={this.model}>
|
<Form layout="vertical" {...this.formProps} class="an-form" ref="formRef" model={this.model}>
|
||||||
{this.items.map(item => (
|
{this.items.map(item => (
|
||||||
<AnFormItem key={item.field} item={item} items={this.items} model={this.model}></AnFormItem>
|
<AnFormItem key={item.field} item={item} items={this.items} model={this.model}></AnFormItem>
|
||||||
))}
|
))}
|
||||||
{this.$slots.submit?.(this.model, this.validate) ||
|
{this.submitItem()}
|
||||||
(this.submit && this.submitItem && (
|
|
||||||
<AnFormItem item={this.submitItem} items={this.items} model={this.model}></AnFormItem>
|
|
||||||
))}
|
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -99,4 +152,4 @@ export type AnFormProps = Pick<AnFormInstance['$props'], 'model' | 'items' | 'su
|
||||||
|
|
||||||
export type AnFormSubmitFn = (model: Recordable, items: AnFormItemProps[]) => any;
|
export type AnFormSubmitFn = (model: Recordable, items: AnFormItemProps[]) => any;
|
||||||
|
|
||||||
export type AnFormSubmit = string | AnFormSubmitFn;
|
export type AnFormSubmit = AnFormSubmitFn | AnFormItemProps;
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,17 @@
|
||||||
import { useVisible } from '@/hooks/useVisible';
|
import { Button, ButtonInstance, FormInstance, Message, Modal } from '@arco-design/web-vue';
|
||||||
import { Button, ButtonInstance, FormInstance, Modal } from '@arco-design/web-vue';
|
|
||||||
import { InjectionKey, PropType, Ref } from 'vue';
|
import { InjectionKey, PropType, Ref } from 'vue';
|
||||||
import { useModalSubmit } from './useModalSubmit';
|
import { AnForm, AnFormInstance, AnFormSubmit } from './Form';
|
||||||
import { useModalTrigger } from './useModalTrigger';
|
|
||||||
import { AnForm, AnFormInstance, AnFormProps, AnFormSubmit } from './Form';
|
|
||||||
import { AnFormItemProps } from './FormItem';
|
import { AnFormItemProps } from './FormItem';
|
||||||
import { useVModel } from '@vueuse/core';
|
import { useVModel } from '@vueuse/core';
|
||||||
|
import { getModel, setModel } from '../utils/useFormModel';
|
||||||
|
|
||||||
export interface AnFormModalContext {
|
export interface AnFormModalContext {
|
||||||
visible: Ref<boolean>;
|
visible: Ref<boolean>;
|
||||||
loading: Ref<boolean>;
|
loading: Ref<boolean>;
|
||||||
formRef: Ref<AnFormInstance | null>;
|
anFormRef: Ref<AnFormInstance | null>;
|
||||||
|
submitForm: () => any | Promise<any>;
|
||||||
open: (data: Recordable) => void;
|
open: (data: Recordable) => void;
|
||||||
close: () => void;
|
close: () => void;
|
||||||
submitForm: () => any | Promise<any>;
|
|
||||||
modalTitle: () => any;
|
modalTitle: () => any;
|
||||||
modalTrigger: () => any;
|
modalTrigger: () => any;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
|
|
@ -97,7 +95,7 @@ export const AnFormModal = defineComponent({
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
submit: {
|
submit: {
|
||||||
type: [String, Function] as PropType<AnFormSubmit>,
|
type: [Object, Function] as PropType<AnFormSubmit>,
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 传给Form组件的参数
|
* 传给Form组件的参数
|
||||||
|
|
@ -114,25 +112,10 @@ export const AnFormModal = defineComponent({
|
||||||
},
|
},
|
||||||
emits: ['update:model', 'submited'],
|
emits: ['update:model', 'submited'],
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
const formRef = ref<AnFormInstance | null>(null);
|
|
||||||
const model = useVModel(props, 'model', emit);
|
const model = useVModel(props, 'model', emit);
|
||||||
|
const anFormRef = ref<AnFormInstance | null>(null);
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
const show = () => (visible.value = true);
|
const loading = ref(false);
|
||||||
const hide = () => (visible.value = false);
|
|
||||||
const modalTrigger = useModalTrigger(props, show);
|
|
||||||
const { loading, setLoading, submitForm } = useModalSubmit(props, formRef, visible, emit, model);
|
|
||||||
|
|
||||||
const open = (data: Recordable = {}) => {
|
|
||||||
formRef.value?.setModel(data);
|
|
||||||
visible.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const close = () => {
|
|
||||||
setLoading(false);
|
|
||||||
hide();
|
|
||||||
};
|
|
||||||
|
|
||||||
const onClose = () => {};
|
|
||||||
|
|
||||||
const modalTitle = () => {
|
const modalTitle = () => {
|
||||||
if (typeof props.title === 'string') {
|
if (typeof props.title === 'string') {
|
||||||
|
|
@ -141,10 +124,71 @@ export const AnFormModal = defineComponent({
|
||||||
return <props.title model={props.model} items={props.items}></props.title>;
|
return <props.title model={props.model} items={props.items}></props.title>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const modalTrigger = () => {
|
||||||
|
if (!props.trigger) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (typeof props.trigger === 'function') {
|
||||||
|
return <props.trigger model={props.model} items={props.items} open={open}></props.trigger>;
|
||||||
|
}
|
||||||
|
const internal = {
|
||||||
|
text: '新增',
|
||||||
|
buttonProps: {},
|
||||||
|
buttonSlots: {},
|
||||||
|
};
|
||||||
|
if (typeof props.trigger === 'string') {
|
||||||
|
internal.text = props.trigger;
|
||||||
|
}
|
||||||
|
if (typeof props.trigger === 'object') {
|
||||||
|
Object.assign(internal, props.trigger);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Button type="primary" {...internal.buttonProps} onClick={open}>
|
||||||
|
{{
|
||||||
|
...internal.buttonSlots,
|
||||||
|
icon: () => <i class="icon-park-outline-add"></i>,
|
||||||
|
default: () => internal.text,
|
||||||
|
}}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitForm = async () => {
|
||||||
|
if (await anFormRef.value?.validate()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
const data = getModel(model.value);
|
||||||
|
const res = await (props as any).submit?.(data, props.items);
|
||||||
|
const msg = res?.data?.message;
|
||||||
|
msg && Message.success(msg);
|
||||||
|
visible.value = false;
|
||||||
|
emit('submited', res);
|
||||||
|
} catch {
|
||||||
|
// todo
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const open = async (data: Recordable = {}) => {
|
||||||
|
visible.value = true;
|
||||||
|
await nextTick();
|
||||||
|
anFormRef.value && setModel(model.value, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
loading.value = false;
|
||||||
|
visible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClose = () => {};
|
||||||
|
|
||||||
const context: AnFormModalContext = {
|
const context: AnFormModalContext = {
|
||||||
visible,
|
visible,
|
||||||
loading,
|
loading,
|
||||||
formRef,
|
anFormRef,
|
||||||
open,
|
open,
|
||||||
close,
|
close,
|
||||||
onClose,
|
onClose,
|
||||||
|
|
@ -155,7 +199,9 @@ export const AnFormModal = defineComponent({
|
||||||
|
|
||||||
provide(AnFormModalContextKey, context);
|
provide(AnFormModalContextKey, context);
|
||||||
|
|
||||||
return context;
|
return {
|
||||||
|
...context
|
||||||
|
};
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -164,11 +210,11 @@ export const AnFormModal = defineComponent({
|
||||||
<Modal
|
<Modal
|
||||||
titleAlign="start"
|
titleAlign="start"
|
||||||
closable={false}
|
closable={false}
|
||||||
{...this.$attrs}
|
|
||||||
{...this.modalProps}
|
{...this.modalProps}
|
||||||
v-model:visible={this.visible}
|
v-model:visible={this.visible}
|
||||||
class="an-form-modal"
|
class="an-form-modal"
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
|
unmountOnClose={true}
|
||||||
onClose={this.onClose}
|
onClose={this.onClose}
|
||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
import { InjectionKey } from "vue";
|
|
||||||
import { FormItems } from "./useFormItems";
|
|
||||||
import { FormModel } from "./useFormModel";
|
|
||||||
import { FormRef } from "./useFormRef";
|
|
||||||
import { FormSubmit } from "./useFormSubmit";
|
|
||||||
|
|
||||||
export type FormContextInterface = FormModel &
|
|
||||||
FormItems &
|
|
||||||
FormRef &
|
|
||||||
FormSubmit & {
|
|
||||||
slots: Recordable;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const FormContextKey = Symbol("FormContextKey") as InjectionKey<FormContextInterface>;
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
import { Ref } from 'vue';
|
|
||||||
import { AnFormItemProps } from './FormItem';
|
|
||||||
import { setterMap } from './FormSetter';
|
|
||||||
|
|
||||||
export function useFormItems(items: Ref<AnFormItemProps[]>, model: Ref<Recordable>) {
|
|
||||||
const getItem = (field: string) => {
|
|
||||||
return items.value.find(i => i.field === field);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getItemOptions = (field: string) => {
|
|
||||||
const item = getItem(field);
|
|
||||||
if (item) {
|
|
||||||
return (item.setterProps as any)?.options;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const initItemOptions = (field: string) => {
|
|
||||||
const item = getItem(field);
|
|
||||||
if (!item) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const setter = setterMap[item.setter!];
|
|
||||||
if (!setter) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setter.onSetup?.({ item, items: items.value, model: model.value });
|
|
||||||
};
|
|
||||||
|
|
||||||
const initItems = () => {
|
|
||||||
for (const item of items.value) {
|
|
||||||
const setter = setterMap[item?.setter!];
|
|
||||||
setter.onSetup?.({ item, items: items.value, model: model.value });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const initItem = (field: string) => {
|
|
||||||
const item = getItem(field);
|
|
||||||
if (!item) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const setter = setterMap[item?.setter!];
|
|
||||||
setter.onSetup?.({ item, items: items.value, model: model.value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
initItems();
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
items,
|
|
||||||
getItem,
|
|
||||||
initItem,
|
|
||||||
initItems,
|
|
||||||
getItemOptions,
|
|
||||||
initItemOptions,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export type FormItems = ReturnType<typeof useFormItems>;
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
import { cloneDeep } from 'lodash-es';
|
|
||||||
import { Ref } from 'vue';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 表单数据管理
|
|
||||||
* @param initial 初始值
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function useFormModel(model: Ref<Recordable>, clearValidate: any) {
|
|
||||||
const initial = cloneDeep(model.value);
|
|
||||||
|
|
||||||
const resetModel = () => {
|
|
||||||
model.value = cloneDeep(initial);
|
|
||||||
clearValidate();
|
|
||||||
};
|
|
||||||
|
|
||||||
const getInitialModel = () => {
|
|
||||||
return initial;
|
|
||||||
};
|
|
||||||
|
|
||||||
const setModel = (data: Recordable) => {
|
|
||||||
for (const key of Object.keys(model.value)) {
|
|
||||||
model.value[key] = data[key];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getModel = () => {
|
|
||||||
return formatModel(model.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
model,
|
|
||||||
getInitialModel,
|
|
||||||
resetModel,
|
|
||||||
setModel,
|
|
||||||
getModel,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export type FormModel = ReturnType<typeof useFormModel>;
|
|
||||||
|
|
||||||
export function formatModel(model: Recordable) {
|
|
||||||
const data: Recordable = {};
|
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(model)) {
|
|
||||||
if (value === '') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (/^\[.+\]$/.test(key)) {
|
|
||||||
formatModelArray(key, value, data);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (/^\{.+\}$/.test(key)) {
|
|
||||||
formatModelObject(key, value, data);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
data[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatModelArray(key: string, value: any, data: Recordable) {
|
|
||||||
let field = key.replaceAll(/\s/g, '');
|
|
||||||
field = field.match(/^\[(.+)\]$/)?.[1] ?? '';
|
|
||||||
|
|
||||||
if (!field) {
|
|
||||||
data[key] = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
field.split(',').forEach((key, index) => {
|
|
||||||
data[key] = value?.[index];
|
|
||||||
});
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatModelObject(key: string, value: any, data: Recordable) {
|
|
||||||
let field = key.replaceAll(/\s/g, '');
|
|
||||||
field = field.match(/^\{(.+)\}$/)?.[1] ?? '';
|
|
||||||
|
|
||||||
if (!field) {
|
|
||||||
data[key] = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key of field.split(',')) {
|
|
||||||
data[key] = value?.[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
import { Message } from '@arco-design/web-vue';
|
|
||||||
import { AnFormProps } from './Form';
|
|
||||||
import { AnFormItemProps } from './FormItem';
|
|
||||||
import { cloneDeep } from 'lodash-es';
|
|
||||||
|
|
||||||
const SUBMIT_ITEM = {
|
|
||||||
field: 'id',
|
|
||||||
setter: 'submit' as const,
|
|
||||||
itemProps: {
|
|
||||||
hideLabel: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export function useFormSubmit(props: AnFormProps, validate: any, getModel: any) {
|
|
||||||
const loading = ref(false);
|
|
||||||
const submitItem = ref<AnFormItemProps | null>(null);
|
|
||||||
|
|
||||||
if (props.submit) {
|
|
||||||
submitItem.value = cloneDeep(SUBMIT_ITEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置loading
|
|
||||||
* @param value 值
|
|
||||||
*/
|
|
||||||
const setLoading = (value: boolean) => {
|
|
||||||
loading.value = value;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 提交表单
|
|
||||||
*/
|
|
||||||
const submitForm = async () => {
|
|
||||||
if (await validate()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const submit = typeof props.submit === 'string' ? () => null : props.submit;
|
|
||||||
try {
|
|
||||||
loading.value = true;
|
|
||||||
const data = getModel();
|
|
||||||
const res = await submit?.(data, props.items ?? []);
|
|
||||||
const msg = res?.data?.message;
|
|
||||||
msg && Message.success(`提示: ${msg}`);
|
|
||||||
} catch {
|
|
||||||
console.log();
|
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 取消提交
|
|
||||||
*/
|
|
||||||
const cancelForm = () => {};
|
|
||||||
|
|
||||||
return {
|
|
||||||
loading,
|
|
||||||
submitItem,
|
|
||||||
setLoading,
|
|
||||||
submitForm,
|
|
||||||
cancelForm,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export type FormSubmit = ReturnType<typeof useFormSubmit>;
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
import { sleep } from '@/utils';
|
|
||||||
import { Message } from '@arco-design/web-vue';
|
|
||||||
import { cloneDeep } from 'lodash-es';
|
|
||||||
import { Ref } from 'vue';
|
|
||||||
|
|
||||||
export function useModalSubmit(props: any, formRef: any, visible: Ref<boolean>, emit?: any, model?: Ref<Recordable>) {
|
|
||||||
const loading = ref(false);
|
|
||||||
const origin = cloneDeep(props.model);
|
|
||||||
|
|
||||||
const submitForm = async () => {
|
|
||||||
if (await formRef.value?.validate()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
loading.value = true;
|
|
||||||
const data = formRef.value?.getModel() ?? {};
|
|
||||||
const res = await props.submit?.(data, props.items);
|
|
||||||
const msg = res?.data?.message;
|
|
||||||
msg && Message.success(msg);
|
|
||||||
emit('submited', res);
|
|
||||||
visible.value = false;
|
|
||||||
if (model) {
|
|
||||||
model.value = cloneDeep(origin);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// todo
|
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const setLoading = (value: boolean) => {
|
|
||||||
loading.value = value;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
loading,
|
|
||||||
setLoading,
|
|
||||||
submitForm,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
import { Button } from '@arco-design/web-vue';
|
|
||||||
|
|
||||||
export function useModalTrigger(props: any, open: () => void) {
|
|
||||||
const modalTrigger = () => {
|
|
||||||
if (!props.trigger) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (typeof props.trigger === 'function') {
|
|
||||||
return <props.trigger model={props.model} items={props.items} open={open}></props.trigger>;
|
|
||||||
}
|
|
||||||
const internal = {
|
|
||||||
text: '新增',
|
|
||||||
buttonProps: {},
|
|
||||||
buttonSlots: {},
|
|
||||||
};
|
|
||||||
if (typeof props.trigger === 'string') {
|
|
||||||
internal.text = props.trigger;
|
|
||||||
}
|
|
||||||
if (typeof props.trigger === 'object') {
|
|
||||||
Object.assign(internal, props.trigger);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Button type="primary" {...internal.buttonProps} onClick={open}>
|
|
||||||
{{
|
|
||||||
...internal.buttonSlots,
|
|
||||||
icon: () => <i class="icon-park-outline-add"></i>,
|
|
||||||
default: () => internal.text,
|
|
||||||
}}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
return modalTrigger;
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { merge } from 'lodash-es';
|
import { merge } from 'lodash-es';
|
||||||
import { AnForm, AnFormInstance, AnFormProps } from '../components/Form';
|
import { AnForm, AnFormInstance, AnFormProps } from '../components/Form';
|
||||||
import { FormItem, useItems } from './useItems';
|
import { FormItem, useFormItems } from './useFormItems';
|
||||||
|
|
||||||
export type FormUseOptions = Partial<Omit<AnFormProps, 'items'>> & {
|
export type FormUseOptions = Partial<Omit<AnFormProps, 'items'>> & {
|
||||||
/**
|
/**
|
||||||
|
|
@ -20,7 +20,7 @@ export type FormUseOptions = Partial<Omit<AnFormProps, 'items'>> & {
|
||||||
export function useFormProps(options: FormUseOptions): Required<AnFormProps> {
|
export function useFormProps(options: FormUseOptions): Required<AnFormProps> {
|
||||||
const { model: _model = {}, items: _items = [], submit = () => null, formProps = {} } = options;
|
const { model: _model = {}, items: _items = [], submit = () => null, formProps = {} } = options;
|
||||||
const model = merge({ id: undefined }, _model);
|
const model = merge({ id: undefined }, _model);
|
||||||
const items = useItems(_items ?? [], model);
|
const items = useFormItems(_items ?? [], model);
|
||||||
return {
|
return {
|
||||||
model,
|
model,
|
||||||
items,
|
items,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { defaultsDeep, merge, omit } from 'lodash-es';
|
import { defaultsDeep, has, merge, omit } from 'lodash-es';
|
||||||
import { AnFormItemProps, AnFormItemPropsBase } from '../components/FormItem';
|
import { AnFormItemProps, AnFormItemPropsBase } from '../components/FormItem';
|
||||||
import { SetterItem, setterMap } from '../components/FormSetter';
|
import { SetterItem, setterMap } from '../components/FormSetter';
|
||||||
import { Rule, useRules } from './useRules';
|
import { Rule, useFormRules } from './useFormRules';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表单项数据
|
* 表单项数据
|
||||||
|
|
@ -34,17 +34,26 @@ export type FormItem = Omit<AnFormItemPropsBase, 'rules'> &
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
rules?: Rule[];
|
rules?: Rule[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数 `setterProps.placeholder` 的快捷语法
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* '请输入用户名称'
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
placeholder?: string | string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const ITEM: Partial<FormItem> = {
|
const ITEM: Partial<FormItem> = {
|
||||||
setter: 'input',
|
setter: 'input',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useItems(list: FormItem[], model: Recordable) {
|
export function useFormItems(items: FormItem[], model: Recordable) {
|
||||||
const items: AnFormItemProps[] = [];
|
const data: AnFormItemProps[] = [];
|
||||||
|
|
||||||
for (const item of list) {
|
for (const item of items) {
|
||||||
let target: any = defaultsDeep({}, ITEM);
|
let target: AnFormItemProps = defaultsDeep({}, ITEM);
|
||||||
|
|
||||||
if (!item.setter || typeof item.setter === 'string') {
|
if (!item.setter || typeof item.setter === 'string') {
|
||||||
const setter = setterMap[item.setter ?? 'input'];
|
const setter = setterMap[item.setter ?? 'input'];
|
||||||
|
|
@ -53,16 +62,23 @@ export function useItems(list: FormItem[], model: Recordable) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
target = merge(target, omit(item, ['required', 'rules', 'value']));
|
target = merge(target, omit(item, ['required', 'rules', 'value', 'placeholder']));
|
||||||
|
|
||||||
const rules = useRules(item);
|
if (item.required || item.rules) {
|
||||||
if (rules) {
|
const rules = useFormRules(item)!;
|
||||||
target.rules = rules;
|
target.rules = rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
model[item.field] = model[item.field] ?? item.value;
|
if (target.setterProps && has(item, 'placeholder')) {
|
||||||
items.push(target);
|
(target.setterProps as Recordable).placholder = item.placeholder;
|
||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
if (has(item, 'value')) {
|
||||||
|
model[item.field] = item.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.push(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { merge } from 'lodash-es';
|
import { merge } from 'lodash-es';
|
||||||
import { AnFormModal, AnFormModalProps } from '../components/FormModal';
|
import { AnFormModal, AnFormModalProps } from '../components/FormModal';
|
||||||
import { useFormProps } from './useForm';
|
import { useFormProps } from './useForm';
|
||||||
import { FormItem } from './useItems';
|
import { FormItem } from './useFormItems';
|
||||||
|
|
||||||
export type FormModalUseOptions = Partial<Omit<AnFormModalProps, 'items'>> & {
|
export type FormModalUseOptions = Partial<Omit<AnFormModalProps, 'items'>> & {
|
||||||
/**
|
/**
|
||||||
|
|
@ -13,6 +13,10 @@ export type FormModalUseOptions = Partial<Omit<AnFormModalProps, 'items'>> & {
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
width?: number;
|
width?: number;
|
||||||
|
/**
|
||||||
|
* modal宽度
|
||||||
|
*/
|
||||||
|
modalWidth?: number;
|
||||||
/**
|
/**
|
||||||
* 表单类名
|
* 表单类名
|
||||||
* @description 参数 `formProps.class` 的便捷语法
|
* @description 参数 `formProps.class` 的便捷语法
|
||||||
|
|
@ -58,7 +62,7 @@ export function useFormModalProps(options: FormModalUseOptions): AnFormModalProp
|
||||||
|
|
||||||
export function useFormModal(options: FormModalUseOptions) {
|
export function useFormModal(options: FormModalUseOptions) {
|
||||||
const modalRef = ref<InstanceType<typeof AnFormModal> | null>(null);
|
const modalRef = ref<InstanceType<typeof AnFormModal> | null>(null);
|
||||||
const formRef = computed(() => modalRef.value?.formRef);
|
const formRef = computed(() => modalRef.value?.anFormRef);
|
||||||
const open = (data: Recordable = {}) => modalRef.value?.open(data);
|
const open = (data: Recordable = {}) => modalRef.value?.open(data);
|
||||||
const rawProps = useFormModalProps(options);
|
const rawProps = useFormModalProps(options);
|
||||||
const props = reactive(rawProps);
|
const props = reactive(rawProps);
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ function defineRuleMap<T extends Record<string, FieldRule>>(ruleMap: T) {
|
||||||
* @param item 表单项
|
* @param item 表单项
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const useRules = <T extends { required?: boolean; rules?: Rule[] }>(item: T) => {
|
export const useFormRules = <T extends { required?: boolean; rules?: Rule[] }>(item: T) => {
|
||||||
const data: AnFormItemRule[] = [];
|
const data: AnFormItemRule[] = [];
|
||||||
const { required, rules } = item;
|
const { required, rules } = item;
|
||||||
|
|
||||||
|
|
@ -2,15 +2,11 @@ export * from './components/Form';
|
||||||
export * from './components/FormItem';
|
export * from './components/FormItem';
|
||||||
export * from './components/FormModal';
|
export * from './components/FormModal';
|
||||||
export * from './components/FormSetter';
|
export * from './components/FormSetter';
|
||||||
export * from './components/useFormContext';
|
export * from './utils/useFormItems';
|
||||||
export * from './components/useFormItems';
|
export * from './utils/useFormModel';
|
||||||
export * from './components/useFormModel';
|
export * from './utils/useFormRef';
|
||||||
export * from './components/useFormRef';
|
|
||||||
export * from './components/useFormSubmit';
|
|
||||||
export * from './components/useModalSubmit';
|
|
||||||
export * from './components/useModalTrigger';
|
|
||||||
export * from './hooks/useForm';
|
export * from './hooks/useForm';
|
||||||
export * from './hooks/useFormModal';
|
export * from './hooks/useFormModal';
|
||||||
export * from './hooks/useItems';
|
export * from './hooks/useFormItems';
|
||||||
export * from './hooks/useRules';
|
export * from './hooks/useFormRules';
|
||||||
export * from './setters';
|
export * from './setters';
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,16 @@
|
||||||
import { Button } from '@arco-design/web-vue';
|
import { Button } from '@arco-design/web-vue';
|
||||||
import { FormContextKey } from '../components/useFormContext';
|
import { FormContextKey } from '../components/Form';
|
||||||
import { defineSetter } from './util';
|
import { defineSetter } from './util';
|
||||||
|
|
||||||
export default defineSetter<{}, 'none'>({
|
export default defineSetter<{}, 'none'>({
|
||||||
setter() {
|
setter() {
|
||||||
const { loading, submitForm, resetModel } = inject(FormContextKey)!;
|
const { submitForm, resetForm } = inject(FormContextKey)!;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button type="primary" loading={loading.value} onClick={submitForm} class="mr-3">
|
<Button type="primary" onClick={submitForm} class="mr-3">
|
||||||
提交
|
提交
|
||||||
</Button>
|
</Button>
|
||||||
<Button disabled={loading.value} onClick={resetModel}>
|
<Button onClick={resetForm}>重置</Button>
|
||||||
重置
|
|
||||||
</Button>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { AnFormItemProps } from '../components/FormItem';
|
||||||
|
import { setterMap } from '../components/FormSetter';
|
||||||
|
|
||||||
|
export const getFormItem = (items: AnFormItemProps[], field: string) => {
|
||||||
|
return items.find(i => i.field === field);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initFormItems = (items: AnFormItemProps[], model: Recordable) => {
|
||||||
|
for (const item of items) {
|
||||||
|
const setter = setterMap[item.setter!];
|
||||||
|
setter.onSetup?.({ item, items, model });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
|
||||||
|
export function getModel(model: Recordable) {
|
||||||
|
const data: Recordable = {};
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(model)) {
|
||||||
|
if (value === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (/^\[.+\]$/.test(key)) {
|
||||||
|
getModelArray(key, value, data);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (/^\{.+\}$/.test(key)) {
|
||||||
|
getModelObject(key, value, data);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
data[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setModel(model: Recordable, data: Recordable) {
|
||||||
|
for (const [key, value] of Object.entries(model)) {
|
||||||
|
if (/^\[.+\]$/.test(key)) {
|
||||||
|
model[key] = setModelArray(data, key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (/^\{.+\}$/.test(key)) {
|
||||||
|
model[key] = setModelObject(data, key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
model[key] = data[key];
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
function rmString(str: string) {
|
||||||
|
const field = str.replaceAll(/\s/g, '');
|
||||||
|
return field.match(/^(\{|\[)(.+)(\}|\])$/)?.[1] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function setModelArray(data: Recordable, key: string) {
|
||||||
|
const result = [];
|
||||||
|
const field = rmString(key);
|
||||||
|
for (const key of field.split(',')) {
|
||||||
|
result.push(data[key]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setModelObject(data: Recordable, key: string) {
|
||||||
|
const result: Recordable = {};
|
||||||
|
const field = rmString(key);
|
||||||
|
for (const key of field.split(',')) {
|
||||||
|
result[key] = data[key];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getModelArray(key: string, value: any, data: Recordable) {
|
||||||
|
let field = rmString(key);
|
||||||
|
|
||||||
|
if (!field) {
|
||||||
|
data[key] = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
field.split(',').forEach((key, index) => {
|
||||||
|
data[key] = value?.[index];
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getModelObject(key: string, value: any, data: Recordable) {
|
||||||
|
const field = rmString(key);
|
||||||
|
|
||||||
|
if (!field) {
|
||||||
|
data[key] = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key of field.split(',')) {
|
||||||
|
data[key] = value?.[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
@ -5,11 +5,12 @@ import {
|
||||||
AnFormModalInstance,
|
AnFormModalInstance,
|
||||||
AnFormModalProps,
|
AnFormModalProps,
|
||||||
AnFormProps,
|
AnFormProps,
|
||||||
|
getModel,
|
||||||
} from '@/components/AnForm';
|
} from '@/components/AnForm';
|
||||||
import AnEmpty from '@/components/AnEmpty/AnEmpty.vue';
|
import AnEmpty from '@/components/AnEmpty/AnEmpty.vue';
|
||||||
import { Button, PaginationProps, Table, TableColumnData, TableData, TableInstance } from '@arco-design/web-vue';
|
import { Button, PaginationProps, Table, TableColumnData, TableData, TableInstance } from '@arco-design/web-vue';
|
||||||
import { isArray, isFunction, merge } from 'lodash-es';
|
import { isArray, isFunction, merge } from 'lodash-es';
|
||||||
import { InjectionKey, PropType, Ref, defineComponent, ref } from 'vue';
|
import { InjectionKey, PropType, Ref, VNodeChild, defineComponent, ref } from 'vue';
|
||||||
import { PluginContainer } from '../hooks/useTablePlugin';
|
import { PluginContainer } from '../hooks/useTablePlugin';
|
||||||
|
|
||||||
type DataFn = (filter: { page: number; size: number; [key: string]: any }) => any | Promise<any>;
|
type DataFn = (filter: { page: number; size: number; [key: string]: any }) => any | Promise<any>;
|
||||||
|
|
@ -21,6 +22,8 @@ export type ArcoTableProps = Omit<
|
||||||
|
|
||||||
export const AnTableContextKey = Symbol('AnTableContextKey') as InjectionKey<AnTableContext>;
|
export const AnTableContextKey = Symbol('AnTableContextKey') as InjectionKey<AnTableContext>;
|
||||||
|
|
||||||
|
export type TableColumnRender = (data: { record: TableData; column: TableColumnData; rowIndex: number }) => VNodeChild;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表格组件
|
* 表格组件
|
||||||
*/
|
*/
|
||||||
|
|
@ -119,7 +122,7 @@ export const AnTable = defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
const paging = getPaging();
|
const paging = getPaging();
|
||||||
const search = searchRef.value?.getModel() ?? {};
|
const search = getModel(props.search?.model ?? {});
|
||||||
|
|
||||||
if (isArray(props.source)) {
|
if (isArray(props.source)) {
|
||||||
// todo
|
// todo
|
||||||
|
|
@ -129,14 +132,20 @@ export const AnTable = defineComponent({
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
let params = { ...search, ...paging };
|
let params = { ...search, ...paging };
|
||||||
params = props.pluginer?.callLoadHook(params) ?? params;
|
let resData = (await props.pluginer?.callLoadHook(props.source, params)) || (await props.source(params));
|
||||||
let resData = await props.source(params);
|
let data: any[] = [];
|
||||||
resData = props.pluginer?.callLoadedHook(resData) ?? params;
|
let total = 0;
|
||||||
const { data = [], total = 0 } = resData?.data || {};
|
if (isArray(resData)) {
|
||||||
|
data = resData;
|
||||||
|
total = resData.length;
|
||||||
|
} else {
|
||||||
|
data = resData.data.data;
|
||||||
|
total = resData.data.total;
|
||||||
|
}
|
||||||
renderData.value = data;
|
renderData.value = data;
|
||||||
setPaging({ total });
|
setPaging({ total });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// todo
|
console.log('AnTable load fail: ', e);
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { defaultsDeep, isArray, merge } from 'lodash-es';
|
import { defaultsDeep, isArray, merge } from 'lodash-es';
|
||||||
import { AnFormProps, FormUseOptions, AnFormItemProps, FormItem, useItems } from '@/components/AnForm';
|
import { AnFormProps, FormUseOptions, AnFormItemProps, FormItem, useFormItems } from '@/components/AnForm';
|
||||||
|
|
||||||
export type ExtendFormItem = Partial<
|
export type ExtendFormItem = Partial<
|
||||||
FormItem & {
|
FormItem & {
|
||||||
|
|
@ -14,9 +14,10 @@ export type ExtendFormItem = Partial<
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type SearchFormItem = ExtendFormItem & {
|
export type SearchFormItem = ExtendFormItem & {
|
||||||
/**
|
/**
|
||||||
* 是否点击图标后进行搜索
|
* 是否点击图标后进行搜索
|
||||||
|
* @description 仅 setter: 'search' 类型可用
|
||||||
* @default
|
* @default
|
||||||
* ```ts
|
* ```ts
|
||||||
* false
|
* false
|
||||||
|
|
@ -33,7 +34,7 @@ type SearchFormItem = ExtendFormItem & {
|
||||||
enterable?: boolean;
|
enterable?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SearchFormObject = Omit<FormUseOptions, 'items' | 'submit'> & {
|
export type SearchForm = Omit<FormUseOptions, 'items' | 'submit'> & {
|
||||||
/**
|
/**
|
||||||
* 搜索表单项
|
* 搜索表单项
|
||||||
* @example
|
* @example
|
||||||
|
|
@ -54,9 +55,10 @@ export type SearchFormObject = Omit<FormUseOptions, 'items' | 'submit'> & {
|
||||||
hideSearch?: boolean;
|
hideSearch?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SearchForm = SearchFormObject | SearchFormItem[];
|
export function useSearchForm(
|
||||||
|
search?: SearchForm | SearchFormItem[],
|
||||||
export function useSearchForm(search?: SearchForm, extendItems: AnFormItemProps[] = []): AnFormProps | undefined {
|
extendItems: AnFormItemProps[] = []
|
||||||
|
): AnFormProps | undefined {
|
||||||
if (!search) {
|
if (!search) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +97,7 @@ export function useSearchForm(search?: SearchForm, extendItems: AnFormItemProps[
|
||||||
item = merge({}, extendItem, itemRest);
|
item = merge({}, extendItem, itemRest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (searchable) {
|
if (searchable && item.setter === 'search') {
|
||||||
(item as any).setterProps.onSearch = () => null;
|
(item as any).setterProps.onSearch = () => null;
|
||||||
}
|
}
|
||||||
if (enterable) {
|
if (enterable) {
|
||||||
|
|
@ -107,7 +109,7 @@ export function useSearchForm(search?: SearchForm, extendItems: AnFormItemProps[
|
||||||
items.push(item);
|
items.push(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
props.items = useItems(items, props.model);
|
props.items = useFormItems(items, props.model);
|
||||||
|
|
||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useFormModalProps } from '@/components/AnForm';
|
import { useFormModalProps } from '@/components/AnForm';
|
||||||
import { AnTable, AnTableInstance, AnTableProps } from '../components/Table';
|
import { AnTable, AnTableInstance, AnTableProps } from '../components/Table';
|
||||||
import { ModifyForm, useModifyForm } from './useModiyForm';
|
import { ModifyForm, useModifyForm } from './useModiyForm';
|
||||||
import { SearchForm, useSearchForm } from './useSearchForm';
|
import { SearchForm, SearchFormItem, useSearchForm } from './useSearchForm';
|
||||||
import { TableColumn, useTableColumns } from './useTableColumn';
|
import { TableColumn, useTableColumns } from './useTableColumn';
|
||||||
import { AnTablePlugin, PluginContainer } from './useTablePlugin';
|
import { AnTablePlugin, PluginContainer } from './useTablePlugin';
|
||||||
import { UseCreateFormOptions } from './useCreateForm';
|
import { UseCreateFormOptions } from './useCreateForm';
|
||||||
|
|
@ -46,7 +46,7 @@ export interface TableUseOptions extends Pick<AnTableProps, 'source' | 'tablePro
|
||||||
* }]
|
* }]
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
search?: SearchForm;
|
search?: SearchForm | SearchFormItem[];
|
||||||
/**
|
/**
|
||||||
* 新建弹窗
|
* 新建弹窗
|
||||||
* @example
|
* @example
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ interface TableColumnButton {
|
||||||
* @see ALink
|
* @see ALink
|
||||||
*/
|
*/
|
||||||
buttonProps?: Recordable;
|
buttonProps?: Recordable;
|
||||||
|
icon?: string;
|
||||||
/**
|
/**
|
||||||
* 是否可见
|
* 是否可见
|
||||||
* @example
|
* @example
|
||||||
|
|
@ -155,7 +156,10 @@ function useTableButtonColumn(column: TableButtonColumn & TableColumnData) {
|
||||||
<>
|
<>
|
||||||
{index !== 0 && <Divider direction="vertical" margin={2} />}
|
{index !== 0 && <Divider direction="vertical" margin={2} />}
|
||||||
<Link {...item.buttonProps} disabled={item.disable?.(props)} onClick={() => item.onClick?.(props)}>
|
<Link {...item.buttonProps} disabled={item.disable?.(props)} onClick={() => item.onClick?.(props)}>
|
||||||
{item.text}
|
{{
|
||||||
|
default: () => item.text,
|
||||||
|
// icon: () => item.icon ? <i class={item.icon}></i> : null
|
||||||
|
}}
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -28,17 +28,25 @@ export interface AnTablePlugin {
|
||||||
provide?: Recordable;
|
provide?: Recordable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件钩子
|
* 在表格组件的 `setup` 函数中调用
|
||||||
* @description 对应表格组件的 `setup` 钩子
|
|
||||||
*/
|
*/
|
||||||
onSetup?: (context: AnTableContext) => void;
|
onSetup?: (context: AnTableContext) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 钩子
|
* 钩子
|
||||||
* @description 在处理前进行参数处理
|
|
||||||
*/
|
*/
|
||||||
options?: (options: TableUseOptions) => TableUseOptions | null | undefined | void;
|
options?: (options: TableUseOptions) => TableUseOptions | null | undefined | void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析参数之前调用
|
||||||
|
*/
|
||||||
|
parse?: (options: TableUseOptions) => TableUseOptions | null | undefined | void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析参数之后调用
|
||||||
|
*/
|
||||||
|
parsed?: (options: any) => any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表格列
|
* 表格列
|
||||||
*/
|
*/
|
||||||
|
|
@ -62,15 +70,31 @@ export interface AnTablePlugin {
|
||||||
*/
|
*/
|
||||||
action?: () => (props: any) => any | Component;
|
action?: () => (props: any) => any | Component;
|
||||||
|
|
||||||
/**
|
onSearch?: (search: Recordable) => any[] | { data: any[]; total: number };
|
||||||
* 搜索前处理
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
onBeforeSearch?: (args: { page: number; size: number; [key: string]: any }) => Recordable | null | undefined | void;
|
|
||||||
onLoad?: (search: Recordable) => void;
|
onLoad?: (search: Recordable) => void;
|
||||||
onLoaded?: (res: any) => void;
|
onLoaded?: (res: any) => void;
|
||||||
|
onLoadOk?: (res: any) => void;
|
||||||
|
onLoadFail?: (e: any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const callHookWithData = async (name: string, plugins: AnTablePlugin[], data?: any) => {
|
||||||
|
for (const plugin of plugins) {
|
||||||
|
data = (await (plugin as any)[name]?.(data)) ?? data;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const callHookFirst = async (name: string, plugins: AnTablePlugin[], ...args: any[]) => {
|
||||||
|
for (const plugin of plugins) {
|
||||||
|
const data = await (plugin as any)[name]?.(...args);
|
||||||
|
if (data) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
export class PluginContainer {
|
export class PluginContainer {
|
||||||
actions: any[] = [];
|
actions: any[] = [];
|
||||||
widgets: any[] = [];
|
widgets: any[] = [];
|
||||||
|
|
@ -116,24 +140,19 @@ export class PluginContainer {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
callBeforeSearchHook(options: any) {
|
callLoadHook(data: any[] | ((...args: any[]) => Promise<any> | any), params: Recordable) {
|
||||||
for (const plugin of this.plugins) {
|
return callHookFirst('onLoad', this.plugins, data, params);
|
||||||
options = plugin.onBeforeSearch?.(options) ?? options;
|
|
||||||
}
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
callLoadHook(search: Recordable) {
|
|
||||||
for (const plugin of this.plugins) {
|
|
||||||
search = plugin.onLoad?.(search) ?? search;
|
|
||||||
}
|
|
||||||
return search as any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callLoadedHook(res: any) {
|
callLoadedHook(res: any) {
|
||||||
for (const plugin of this.plugins) {
|
return callHookWithData('onLoaded', this.plugins, res);
|
||||||
res = plugin.onLoaded?.(res) ?? res;
|
|
||||||
}
|
}
|
||||||
return res;
|
|
||||||
|
callLoadOkHook(res: any) {
|
||||||
|
return callHookWithData('onLoadOk', this.plugins, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
callLoadFailHook(res: any) {
|
||||||
|
return callHookWithData('onLoadFail', this.plugins, res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ export function useRowModify(): AnTablePlugin {
|
||||||
}
|
}
|
||||||
const onClick = btn.onClick;
|
const onClick = btn.onClick;
|
||||||
btn.onClick = async props => {
|
btn.onClick = async props => {
|
||||||
const { modifyRef } = ctx ?? {};
|
const data = (await onClick?.(props)) ?? props.record;
|
||||||
modifyRef?.value?.open(props.record);
|
ctx.modifyRef.value?.open(data);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import { Blocker } from "../core";
|
import { InjectionKey } from 'vue';
|
||||||
|
import { Block, Blocker, Container } from '../core';
|
||||||
|
import { useTextBlock } from './text';
|
||||||
|
|
||||||
const blockers: Record<string, Blocker> = import.meta.glob(["./*/index.ts", "!./font/*"], {
|
const blockers: Record<string, Blocker> = import.meta.glob(['./*/index.ts', '!./font/*'], {
|
||||||
eager: true,
|
eager: true,
|
||||||
import: "default",
|
import: 'default',
|
||||||
});
|
});
|
||||||
const BlockerMap: Record<string, Blocker> = {};
|
const BlockerMap: Record<string, Blocker> = {};
|
||||||
|
|
||||||
|
|
@ -23,3 +25,47 @@ const getIcon = (type: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export { BlockerMap, getBlockerRender, getIcon, getTypeName };
|
export { BlockerMap, getBlockerRender, getIcon, getTypeName };
|
||||||
|
|
||||||
|
export const BlockerManagerKey = Symbol('k') as InjectionKey<ReturnType<typeof useBlockerManage>>
|
||||||
|
|
||||||
|
export function useBlockerManage() {
|
||||||
|
const blockers: Blocker[] = [useTextBlock()];
|
||||||
|
const leftPanels: any[] = [];
|
||||||
|
|
||||||
|
for (const blocker of blockers) {
|
||||||
|
const panel = blocker.addLeftTab?.();
|
||||||
|
if (panel) {
|
||||||
|
leftPanels.push(leftPanels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const callInitHook = (container: Container) => {
|
||||||
|
for (const blocker of blockers) {
|
||||||
|
container = blocker.onLoadContainer?.(container) || container;
|
||||||
|
}
|
||||||
|
return container;
|
||||||
|
};
|
||||||
|
|
||||||
|
const callLoadHook = (data: any): Blocker => {
|
||||||
|
for (const blocker of blockers) {
|
||||||
|
data = blocker.onLoadBlock?.(data) || data;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const callSaveHook = (block: Block) => {
|
||||||
|
let data = block;
|
||||||
|
for (const blocker of blockers) {
|
||||||
|
data = blocker.onSaveBlock?.(data) || data;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
blockers,
|
||||||
|
leftPanels,
|
||||||
|
callInitHook,
|
||||||
|
callLoadHook,
|
||||||
|
callSaveHook,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,28 @@
|
||||||
import { defineBlocker } from "../../core";
|
import { Block, Blocker, defineBlocker } from '../../core';
|
||||||
import { font } from "../font";
|
import { font } from '../font';
|
||||||
import { Text } from "./interface";
|
import { Text } from './interface';
|
||||||
import Option from "./option.vue";
|
import Option from './option.vue';
|
||||||
import Render from "./render.vue";
|
import Render from './render.vue';
|
||||||
|
|
||||||
export default defineBlocker<Text>({
|
export default defineBlocker<Text>({
|
||||||
type: "text",
|
type: 'text',
|
||||||
icon: "icon-park-outline-text",
|
icon: 'icon-park-outline-text',
|
||||||
title: "文本组件",
|
title: '文本组件',
|
||||||
description: "文字",
|
description: '文字',
|
||||||
render: Render,
|
render: Render,
|
||||||
option: Option,
|
option: Option,
|
||||||
initial: {
|
initial: {
|
||||||
id: "",
|
id: '',
|
||||||
type: "text",
|
type: 'text',
|
||||||
title: "",
|
title: '',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
w: 300,
|
w: 300,
|
||||||
h: 100,
|
h: 100,
|
||||||
xFixed: false,
|
xFixed: false,
|
||||||
yFixed: false,
|
yFixed: false,
|
||||||
bgImage: "",
|
bgImage: '',
|
||||||
bgColor: "",
|
bgColor: '',
|
||||||
meta: {},
|
meta: {},
|
||||||
actived: false,
|
actived: false,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
|
|
@ -30,12 +30,58 @@ export default defineBlocker<Text>({
|
||||||
params: {
|
params: {
|
||||||
marquee: false,
|
marquee: false,
|
||||||
speed: 100,
|
speed: 100,
|
||||||
direction: "left",
|
direction: 'left',
|
||||||
fontCh: {
|
fontCh: {
|
||||||
...font,
|
...font,
|
||||||
content:
|
content:
|
||||||
"温馨提示:乘客您好,进站检票时,持票卡的乘客请在右侧闸机上方感应区内验票,扫码过闸的乘客请将乘车码对准闸机扫码口,扇门打开后依次进闸。乘车过程中请妥善保管好车票,以免丢失。",
|
'温馨提示:乘客您好,进站检票时,持票卡的乘客请在右侧闸机上方感应区内验票,扫码过闸的乘客请将乘车码对准闸机扫码口,扇门打开后依次进闸。乘车过程中请妥善保管好车票,以免丢失。',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export function useTextBlock(): Blocker<Text> {
|
||||||
|
const initialData: Text = {
|
||||||
|
id: '',
|
||||||
|
type: 'text',
|
||||||
|
title: '',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
w: 300,
|
||||||
|
h: 100,
|
||||||
|
xFixed: false,
|
||||||
|
yFixed: false,
|
||||||
|
bgImage: '',
|
||||||
|
bgColor: '',
|
||||||
|
meta: {},
|
||||||
|
actived: false,
|
||||||
|
resizable: true,
|
||||||
|
draggable: true,
|
||||||
|
params: {
|
||||||
|
marquee: false,
|
||||||
|
speed: 100,
|
||||||
|
direction: 'left',
|
||||||
|
fontCh: {
|
||||||
|
...font,
|
||||||
|
content:
|
||||||
|
'温馨提示:乘客您好,进站检票时,持票卡的乘客请在右侧闸机上方感应区内验票,扫码过闸的乘客请将乘车码对准闸机扫码口,扇门打开后依次进闸。乘车过程中请妥善保管好车票,以免丢失。',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
type: 'text',
|
||||||
|
icon: 'icon-park-outline-text',
|
||||||
|
title: '文本组件',
|
||||||
|
description: '文字',
|
||||||
|
render: Render,
|
||||||
|
option: Option,
|
||||||
|
initial: initialData,
|
||||||
|
addLeftTab() {
|
||||||
|
return {
|
||||||
|
title: '文本测试',
|
||||||
|
icon: 'icon-park-outline-user',
|
||||||
|
component: () => h('div', null, 'TODO')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
import { Block } from "../../core";
|
import { Block } from "../../core";
|
||||||
import { Font } from "../font";
|
import { Font } from "../font";
|
||||||
|
|
||||||
|
export interface OutputText {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface TextPrams {
|
export interface TextPrams {
|
||||||
/**
|
/**
|
||||||
* 是否滚动
|
* 是否滚动
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,12 @@
|
||||||
</template>
|
</template>
|
||||||
预览
|
预览
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button @click="emit('config')">
|
<!-- <a-button @click="emit('config')">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i class="icon-park-outline-config"></i>
|
<i class="icon-park-outline-config"></i>
|
||||||
</template>
|
</template>
|
||||||
设置
|
设置
|
||||||
</a-button>
|
</a-button> -->
|
||||||
<a-button type="primary" :loading="saving" @click="emit('save')">
|
<a-button type="primary" :loading="saving" @click="emit('save')">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i class="icon-park-outline-save"></i>
|
<i class="icon-park-outline-save"></i>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="h-full w-[248px] overflow-hidden" :style="`display: ${collapsed ? 'none' : 'block'}`">
|
<div class="h-full w-[248px] overflow-hidden" :style="`display: ${collapsed ? 'none' : 'block'}`">
|
||||||
<div v-if="model" class="p-3 pr-0 grid grid-rows-[auto_1fr]">
|
<div v-if="model" class="p-3 pr-0 grid grid-rows-[auto_1fr]">
|
||||||
<a-tag class="text-sm! mb-2 mr-3" size="large" color="blue" :bordered="true">
|
<a-tag class="text-sm! mb-2 mr-3" size="large" color="blue" :bordered="false">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i class="icon-park-outline-components"></i>
|
<i :class="BlockerMap[model.type].icon"></i>
|
||||||
</template>
|
</template>
|
||||||
组件属性({{ BlockerMap[model.type].title }})
|
{{ BlockerMap[model.type].title }}属性
|
||||||
</a-tag>
|
</a-tag>
|
||||||
<a-scrollbar outer-class="h-full overflow-hidden" class="h-full overflow-auto">
|
<a-scrollbar outer-class="h-full overflow-hidden" class="h-full overflow-auto">
|
||||||
<a-form :model="{}" layout="vertical" class="pr-3">
|
<a-form :model="{}" layout="vertical" class="pr-3">
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="p-3">
|
<div class="p-3">
|
||||||
<a-tag class="text-sm! mb-2 w-full" size="large" color="blue" :bordered="true">
|
<a-tag class="text-sm! mb-2 w-full" size="large" color="blue" :bordered="false">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i class="icon-park-outline-config" ></i>
|
<i class="icon-park-outline-config" ></i>
|
||||||
</template>
|
</template>
|
||||||
画布设置
|
画布属性
|
||||||
</a-tag>
|
</a-tag>
|
||||||
<a-form :model="{}" layout="vertical">
|
<a-form :model="{}" layout="vertical">
|
||||||
<a-form-item label="标题">
|
<a-form-item label="标题">
|
||||||
|
|
@ -31,6 +31,22 @@
|
||||||
<a-form-item label="背景颜色">
|
<a-form-item label="背景颜色">
|
||||||
<input-color v-model="model.bgColor"></input-color>
|
<input-color v-model="model.bgColor"></input-color>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="语言列表">
|
||||||
|
<a-checkbox-group v-model="model.langList" direction="vertical" class="bg-gray-100 w-full px-1.5 py-1 rounded">
|
||||||
|
<a-checkbox value="ch">中文<span class="text-gray-400">(cn)</span></a-checkbox>
|
||||||
|
<a-checkbox value="en">英语<span class="text-gray-400">(en)</span></a-checkbox>
|
||||||
|
<a-checkbox value="ru">俄语<span class="text-gray-400">(ru)</span></a-checkbox>
|
||||||
|
</a-checkbox-group>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="语言切换">
|
||||||
|
<a-input-number v-model="model.langSwitch" :min="0">
|
||||||
|
<template #append>
|
||||||
|
秒(s)
|
||||||
|
</template>
|
||||||
|
</a-input-number>
|
||||||
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { Component } from "vue";
|
import { Component } from 'vue';
|
||||||
import { Block } from "./block";
|
import { Block } from './block';
|
||||||
|
import { Container } from './container';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件配置
|
* 组件配置
|
||||||
|
|
@ -38,11 +39,63 @@ export interface Blocker<T = any> {
|
||||||
*/
|
*/
|
||||||
viewer?: Component;
|
viewer?: Component;
|
||||||
/**
|
/**
|
||||||
* 初始化钩子
|
* 将实际格式转换为内部格式
|
||||||
* @param block 组件
|
|
||||||
* @returns
|
|
||||||
*/
|
*/
|
||||||
onInit?: (block: Block) => void;
|
onLoadContainer?: (container: Container) => void;
|
||||||
|
/**
|
||||||
|
* 将内部格式转换为实际格式
|
||||||
|
*/
|
||||||
|
onSaveContainer?: (container: Container) => any;
|
||||||
|
/**
|
||||||
|
* 将实际格式转换为内部格式
|
||||||
|
*/
|
||||||
|
onLoadBlock?: (data: any) => Block;
|
||||||
|
/**
|
||||||
|
* 将内部格式转换为实际格式
|
||||||
|
*/
|
||||||
|
onSaveBlock?: (block: Block) => any;
|
||||||
|
/**
|
||||||
|
* 在左侧添加选项卡
|
||||||
|
*/
|
||||||
|
addLeftTab?: () => {
|
||||||
|
title: string;
|
||||||
|
icon: string | Component;
|
||||||
|
component: Component;
|
||||||
|
};
|
||||||
|
addBlock?: () => {
|
||||||
|
/**
|
||||||
|
* 唯一标识符
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* 显示标题
|
||||||
|
*/
|
||||||
|
title: string;
|
||||||
|
/**
|
||||||
|
* 显示图标
|
||||||
|
*/
|
||||||
|
icon: string;
|
||||||
|
/**
|
||||||
|
* 显示描述
|
||||||
|
*/
|
||||||
|
description: string;
|
||||||
|
/**
|
||||||
|
* 默认初始值
|
||||||
|
*/
|
||||||
|
initial: Block;
|
||||||
|
/**
|
||||||
|
* 预览时的渲染组件
|
||||||
|
*/
|
||||||
|
render: Component;
|
||||||
|
/**
|
||||||
|
* 编辑参数时的渲染组件
|
||||||
|
*/
|
||||||
|
optionRender: Component<{ modelValue: Block }>;
|
||||||
|
/**
|
||||||
|
* 编辑时的渲染组件
|
||||||
|
*/
|
||||||
|
modifyRender: Component;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,8 @@ export interface Container {
|
||||||
* 背景颜色
|
* 背景颜色
|
||||||
*/
|
*/
|
||||||
bgColor: string;
|
bgColor: string;
|
||||||
|
langList: string[];
|
||||||
|
langSwitch: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -58,4 +60,6 @@ export const defaultContainer: Container = {
|
||||||
height: 1080,
|
height: 1080,
|
||||||
bgImage: "",
|
bgImage: "",
|
||||||
bgColor: "#ffffff",
|
bgColor: "#ffffff",
|
||||||
|
langList: ['ch', 'en'],
|
||||||
|
langSwitch: 0
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,16 @@
|
||||||
</a-doption>
|
</a-doption>
|
||||||
<a-doption @click="router.push('/my')">
|
<a-doption @click="router.push('/my')">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i class="icon-park-outline-config"></i>
|
<i class="icon-park-outline-user"></i>
|
||||||
</template>
|
</template>
|
||||||
个人设置
|
个人设置
|
||||||
</a-doption>
|
</a-doption>
|
||||||
|
<a-doption @click="router.push('/my')">
|
||||||
|
<template #icon>
|
||||||
|
<i class="icon-park-outline-config"></i>
|
||||||
|
</template>
|
||||||
|
系统设置
|
||||||
|
</a-doption>
|
||||||
<a-divider :margin="4"></a-divider>
|
<a-divider :margin="4"></a-divider>
|
||||||
<a-doption @click="logout">
|
<a-doption @click="logout">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@
|
||||||
class="h-13 overflow-hidden flex justify-between items-center gap-4 px-2 pr-4 border-b border-slate-200 bg-white dark:bg-slate-800 dark:border-slate-700"
|
class="h-13 overflow-hidden flex justify-between items-center gap-4 px-2 pr-4 border-b border-slate-200 bg-white dark:bg-slate-800 dark:border-slate-700"
|
||||||
>
|
>
|
||||||
<div class="h-13 flex items-center">
|
<div class="h-13 flex items-center">
|
||||||
<router-link to="/" class="px-2 py-1 rounded flex items-center gap-2 text-slate-700">
|
<router-link to="/" class="px-2 flex items-center gap-2 text-slate-700">
|
||||||
<img src="/favicon.ico" alt="" width="22" height="22" class="" />
|
<img src="/favicon.ico" alt="" width="24" height="24" class="" />
|
||||||
<h1 class="relative text-lg leading-[20px] dark:text-white m-0 p-0 font-semibold">
|
<h1 class="relative text-[22px] leading-[22px] dark:text-white m-0 p-0 font-normal">
|
||||||
{{ appStore.title }}
|
{{ appStore.title }}
|
||||||
<span class="absolute -right-10 -top-1 font-normal text-xs text-gray-400"> v0.0.1 </span>
|
<span class="absolute -right-10 -top-1 font-normal text-xs text-gray-400"> v0.0.1 </span>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
@ -16,6 +16,15 @@
|
||||||
<div>
|
<div>
|
||||||
<a-input-search placeholder="搜索菜单/页面" :allow-clear="true"></a-input-search>
|
<a-input-search placeholder="搜索菜单/页面" :allow-clear="true"></a-input-search>
|
||||||
</div>
|
</div>
|
||||||
|
<a-tooltip content="上传文件">
|
||||||
|
<a-button @click="() => null" class="!bg-transparent !hover:bg-gray-100">
|
||||||
|
<template #icon>
|
||||||
|
<a-badge :count="1" :dot="true">
|
||||||
|
<i class="text-base icon-park-outline-upload-one"></i>
|
||||||
|
</a-badge>
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
<a-tooltip v-for="btn in buttons" :key="btn.icon" :content="btn.tooltip">
|
<a-tooltip v-for="btn in buttons" :key="btn.icon" :content="btn.tooltip">
|
||||||
<a-button @click="btn.onClick" class="!bg-transparent !hover:bg-gray-100">
|
<a-button @click="btn.onClick" class="!bg-transparent !hover:bg-gray-100">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,9 @@
|
||||||
class="login-left relative hidden md:block w-full h-full overflow-hidden bg-[rgb(var(--primary-6))] px-4"
|
class="login-left relative hidden md:block w-full h-full overflow-hidden bg-[rgb(var(--primary-6))] px-4"
|
||||||
></div>
|
></div>
|
||||||
<div class="relative p-20 px-8 md:px-14 bg-white shadow-sm">
|
<div class="relative p-20 px-8 md:px-14 bg-white shadow-sm">
|
||||||
<div class="text-2xl">欢迎登陆</div>
|
<div class="text-xl text-brand-500 font-semibold">用户登陆</div>
|
||||||
<div class="text-base text-gray-500 mt-2">{{ meridiem }}好,欢迎登陆{{ appStore.title }}!</div>
|
<div class="text-gray-500 mt-2.5">{{ meridiem }}好,欢迎访问 {{ appStore.title }} 系统!</div>
|
||||||
<a-form ref="formRef" :model="model" :rules="formRules" layout="vertical" class="mt-8">
|
<a-form ref="formRef" :model="model" :rules="formRules" layout="vertical" class="mt-6">
|
||||||
<a-form-item field="username" label="账号" :disabled="loading" hide-asterisk>
|
<a-form-item field="username" label="账号" :disabled="loading" hide-asterisk>
|
||||||
<a-input v-model="model.username" placeholder="请输入账号/手机号/邮箱" allow-clear>
|
<a-input v-model="model.username" placeholder="请输入账号/手机号/邮箱" allow-clear>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,19 @@
|
||||||
</a-button>
|
</a-button>
|
||||||
<CategoryModal></CategoryModal>
|
<CategoryModal></CategoryModal>
|
||||||
</div>
|
</div>
|
||||||
<a-scrollbar outer-class="h-full overflow-hidden" class="h-full overflow-auto">
|
|
||||||
<a-spin :loading="loading" class="w-full h-full">
|
<a-spin :loading="loading" class="w-full h-full">
|
||||||
|
<a-scrollbar outer-class="h-full overflow-hidden" class="h-full overflow-auto">
|
||||||
<ul v-if="list.length" class="pl-0 mt-0">
|
<ul v-if="list.length" class="pl-0 mt-0">
|
||||||
|
<li
|
||||||
|
:class="{ active: !current?.id }"
|
||||||
|
class="group flex items-center justify-between gap-1 h-8 rounded mb-2 pl-3 hover:bg-gray-100 cursor-pointer"
|
||||||
|
>
|
||||||
|
<div class="flex-1 h-full flex items-center gap-2 overflow-hidden" @click="emit('change', {})">
|
||||||
|
<i class="icon-park-outline-folder-close align-[-2px]"></i>
|
||||||
|
<span class="flex-1 truncate">全部</span>
|
||||||
|
</div>
|
||||||
|
<div class=""></div>
|
||||||
|
</li>
|
||||||
<li
|
<li
|
||||||
v-for="item in list"
|
v-for="item in list"
|
||||||
:key="item.code"
|
:key="item.code"
|
||||||
|
|
@ -48,8 +58,8 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<an-empty v-else></an-empty>
|
<an-empty v-else></an-empty>
|
||||||
</a-spin>
|
|
||||||
</a-scrollbar>
|
</a-scrollbar>
|
||||||
|
</a-spin>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -75,7 +85,6 @@ const updateFileCategories = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const res = await api.fileCategory.getFileCategorys({ size: 0 });
|
const res = await api.fileCategory.getFileCategorys({ size: 0 });
|
||||||
list.value = res.data.data ?? [];
|
list.value = res.data.data ?? [];
|
||||||
list.value.unshift({ id: undefined, name: '全部' } as any);
|
|
||||||
list.value.length && emit('change', list.value[0]);
|
list.value.length && emit('change', list.value[0]);
|
||||||
} catch {
|
} catch {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
<template>
|
||||||
|
<template v-if="fileType === 'image'">
|
||||||
|
<a-image-preview v-model:visible="show" :src="url"></a-image-preview>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="fileType === 'video'">
|
||||||
|
<a-modal v-model:visible="show" title="预览" title-align="start" :footer="false">
|
||||||
|
<video :src="url" controls></video>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="fileType === 'text'">
|
||||||
|
<a-modal v-model:visible="show"></a-modal>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="fileType === 'audio'">
|
||||||
|
<a-modal v-model:visible="show" :footer="false"></a-modal>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<a-modal v-model:visible="show" title="预览" title-align="start" :closable="false" :width="420">
|
||||||
|
抱歉,此文件类型暂不支持预览!
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PropType } from 'vue';
|
||||||
|
import { useVModel } from '@vueuse/core';
|
||||||
|
|
||||||
|
type FileType = 'text' | 'audio' | 'image' | 'video' | 'unknown';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String as PropType<FileType>,
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const fileType = computed<FileType>(() => {
|
||||||
|
if (props.type === 'text') {
|
||||||
|
return 'text';
|
||||||
|
}
|
||||||
|
if (props.type === 'image') {
|
||||||
|
return 'image';
|
||||||
|
}
|
||||||
|
if (props.type === 'video') {
|
||||||
|
return 'video';
|
||||||
|
}
|
||||||
|
if (props.type === 'audio') {
|
||||||
|
return 'audio';
|
||||||
|
}
|
||||||
|
return 'unknown';
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:visible']);
|
||||||
|
const show = useVModel(props, 'visible', emit);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
|
@ -38,7 +38,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul v-if="fileList.length" class="h-[424px] overflow-hidden p-0 m-0">
|
<div class="h-[424px] border-t border-b border-zinc-100 mt-3">
|
||||||
|
<ul v-if="fileList.length" class="overflow-hidden p-0 m-0">
|
||||||
<a-scrollbar outer-class="h-full overflow-hidden" class="h-full overflow-auto pr-[20px] divide-y">
|
<a-scrollbar outer-class="h-full overflow-hidden" class="h-full overflow-auto pr-[20px] divide-y">
|
||||||
<li v-for="item in fileList" :key="item.uid" class="flex items-center gap-4 py-3">
|
<li v-for="item in fileList" :key="item.uid" class="flex items-center gap-4 py-3">
|
||||||
<div class="text-4xl rounded pr-0.5 flex justify-center">
|
<div class="text-4xl rounded pr-0.5 flex justify-center">
|
||||||
|
|
@ -93,10 +94,11 @@
|
||||||
</li>
|
</li>
|
||||||
</a-scrollbar>
|
</a-scrollbar>
|
||||||
</ul>
|
</ul>
|
||||||
|
<div v-else class="h-full flex items-center justify-center">
|
||||||
<div v-else class="h-[424px] flex items-center justify-center">
|
|
||||||
<an-empty></an-empty>
|
<an-empty></an-empty>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="flex justify-between gap-2 items-center">
|
<div class="flex justify-between gap-2 items-center">
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
const typeIconMap: Record<string, string> = {
|
||||||
|
video: 'icon-park-outline-video-file',
|
||||||
|
audio: 'icon-park-outline-audio-file',
|
||||||
|
image: 'icon-park-outline-file-pdf',
|
||||||
|
text: 'icon-park-outline-file-txt',
|
||||||
|
application: 'icon-park-outline-file-code',
|
||||||
|
unknown: 'icon-park-outline-file-question',
|
||||||
|
};
|
||||||
|
|
||||||
|
function getIconnameByMimetype(mimetype: string) {
|
||||||
|
const [type, subtype] = mimetype.split('/');
|
||||||
|
return typeIconMap[type] || typeIconMap.unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MIME {
|
||||||
|
IMAGE = 'image',
|
||||||
|
VIDEO = 'video',
|
||||||
|
AUDIO = 'audio',
|
||||||
|
TEXT = 'text',
|
||||||
|
APPLICATION = 'application',
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIcon(mimetype: string) {
|
||||||
|
const [type, subtype] = mimetype.split('/');
|
||||||
|
if (type === MIME.IMAGE) {
|
||||||
|
return 'icon-file-iimage';
|
||||||
|
}
|
||||||
|
if (type === MIME.VIDEO) {
|
||||||
|
return 'icon-file-ivideo';
|
||||||
|
}
|
||||||
|
if (type === MIME.TEXT) {
|
||||||
|
return 'icon-file-itxt';
|
||||||
|
}
|
||||||
|
if (type === MIME.AUDIO) {
|
||||||
|
return 'icon-file-iaudio';
|
||||||
|
}
|
||||||
|
if (type === MIME.APPLICATION) {
|
||||||
|
if (subtype === 'zip') {
|
||||||
|
return 'icon-file-izip';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 'icon-file-iunknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
export { getIcon, getIconnameByMimetype };
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
<AnUpload @success="() => tableRef?.refresh()"></AnUpload>
|
<AnUpload @success="() => tableRef?.refresh()"></AnUpload>
|
||||||
</template>
|
</template>
|
||||||
</MaterialTable>
|
</MaterialTable>
|
||||||
<a-image-preview v-model:visible="visible" :src="image"></a-image-preview>
|
<AnPreview v-model:visible="viewer.visible" :type="viewer.type" :url="viewer.url"></AnPreview>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -23,19 +23,17 @@ import { Message } from '@arco-design/web-vue';
|
||||||
import numeral from 'numeral';
|
import numeral from 'numeral';
|
||||||
import AnGroup from './components/AnGroup.vue';
|
import AnGroup from './components/AnGroup.vue';
|
||||||
import AnUpload from './components/AnUpload.vue';
|
import AnUpload from './components/AnUpload.vue';
|
||||||
|
import AnPreview from './components/AnPreview.vue';
|
||||||
import { getIcon } from './components/util';
|
import { getIcon } from './components/util';
|
||||||
|
|
||||||
const visible = ref(false);
|
|
||||||
const current = ref<FileCategory>();
|
const current = ref<FileCategory>();
|
||||||
const image = ref('');
|
const viewer = reactive({ visible: false, url: undefined, type: undefined });
|
||||||
|
|
||||||
const preview = (record: any) => {
|
const preview = (record: any) => {
|
||||||
if (!record.mimetype.startsWith('image')) {
|
const [type] = record.mimetype.split('/');
|
||||||
window.open(record.path, '_blank');
|
viewer.url = record.path;
|
||||||
return;
|
viewer.type = type;
|
||||||
}
|
viewer.visible = true;
|
||||||
image.value = record.path;
|
|
||||||
visible.value = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCategoryChange = (category: FileCategory) => {
|
const onCategoryChange = (category: FileCategory) => {
|
||||||
|
|
@ -48,7 +46,7 @@ const onCategoryChange = (category: FileCategory) => {
|
||||||
|
|
||||||
const copyLink = (record: Recordable) => {
|
const copyLink = (record: Recordable) => {
|
||||||
window.navigator.clipboard.writeText(record.path);
|
window.navigator.clipboard.writeText(record.path);
|
||||||
Message.success(`提示:已复制 ${record.name} 的地址!`);
|
Message.success(`已复制 ${record.name} 的地址!`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
@ -63,10 +61,10 @@ const {
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
render: ({ record }) => {
|
render: ({ record }) => {
|
||||||
return (
|
return (
|
||||||
<div class="group flex items-center gap-2">
|
<div class="group flex items-center gap-4">
|
||||||
<div class="w-8 flex justify-center">
|
<div class="w-8 flex justify-center">
|
||||||
{record.mimetype.startsWith('image') ? (
|
{record.mimetype.startsWith('image') ? (
|
||||||
<a-avatar size={26} shape="square">
|
<a-avatar size={32} shape="square">
|
||||||
<img src={record.path}></img>
|
<img src={record.path}></img>
|
||||||
</a-avatar>
|
</a-avatar>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
const typeIconMap: Record<string, string> = {
|
|
||||||
video: "icon-park-outline-video-file",
|
|
||||||
audio: "icon-park-outline-audio-file",
|
|
||||||
image: "icon-park-outline-file-pdf",
|
|
||||||
text: "icon-park-outline-file-txt",
|
|
||||||
application: "icon-park-outline-file-code",
|
|
||||||
unknown: "icon-park-outline-file-question",
|
|
||||||
};
|
|
||||||
|
|
||||||
function getIconnameByMimetype(mimetype: string) {
|
|
||||||
const [type, subtype] = mimetype.split("/");
|
|
||||||
return typeIconMap[type] || typeIconMap.unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getIcon(mimetype: string) {
|
|
||||||
if (mimetype.startsWith("image")) {
|
|
||||||
return "icon-fmt-png";
|
|
||||||
}
|
|
||||||
if (mimetype.startsWith("video")) {
|
|
||||||
return "icon-fmt-video";
|
|
||||||
}
|
|
||||||
if (mimetype.startsWith("text")) {
|
|
||||||
return "icon-fmt-txt";
|
|
||||||
}
|
|
||||||
if (mimetype.startsWith("audio")) {
|
|
||||||
return "icon-fmt-mp";
|
|
||||||
}
|
|
||||||
return "icon-fmt-visio";
|
|
||||||
}
|
|
||||||
|
|
||||||
export { getIcon, getIconnameByMimetype };
|
|
||||||
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
<template><div></div></template>
|
<template>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<route lang="json">
|
<route lang="json">
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,140 +0,0 @@
|
||||||
<template>
|
|
||||||
<BreadPage>
|
|
||||||
<LoginLogTable>
|
|
||||||
<template #action>
|
|
||||||
<a-button type="primary" @click="visible = true">添加</a-button>
|
|
||||||
<ani-editor v-model:visible="visible"></ani-editor>
|
|
||||||
</template>
|
|
||||||
</LoginLogTable>
|
|
||||||
</BreadPage>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="tsx">
|
|
||||||
import { api } from '@/api';
|
|
||||||
import { useTable } from '@/components/AnTable';
|
|
||||||
import { Editor as aniEditor } from '@/components/editor';
|
|
||||||
import { TableColumnData } from '@arco-design/web-vue';
|
|
||||||
import dayjs from 'dayjs';
|
|
||||||
|
|
||||||
defineOptions({ name: 'SystemLoglPage' });
|
|
||||||
const visible = ref(false);
|
|
||||||
|
|
||||||
const useTwoRowsColumn = (tkey: string, bkey: string): TableColumnData['render'] => {
|
|
||||||
return ({ record }) => {
|
|
||||||
return (
|
|
||||||
<div class="flex flex-col overflow-hidden">
|
|
||||||
<span>{record[tkey] || '未知'}</span>
|
|
||||||
<span class="text-gray-400 text-xs truncate">{record[bkey]}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const { component: LoginLogTable } = useTable({
|
|
||||||
source: async model => {
|
|
||||||
return api.log.getLoginLogs(model);
|
|
||||||
},
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
title: '操作描述',
|
|
||||||
dataIndex: 'description',
|
|
||||||
render: ({ record }) => {
|
|
||||||
return (
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<span
|
|
||||||
class={
|
|
||||||
record.status === null || record.status
|
|
||||||
? 'text-base text-green-500 icon-park-outline-check-one mr-2'
|
|
||||||
: 'text-base text-red-500 icon-park-outline-close-one mr-2'
|
|
||||||
}
|
|
||||||
></span>
|
|
||||||
<div>
|
|
||||||
<div>{record.nickname}</div>
|
|
||||||
<div class="text-xs text-gray-400">{record.description}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '登陆地址',
|
|
||||||
dataIndex: 'ip',
|
|
||||||
width: 200,
|
|
||||||
render: useTwoRowsColumn('addr', 'ip'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作系统',
|
|
||||||
dataIndex: 'os',
|
|
||||||
width: 200,
|
|
||||||
render({ record }) {
|
|
||||||
const [os, version] = record.os.split(' ');
|
|
||||||
return (
|
|
||||||
<div class="flex flex-col overflow-hidden">
|
|
||||||
<span>{os || '未知'}</span>
|
|
||||||
<span class="text-gray-400 text-xs truncate">{version}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '浏览器',
|
|
||||||
dataIndex: 'browser',
|
|
||||||
width: 200,
|
|
||||||
render({ record }) {
|
|
||||||
const [browser, version] = record.browser.split(' ');
|
|
||||||
return (
|
|
||||||
<div class="flex flex-col overflow-hidden">
|
|
||||||
<span>{browser || '未知'}</span>
|
|
||||||
<span class="text-gray-400 text-xs truncate">v{version}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '登陆时间',
|
|
||||||
dataIndex: 'createAt',
|
|
||||||
width: 200,
|
|
||||||
render({ record }) {
|
|
||||||
return (
|
|
||||||
<div class="flex flex-col overflow-hidden">
|
|
||||||
<span>{dayjs(record.createdAt).fromNow()}</span>
|
|
||||||
<span class="text-gray-400 text-xs truncate">{dayjs(record.createdAt).format('YYYY-MM-DD HH:mm:ss')}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
search: {
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
field: '[startDate, endDate]',
|
|
||||||
label: '登陆账号',
|
|
||||||
setter: 'dateRange',
|
|
||||||
setterProps: {
|
|
||||||
placeholder: ['开始时间', '结束时间'],
|
|
||||||
showTime: true,
|
|
||||||
timePickerProps: { defaultValue: ['23:59:59', '00:00:00'] },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'nickname',
|
|
||||||
label: '登陆账号',
|
|
||||||
setter: 'input',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
|
||||||
<route lang="json">
|
|
||||||
{
|
|
||||||
"meta": {
|
|
||||||
"name": "SystemLoglPage",
|
|
||||||
"sort": 10303,
|
|
||||||
"title": "登陆日志",
|
|
||||||
"icon": "icon-park-outline-log"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
<template>
|
|
||||||
<BreadPage>
|
|
||||||
<OperationTable></OperationTable>
|
|
||||||
</BreadPage>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="tsx">
|
|
||||||
import { api } from '@/api';
|
|
||||||
import { useTable } from '@/components/AnTable';
|
|
||||||
import { dayjs } from '@/libs/dayjs';
|
|
||||||
import { Tag } from '@arco-design/web-vue';
|
|
||||||
|
|
||||||
defineOptions({ name: 'SystemLogoPage' });
|
|
||||||
|
|
||||||
const { component: OperationTable } = useTable({
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
title: '登陆账号',
|
|
||||||
dataIndex: 'nickname',
|
|
||||||
width: 140,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作描述',
|
|
||||||
dataIndex: 'description',
|
|
||||||
render: ({ record: { status, description } }) => {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<Tag color={status === null || status ? 'green' : 'red'} class="mr-2">
|
|
||||||
{status === null || status ? '成功' : '失败'}
|
|
||||||
</Tag>
|
|
||||||
{description}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '登陆地址',
|
|
||||||
dataIndex: 'ip',
|
|
||||||
width: 200,
|
|
||||||
render: ({ record }) => `${record.addr || '未知'}(${record.ip})`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作系统',
|
|
||||||
dataIndex: 'os',
|
|
||||||
width: 160,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '浏览器',
|
|
||||||
dataIndex: 'browser',
|
|
||||||
width: 160,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '登陆时间',
|
|
||||||
dataIndex: 'createdAt',
|
|
||||||
width: 120,
|
|
||||||
render: ({ record }) => dayjs(record.createdAt).fromNow(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
source: model => {
|
|
||||||
return api.log.getLoginLogs(model);
|
|
||||||
},
|
|
||||||
search: [
|
|
||||||
{
|
|
||||||
field: 'nickname',
|
|
||||||
label: '登陆账号',
|
|
||||||
setter: 'input',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
|
||||||
<route lang="json">
|
|
||||||
{
|
|
||||||
"meta": {
|
|
||||||
"name": "SystemLogoPage",
|
|
||||||
"sort": 10304,
|
|
||||||
"title": "操作日志",
|
|
||||||
"icon": "icon-park-outline-doc-detail"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</route>
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { api } from '@/api';
|
import { api } from '@/api';
|
||||||
import { useCreateColumn, useTable, useUpdateColumn } from '@/components/AnTable';
|
import { TableColumnRender, useCreateColumn, useTable, useUpdateColumn } from '@/components/AnTable';
|
||||||
import { useFormModal } from '@/components/AnForm';
|
import { useFormModal } from '@/components/AnForm';
|
||||||
|
|
||||||
defineOptions({ name: 'SystemUserPage' });
|
defineOptions({ name: 'SystemUserPage' });
|
||||||
|
|
@ -30,12 +30,7 @@ const { component: PasswordModal, open } = useFormModal({
|
||||||
submit: model => api.user.setUser(model.id, model as any),
|
submit: model => api.user.setUser(model.id, model as any),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { component: UserTable } = useTable({
|
const usernameRender: TableColumnRender = ({ record }) => (
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
title: '用户昵称',
|
|
||||||
dataIndex: 'username',
|
|
||||||
render: ({ record }) => (
|
|
||||||
<div class="flex items-center gap-4 w-full overflow-hidden">
|
<div class="flex items-center gap-4 w-full overflow-hidden">
|
||||||
<a-avatar size={32} class="!bg-brand-500">
|
<a-avatar size={32} class="!bg-brand-500">
|
||||||
{record.avatar?.startsWith('/') ? <img src={record.avatar} alt="" /> : record.nickname?.[0]}
|
{record.avatar?.startsWith('/') ? <img src={record.avatar} alt="" /> : record.nickname?.[0]}
|
||||||
|
|
@ -57,10 +52,21 @@ const { component: UserTable } = useTable({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
),
|
);
|
||||||
|
|
||||||
|
const { component: UserTable } = useTable({
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
title: '用户昵称',
|
||||||
|
dataIndex: 'username',
|
||||||
|
render: usernameRender,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...useCreateColumn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...useUpdateColumn(),
|
||||||
},
|
},
|
||||||
useCreateColumn(),
|
|
||||||
useUpdateColumn(),
|
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
type: 'button',
|
type: 'button',
|
||||||
|
|
@ -112,18 +118,14 @@ const { component: UserTable } = useTable({
|
||||||
label: '登录账号',
|
label: '登录账号',
|
||||||
setter: 'input',
|
setter: 'input',
|
||||||
required: true,
|
required: true,
|
||||||
setterProps: {
|
|
||||||
placeholder: '英文字母+数组组成,5~10位',
|
placeholder: '英文字母+数组组成,5~10位',
|
||||||
},
|
},
|
||||||
},
|
|
||||||
{
|
{
|
||||||
field: 'password',
|
field: 'password',
|
||||||
label: '登陆密码',
|
label: '登陆密码',
|
||||||
setter: 'input',
|
setter: 'input',
|
||||||
setterProps: {
|
|
||||||
placeholder: '包含大小写,长度6 ~ 12位',
|
placeholder: '包含大小写,长度6 ~ 12位',
|
||||||
},
|
},
|
||||||
},
|
|
||||||
{
|
{
|
||||||
field: 'nickname',
|
field: 'nickname',
|
||||||
label: '用户昵称',
|
label: '用户昵称',
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ export function useAuthGuard(router: Router) {
|
||||||
};
|
};
|
||||||
|
|
||||||
router.beforeEach(async function (to, from) {
|
router.beforeEach(async function (to, from) {
|
||||||
|
console.log(to);
|
||||||
const userStore = useUserStore(store);
|
const userStore = useUserStore(store);
|
||||||
const menuStore = useMenuStore(store);
|
const menuStore = useMenuStore(store);
|
||||||
|
|
||||||
|
|
@ -47,13 +48,13 @@ export function useAuthGuard(router: Router) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 已登陆进行提示
|
// 提示已登陆
|
||||||
Notification.warning({
|
Notification.warning({
|
||||||
title: '跳转提示',
|
title: '跳转提示',
|
||||||
content: `您已登陆,如需重新登陆请退出后再操作!`,
|
content: `您已登陆,如需重新登陆请退出后再操作!`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 不是从路由跳转的,跳转回首页
|
// 直接访问跳转回首页(不是从路由跳转)
|
||||||
if (!from.matched.length) {
|
if (!from.matched.length) {
|
||||||
return '/';
|
return '/';
|
||||||
}
|
}
|
||||||
|
|
@ -64,10 +65,13 @@ export function useAuthGuard(router: Router) {
|
||||||
|
|
||||||
// 未登录跳转到登陆页面
|
// 未登录跳转到登陆页面
|
||||||
if (!userStore.accessToken) {
|
if (!userStore.accessToken) {
|
||||||
return { path: '/login', query: { redirect: to.path } };
|
return {
|
||||||
|
path: '/login',
|
||||||
|
query: { redirect: to.path },
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 未获取菜单进行获取
|
// 未获取权限进行获取
|
||||||
if (!menuStore.menus.length) {
|
if (!menuStore.menus.length) {
|
||||||
// 菜单处理
|
// 菜单处理
|
||||||
const authMenus = treeFilter(menus, item => {
|
const authMenus = treeFilter(menus, item => {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ declare module '@vue/runtime-core' {
|
||||||
AAlert: typeof import('@arco-design/web-vue')['Alert']
|
AAlert: typeof import('@arco-design/web-vue')['Alert']
|
||||||
AAutoComplete: typeof import('@arco-design/web-vue')['AutoComplete']
|
AAutoComplete: typeof import('@arco-design/web-vue')['AutoComplete']
|
||||||
AAvatar: typeof import('@arco-design/web-vue')['Avatar']
|
AAvatar: typeof import('@arco-design/web-vue')['Avatar']
|
||||||
|
ABadge: typeof import('@arco-design/web-vue')['Badge']
|
||||||
ABreadcrumb: typeof import('@arco-design/web-vue')['Breadcrumb']
|
ABreadcrumb: typeof import('@arco-design/web-vue')['Breadcrumb']
|
||||||
ABreadcrumbItem: typeof import('@arco-design/web-vue')['BreadcrumbItem']
|
ABreadcrumbItem: typeof import('@arco-design/web-vue')['BreadcrumbItem']
|
||||||
AButton: typeof import('@arco-design/web-vue')['Button']
|
AButton: typeof import('@arco-design/web-vue')['Button']
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue