mirror of
https://github.com/vbenjs/vben-admin-thin-next.git
synced 2025-02-03 02:18:40 +08:00
chore: merge branch 'main' of github.com:anncwb/vue-vben-admin into main
This commit is contained in:
commit
b49e2ed35d
@ -22,6 +22,7 @@ module.exports = defineConfig({
|
|||||||
'plugin:@typescript-eslint/recommended',
|
'plugin:@typescript-eslint/recommended',
|
||||||
'prettier',
|
'prettier',
|
||||||
'plugin:prettier/recommended',
|
'plugin:prettier/recommended',
|
||||||
|
'plugin:jest/recommended',
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
'@typescript-eslint/ban-ts-ignore': 'off',
|
'@typescript-eslint/ban-ts-ignore': 'off',
|
||||||
|
5
.github/workflows/ftp-schedule.yml
vendored
5
.github/workflows/ftp-schedule.yml
vendored
@ -18,12 +18,13 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sed -i 's#VITE_PUBLIC_PATH\s*=.*#VITE_PUBLIC_PATH = /thin/next/#g' ./.env.production
|
sed -i 's#VITE_PUBLIC_PATH\s*=.*#VITE_PUBLIC_PATH = /thin/next/#g' ./.env.production
|
||||||
sed -i "s#VITE_BUILD_COMPRESS\s*=.*#VITE_BUILD_COMPRESS = 'gzip'#g" ./.env.production
|
sed -i "s#VITE_BUILD_COMPRESS\s*=.*#VITE_BUILD_COMPRESS = 'gzip'#g" ./.env.production
|
||||||
|
sed -i "s#VITE_DROP_CONSOLE\s*=.*#VITE_DROP_CONSOLE = true#g" ./.env.production
|
||||||
cat ./.env.production
|
cat ./.env.production
|
||||||
|
|
||||||
- name: use Node.js 14
|
- name: use Node.js 15
|
||||||
uses: actions/setup-node@v2.1.2
|
uses: actions/setup-node@v2.1.2
|
||||||
with:
|
with:
|
||||||
node-version: '14.x'
|
node-version: '15.x'
|
||||||
|
|
||||||
- name: Get yarn cache
|
- name: Get yarn cache
|
||||||
id: yarn-cache
|
id: yarn-cache
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,6 +10,7 @@ test/server/static
|
|||||||
# local env files
|
# local env files
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
# Log files
|
# Log files
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
|
44
.vscode/settings.json
vendored
44
.vscode/settings.json
vendored
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"cSpell.words": ["vben", "windi"],
|
|
||||||
"typescript.tsdk": "./node_modules/typescript/lib",
|
"typescript.tsdk": "./node_modules/typescript/lib",
|
||||||
|
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||||
"volar.tsPlugin": true,
|
"volar.tsPlugin": true,
|
||||||
"volar.tsPluginStatus": false,
|
"volar.tsPluginStatus": false,
|
||||||
//===========================================
|
//===========================================
|
||||||
@ -10,23 +10,14 @@
|
|||||||
"editor.tabSize": 2,
|
"editor.tabSize": 2,
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"diffEditor.ignoreTrimWhitespace": false,
|
"diffEditor.ignoreTrimWhitespace": false,
|
||||||
"editor.trimAutoWhitespace": true,
|
|
||||||
//===========================================
|
//===========================================
|
||||||
//============= Other =======================
|
//============= Other =======================
|
||||||
//===========================================
|
//===========================================
|
||||||
"breadcrumbs.enabled": true,
|
"breadcrumbs.enabled": true,
|
||||||
"open-in-browser.default": "chrome",
|
"open-in-browser.default": "chrome",
|
||||||
//===========================================
|
//===========================================
|
||||||
//============= emmet =======================
|
|
||||||
//===========================================
|
|
||||||
"emmet.triggerExpansionOnTab": true,
|
|
||||||
"emmet.showAbbreviationSuggestions": true,
|
|
||||||
//===========================================
|
|
||||||
//============= files =======================
|
//============= files =======================
|
||||||
//===========================================
|
//===========================================
|
||||||
"files.trimTrailingWhitespace": true,
|
|
||||||
// "files.insertFinalNewline": true,
|
|
||||||
// "files.trimFinalNewlines": true,
|
|
||||||
"files.eol": "\n",
|
"files.eol": "\n",
|
||||||
"search.exclude": {
|
"search.exclude": {
|
||||||
"**/node_modules": true,
|
"**/node_modules": true,
|
||||||
@ -49,9 +40,14 @@
|
|||||||
"CHANGELOG.md": true,
|
"CHANGELOG.md": true,
|
||||||
"examples": true,
|
"examples": true,
|
||||||
"res": true,
|
"res": true,
|
||||||
"screenshots": true
|
"screenshots": true,
|
||||||
|
"yarn-error.log": true,
|
||||||
|
"**/.yarn": true
|
||||||
},
|
},
|
||||||
"files.exclude": {
|
"files.exclude": {
|
||||||
|
"**/.cache": true,
|
||||||
|
"**/.editorconfig": true,
|
||||||
|
"**/.eslintcache": true,
|
||||||
"**/bower_components": true,
|
"**/bower_components": true,
|
||||||
"**/.idea": true,
|
"**/.idea": true,
|
||||||
"**/tmp": true,
|
"**/tmp": true,
|
||||||
@ -73,17 +69,6 @@
|
|||||||
},
|
},
|
||||||
"stylelint.enable": true,
|
"stylelint.enable": true,
|
||||||
"stylelint.packageManager": "yarn",
|
"stylelint.packageManager": "yarn",
|
||||||
// ===========================================
|
|
||||||
// ================ Vetur ====================
|
|
||||||
// ===========================================
|
|
||||||
// "vetur.experimental.templateInterpolationService": true,
|
|
||||||
// "vetur.format.options.tabSize": 2,
|
|
||||||
// "vetur.languageFeatures.codeActions": false,
|
|
||||||
// "vetur.format.defaultFormatterOptions": {
|
|
||||||
// "js-beautify-html": {
|
|
||||||
// "wrap_attributes": "force-expand-multiline"
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
"liveServer.settings.donotShowInfoMsg": true,
|
"liveServer.settings.donotShowInfoMsg": true,
|
||||||
"telemetry.enableCrashReporter": false,
|
"telemetry.enableCrashReporter": false,
|
||||||
"workbench.settings.enableNaturalLanguageSearch": false,
|
"workbench.settings.enableNaturalLanguageSearch": false,
|
||||||
@ -132,5 +117,18 @@
|
|||||||
"i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}",
|
"i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}",
|
||||||
"i18n-ally.enabledParsers": ["ts"],
|
"i18n-ally.enabledParsers": ["ts"],
|
||||||
"i18n-ally.sourceLanguage": "en",
|
"i18n-ally.sourceLanguage": "en",
|
||||||
"i18n-ally.enabledFrameworks": ["vue", "react"]
|
"i18n-ally.enabledFrameworks": ["vue", "react"],
|
||||||
|
"cSpell.words": [
|
||||||
|
"vben",
|
||||||
|
"windi",
|
||||||
|
"browserslist",
|
||||||
|
"tailwindcss",
|
||||||
|
"esnext",
|
||||||
|
"antv",
|
||||||
|
"tinymce",
|
||||||
|
"qrcode",
|
||||||
|
"sider",
|
||||||
|
"pinia",
|
||||||
|
"sider"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
107
CHANGELOG.md
107
CHANGELOG.md
@ -1,3 +1,110 @@
|
|||||||
|
# [2.5.0](https://github.com/anncwb/vue-vben-admin/compare/v2.4.0...v2.5.0) (2021-06-20)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- **api:** select api type error ([b387681](https://github.com/anncwb/vue-vben-admin/commit/b387681c00ac018f5bc6a9251009ddffe37acae6))
|
||||||
|
- **api-select:** loss option data on event callback ([c5f2577](https://github.com/anncwb/vue-vben-admin/commit/c5f2577f515e7ae96b27b509e5dd4b3317fcb7b4)), closes [#733](https://github.com/anncwb/vue-vben-admin/issues/733)
|
||||||
|
- **ApiSelect demo:** add demo about ApiSelect's use ([#757](https://github.com/anncwb/vue-vben-admin/issues/757)) ([a03d3cc](https://github.com/anncwb/vue-vben-admin/commit/a03d3cc60c770eba644c1f3837850a2c1c015029))
|
||||||
|
- **avatar:** mock data and Account center style ([2066f66](https://github.com/anncwb/vue-vben-admin/commit/2066f669715491f3e91ac6d0e905cd2b3e80b58d))
|
||||||
|
- **axios:** make sure that the parameter is an object before processing, fix [#660](https://github.com/anncwb/vue-vben-admin/issues/660) ([834fa7e](https://github.com/anncwb/vue-vben-admin/commit/834fa7eb9c8aff252e083d38fdab4f6f53b4d43a))
|
||||||
|
- **axios:** transformRequestHook logic error ([b69dcd7](https://github.com/anncwb/vue-vben-admin/commit/b69dcd79d742fd171302ce0f48c7750d60da217f))
|
||||||
|
- **code-editor:** fix CodeEditor style problem, fix [#655](https://github.com/anncwb/vue-vben-admin/issues/655) ([5662804](https://github.com/anncwb/vue-vben-admin/commit/566280422de0537c4e31496eaaa95a9d51fe9458))
|
||||||
|
- **codeeditor:** empty value set failed.fixed:[#659](https://github.com/anncwb/vue-vben-admin/issues/659) ([ba2bebb](https://github.com/anncwb/vue-vben-admin/commit/ba2bebb4069085817a90d065ed5877fdb50a8039))
|
||||||
|
- **codeMirror:** fix the JsonEditor embedded in the bullet frame causing the style to be disordered ([#668](https://github.com/anncwb/vue-vben-admin/issues/668)) ([e1123a2](https://github.com/anncwb/vue-vben-admin/commit/e1123a2ccb5d5450a5072c19e5508a5dc0f14423))
|
||||||
|
- **demo:** `breadcrumb` route invalid redirect ([84d9300](https://github.com/anncwb/vue-vben-admin/commit/84d9300e52fa73da575591aa4b71858a7e459c8c))
|
||||||
|
- **demo:** account list page validate and save ([21f7a85](https://github.com/anncwb/vue-vben-admin/commit/21f7a854fe2455315287d04e895661ff739bce17))
|
||||||
|
- **demo:** fix basic form page style ([8b6e07b](https://github.com/anncwb/vue-vben-admin/commit/8b6e07b768f110f13b4f2efa6c46e03266667a8c))
|
||||||
|
- **demo:** make sure the map https resource is correct ([7b9cd09](https://github.com/anncwb/vue-vben-admin/commit/7b9cd09ad8a50c45b2e661e07953d786d82f367d))
|
||||||
|
- **flow-chart:** fix drag and drop menu loss ([fa828fd](https://github.com/anncwb/vue-vben-admin/commit/fa828fd972efeea87f364be76a1139ae53ec20d8))
|
||||||
|
- **form:** fix form update problem ([bcad95d](https://github.com/anncwb/vue-vben-admin/commit/bcad95d32a08a73f84ecbabab409cd64159f4077)), closes [#720](https://github.com/anncwb/vue-vben-admin/issues/720)
|
||||||
|
- **form:** loss args on component change event ([513823b](https://github.com/anncwb/vue-vben-admin/commit/513823bfbd3e8acc68098e0708c34bff2dd8dba6))
|
||||||
|
- **form:** radioButtonGroup value support boolean ([9e2aa20](https://github.com/anncwb/vue-vben-admin/commit/9e2aa20daa08d2902cb5d56c1560306947e44939))
|
||||||
|
- **form:** radioButtonGroup value support number ([bbddf30](https://github.com/anncwb/vue-vben-admin/commit/bbddf30e96feb1ab048323d93d3b8c1b18857acd))
|
||||||
|
- **form:** schemas update problem ([808328d](https://github.com/anncwb/vue-vben-admin/commit/808328dc7e56b1cc07b678d501d9589290173443)), closes [#688](https://github.com/anncwb/vue-vben-admin/issues/688)
|
||||||
|
- **keep-alive:** tablist cache updating effect ([d62d0ca](https://github.com/anncwb/vue-vben-admin/commit/d62d0ca08cff442c23eb9265851b066a2f24afa8)), closes [#695](https://github.com/anncwb/vue-vben-admin/issues/695)
|
||||||
|
- **layout:** fix class loss ([d018363](https://github.com/anncwb/vue-vben-admin/commit/d018363ddcd68189a18829a2b2560f3b98da58a6))
|
||||||
|
- **layout:** fix style compatibility issues ([905e5b7](https://github.com/anncwb/vue-vben-admin/commit/905e5b714b582548f32feca723012124343686a6))
|
||||||
|
- **layout:** props warn ([#756](https://github.com/anncwb/vue-vben-admin/issues/756)) ([bbce002](https://github.com/anncwb/vue-vben-admin/commit/bbce002be170c52db984647c931db88d7724cb52))
|
||||||
|
- **lock:** fix lock modal height ([40e3cb0](https://github.com/anncwb/vue-vben-admin/commit/40e3cb043c90a8343fa44a32acad2cb77de732da)), closes [#701](https://github.com/anncwb/vue-vben-admin/issues/701)
|
||||||
|
- **log:** fix Wrong version number ([#653](https://github.com/anncwb/vue-vben-admin/issues/653)) ([4f0d45f](https://github.com/anncwb/vue-vben-admin/commit/4f0d45f1df48755eadc0b09fa19762ee68f9abd1))
|
||||||
|
- **login:** login page modal style fixed: [#662](https://github.com/anncwb/vue-vben-admin/issues/662) ([#666](https://github.com/anncwb/vue-vben-admin/issues/666)) ([b218f10](https://github.com/anncwb/vue-vben-admin/commit/b218f10e25a9364c399a5fe42eedb549f57c01ea))
|
||||||
|
- **menu:** fix the jitter problem of menu folding animation,fix [#732](https://github.com/anncwb/vue-vben-admin/issues/732) ([4c89ea7](https://github.com/anncwb/vue-vben-admin/commit/4c89ea7474f4315870df1790f99f3e431f343b90))
|
||||||
|
- **mock:** make sure ignore matches the file correctly, fix [#745](https://github.com/anncwb/vue-vben-admin/issues/745) ([a222ec8](https://github.com/anncwb/vue-vben-admin/commit/a222ec8553f9b4477a43a8f7d113b5646fbfc373))
|
||||||
|
- **mock:** menu list api loss `type` field ([4185412](https://github.com/anncwb/vue-vben-admin/commit/41854121f3713dbde236afd3a416e9f27bd0c673))
|
||||||
|
- **mock:** type error ([7c1ffa3](https://github.com/anncwb/vue-vben-admin/commit/7c1ffa3d23de508a8d1590985806cb7a484b24e5))
|
||||||
|
- **modal:** add v-model support for visible ([de12bab](https://github.com/anncwb/vue-vben-admin/commit/de12babd314ac831d3cb645f42dbf8a476075623))
|
||||||
|
- **modal:** ensure that the full screen height is calculated correctly ([1c1755c](https://github.com/anncwb/vue-vben-admin/commit/1c1755cf5b4ada7263c05ddf4105abb52a2abb2f))
|
||||||
|
- **modal:** ensure that the shutdown event is not triggered multiple times ([655b743](https://github.com/anncwb/vue-vben-admin/commit/655b74323653147943cbde2352208cb765c82b8a))
|
||||||
|
- **store:** fix type error after pinia version upgrade ([e8d6f88](https://github.com/anncwb/vue-vben-admin/commit/e8d6f8851efd7076946486864936f1797280d3ba))
|
||||||
|
- **use-message:** `content` not support vNode ([154ebc3](https://github.com/anncwb/vue-vben-admin/commit/154ebc3d96f73bb3ceab99ea0229a3619d585aba))
|
||||||
|
- build error ([5212ea7](https://github.com/anncwb/vue-vben-admin/commit/5212ea79b43c832a5136354b549de8f89b6e2156))
|
||||||
|
- fix darkModeSwitch switch failure ([34a8054](https://github.com/anncwb/vue-vben-admin/commit/34a80542de670f0385dffaf5bf64bb9c3f6b90da))
|
||||||
|
- fix if getDropdownList.length==0 show Dropdown component ([21c771b](https://github.com/anncwb/vue-vben-admin/commit/21c771b59cb45defbff37de21c5c1950370b8f92))
|
||||||
|
- fix Login Page LocalePicker showLocale condition ([d683b0f](https://github.com/anncwb/vue-vben-admin/commit/d683b0f1e85b85b07090feba4ac7f741bd3bd482))
|
||||||
|
- **modal:** redoModalHeight not work as expected ([5d554f1](https://github.com/anncwb/vue-vben-admin/commit/5d554f184f7b61774d1a1b2e61451677b38505de))
|
||||||
|
- **page:** `basic form` action btns should be in line ([6c4f947](https://github.com/anncwb/vue-vben-admin/commit/6c4f947386c181f45253c94e4ef735d29a253053))
|
||||||
|
- **radio-button:** fix RadioButton `disabled` support ([ee384b1](https://github.com/anncwb/vue-vben-admin/commit/ee384b1fa7e387b3680e9d54cbe4a1e2f15ec750)), closes [#710](https://github.com/anncwb/vue-vben-admin/issues/710)
|
||||||
|
- **route:** dynamically introduce components error ([c6b766d](https://github.com/anncwb/vue-vben-admin/commit/c6b766d8ea902294ab1f7e4a06781f2bcfdd1f0b))
|
||||||
|
- **router:** loss `directory` route ([df8cd86](https://github.com/anncwb/vue-vben-admin/commit/df8cd860514f32f44847dcf724f0737ed4d8b9e0)), closes [#722](https://github.com/anncwb/vue-vben-admin/issues/722)
|
||||||
|
- **table:** wrong indeterminate state ([495b1da](https://github.com/anncwb/vue-vben-admin/commit/495b1da385e9b6428d2b994669d2065722445923))
|
||||||
|
- **table:** make sure the table width is correct, fix [#593](https://github.com/anncwb/vue-vben-admin/issues/593) ([d73d43e](https://github.com/anncwb/vue-vben-admin/commit/d73d43ed91f30957cfd202c51552ca40a19cef08))
|
||||||
|
- **table:** settings indeterminate state effect ([4fd2051](https://github.com/anncwb/vue-vben-admin/commit/4fd2051bc0403bfc5345ed6a5fc283a372ef7a92))
|
||||||
|
- **table:** support change event ([9f4d171](https://github.com/anncwb/vue-vben-admin/commit/9f4d1719caa76de94e6362c16e4df3ac28df253c)), closes [#677](https://github.com/anncwb/vue-vben-admin/issues/677)
|
||||||
|
- **table:** try to get close to the form stuck ([d81481c](https://github.com/anncwb/vue-vben-admin/commit/d81481c52186145dac130aaa1594f0ba8db4d392))
|
||||||
|
- **table:** useTable support onChange ([9f5085c](https://github.com/anncwb/vue-vben-admin/commit/9f5085c9f9f46b09391156b17091c1771bc13026))
|
||||||
|
- **table-action:** fix the split line style is missing,fix [#674](https://github.com/anncwb/vue-vben-admin/issues/674) ([b1cb863](https://github.com/anncwb/vue-vben-admin/commit/b1cb86350253dc5be095466966d9469775f4395d))
|
||||||
|
- **Tinymce:** Read only status upload button can also be used ([#718](https://github.com/anncwb/vue-vben-admin/issues/718)) ([966571b](https://github.com/anncwb/vue-vben-admin/commit/966571bdcb11c2729ab9ce212bd3e195f7bf3a59))
|
||||||
|
- **tree:** support defaultExpandAll prop ([3ed2339](https://github.com/anncwb/vue-vben-admin/commit/3ed2339a6d75abbd6ccf723b6eaa762f9921409e))
|
||||||
|
- **upload:** ensure preview items valid ([4376928](https://github.com/anncwb/vue-vben-admin/commit/437692869a232ee65c300c65ee473557ae0913c7))
|
||||||
|
- **useViewHeight:** Fix the problem that useContentViewHeight does not calculate the footer ([#747](https://github.com/anncwb/vue-vben-admin/issues/747)) ([33cd8fe](https://github.com/anncwb/vue-vben-admin/commit/33cd8fe6533830176ab63ddfc4d74f75a384366c))
|
||||||
|
- ensure that roleList is not empty ([aebad61](https://github.com/anncwb/vue-vben-admin/commit/aebad61b3d3e11aaf720b37e762e53e2e6999d3c))
|
||||||
|
- fix node12 version data mock error ([644dbe3](https://github.com/anncwb/vue-vben-admin/commit/644dbe315bb03ea1641a682359873237208a5303))
|
||||||
|
- Fix the problem that the `lang` attribute of `HTML` will not be set when it is first loaded ([#682](https://github.com/anncwb/vue-vben-admin/issues/682)) ([eca8907](https://github.com/anncwb/vue-vben-admin/commit/eca8907a11c28d816c3da5a0667f45a38a499012))
|
||||||
|
- login failed ([035f55a](https://github.com/anncwb/vue-vben-admin/commit/035f55af9778819d72adc1700d9de56a6569b58f))
|
||||||
|
- session timeout login logic error ([#678](https://github.com/anncwb/vue-vben-admin/issues/678)) ([132c7fb](https://github.com/anncwb/vue-vben-admin/commit/132c7fb944df255c4d76a25d6d924439f91f9c54)), closes [#673](https://github.com/anncwb/vue-vben-admin/issues/673)
|
||||||
|
- theme switching fails ([7e2ca79](https://github.com/anncwb/vue-vben-admin/commit/7e2ca79ece2f5209cb7ce4b0f5ee15012f9f51de))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- optimize error message for api failure ([ea6834a](https://github.com/anncwb/vue-vben-admin/commit/ea6834aeec3ef56d411b2c10a474f75d3d7bfdfc))
|
||||||
|
- **api-select:** auto refetch after params changed ([50207ad](https://github.com/anncwb/vue-vben-admin/commit/50207ad702ef3faca1e27c873c89132ab92fae8e))
|
||||||
|
- **app-search:** auto focus on show ([1ae6362](https://github.com/anncwb/vue-vben-admin/commit/1ae636296df2cf99e8a777f053c539c50e6ad49a))
|
||||||
|
- **axios:** added authenticationScheme configuration,fix [#774](https://github.com/anncwb/vue-vben-admin/issues/774) ([b6d5b07](https://github.com/anncwb/vue-vben-admin/commit/b6d5b0796de4d0b66c0f33c335ec991d44f64ef2))
|
||||||
|
- **demo:** `switch` use in table ([46899aa](https://github.com/anncwb/vue-vben-admin/commit/46899aa3cd6b1616c42ac263a28af75be839f6a0))
|
||||||
|
- **demo:** added guide page example ([d196340](https://github.com/anncwb/vue-vben-admin/commit/d196340d270d2becbf2cc81b7d4f09273381bd09))
|
||||||
|
- **echarts:** add getInstance for useECharts ([fb6c76d](https://github.com/anncwb/vue-vben-admin/commit/fb6c76db535bd0c6305d03c0cff876a1f079100b))
|
||||||
|
- **modal:** add closeModal for useModal ([6d5f9aa](https://github.com/anncwb/vue-vben-admin/commit/6d5f9aa699c5da8af6bf5841baddc4a8bd603917))
|
||||||
|
- **modal:** add redoModalHeight for useModalInner ([f732b56](https://github.com/anncwb/vue-vben-admin/commit/f732b569042f7fe77c85cb295538ddd85561f7e9))
|
||||||
|
- **preview:** added createImgPreview picture preview function ([305630e](https://github.com/anncwb/vue-vben-admin/commit/305630e3fd886b3f690f890a934a8a6ba224fba1))
|
||||||
|
- **project-setting:** added sessionTimeoutProcessing project configuration item,fix [#772](https://github.com/anncwb/vue-vben-admin/issues/772) ([0d07084](https://github.com/anncwb/vue-vben-admin/commit/0d0708409c4adbe7a0c5e33abf5307031147eaeb))
|
||||||
|
- **table:** add editable DatePicker & TimePicker ([#654](https://github.com/anncwb/vue-vben-admin/issues/654)) ([93006c7](https://github.com/anncwb/vue-vben-admin/commit/93006c7dc7b5243b26637f444c8057c95935e622))
|
||||||
|
- **table:** add updateTableDataRecord method ([8e4f486](https://github.com/anncwb/vue-vben-admin/commit/8e4f486fcf835f0b6f2a95676dba268ffdd0566e))
|
||||||
|
- **table:** editable component text align ([8eaf575](https://github.com/anncwb/vue-vben-admin/commit/8eaf57562610a833c8083ae9957f458319d1cc93))
|
||||||
|
- **table:** support columns-change event ([125a7d1](https://github.com/anncwb/vue-vben-admin/commit/125a7d14831642c9cbb2e4b3e75953c3b2e2cdef))
|
||||||
|
- **table:** support custom update on row editing ([fe2bcfc](https://github.com/anncwb/vue-vben-admin/commit/fe2bcfc6f74159c355f3be153a316869fdb8b644)), closes [#646](https://github.com/anncwb/vue-vben-admin/issues/646)
|
||||||
|
- **table:** updateTableDataRecord support functional rowKey ([448a4c2](https://github.com/anncwb/vue-vben-admin/commit/448a4c2809672480f8f635d7cc4661554112598a))
|
||||||
|
- **table-action:** add stopButtonPropagation prop ([808012b](https://github.com/anncwb/vue-vben-admin/commit/808012b544b8c6f3cf467f42653c2783dbe8be6b)), closes [#699](https://github.com/anncwb/vue-vben-admin/issues/699)
|
||||||
|
- **table-img:** support simple show mode and more props ([19d8e01](https://github.com/anncwb/vue-vben-admin/commit/19d8e01e11644c66222f137abd05940cbdec0bb6))
|
||||||
|
- **test:** add jest test suite ([f6fe1dd](https://github.com/anncwb/vue-vben-admin/commit/f6fe1dd62df231ccbd063db0d32359b48aa5c76b))
|
||||||
|
- **use-drawer:** add closeDrawer function ([639520a](https://github.com/anncwb/vue-vben-admin/commit/639520ad5ddf829875ab517067abf2b45ebc04c2))
|
||||||
|
- add CropperAvatar component ([8e410fc](https://github.com/anncwb/vue-vben-admin/commit/8e410fc6401847d8e5545468b5ce6fd7ce9fc5cc))
|
||||||
|
- **tabs:** add setTabTitle method ([#680](https://github.com/anncwb/vue-vben-admin/issues/680)) ([5ddccf6](https://github.com/anncwb/vue-vben-admin/commit/5ddccf6ba28453b9a35355d53d0db65f1a8876bc))
|
||||||
|
- **tinymce:** support dark theme and I18n ([83c9cd7](https://github.com/anncwb/vue-vben-admin/commit/83c9cd77421e9c0888a41e2d8dcbca816da67488))
|
||||||
|
- **Tinymce:** add dynamics to the read-only state of the rich text editor ([#725](https://github.com/anncwb/vue-vben-admin/issues/725)) ([efce482](https://github.com/anncwb/vue-vben-admin/commit/efce482b3215ddf9ed588f63a218d5f76939e947))
|
||||||
|
- **tree:** add defaultExpandLevel prop ([6edca1c](https://github.com/anncwb/vue-vben-admin/commit/6edca1c19c3b0772f9ab82a7b09251a74fff2173)), closes [#672](https://github.com/anncwb/vue-vben-admin/issues/672)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
- **component:** optimize tree and upload components ([3f6920f](https://github.com/anncwb/vue-vben-admin/commit/3f6920f7a9775fc06a34dead90b1724b23b7759c))
|
||||||
|
- **cropper-avatar:** code optimization ([6dbbdba](https://github.com/anncwb/vue-vben-admin/commit/6dbbdbac76c2c3795e12dd346f6310d1b70f6a7d))
|
||||||
|
- **i18n:** improve circular dependencies ([d677729](https://github.com/anncwb/vue-vben-admin/commit/d677729acbe2c024ab13cf490b205528507c4823))
|
||||||
|
- **i18n:** improve warning prompt ([6ef62ba](https://github.com/anncwb/vue-vben-admin/commit/6ef62ba6ea7f5613a1fec982b30fe6b0f478bf59))
|
||||||
|
- **locale:** reduce the number of multilingual files ([0acc4ab](https://github.com/anncwb/vue-vben-admin/commit/0acc4ab2dd70a239bd13929edede02b283feb7c2))
|
||||||
|
- **PageWrapper:** fix the height calculation problem when footer and global footer are opened at the same time ([#760](https://github.com/anncwb/vue-vben-admin/issues/760)) ([ab2c7ef](https://github.com/anncwb/vue-vben-admin/commit/ab2c7efe6994dacfe0ff407783f2c3b246427bfc))
|
||||||
|
- **utils:** mitt default export is changed from Class to Function ([d3d620f](https://github.com/anncwb/vue-vben-admin/commit/d3d620f4fc75dd69270e4d090a71d426701272ef))
|
||||||
|
- add createImgPreview func ([#713](https://github.com/anncwb/vue-vben-admin/issues/713)) ([b7c7c46](https://github.com/anncwb/vue-vben-admin/commit/b7c7c46853d332641d116d818e657447884784f3))
|
||||||
|
- optimize components and add comments ([55e9d9f](https://github.com/anncwb/vue-vben-admin/commit/55e9d9fc2953643cec95c74b6ed34b0e68641fb6))
|
||||||
|
|
||||||
## [2.4.2](https://github.com/anncwb/vue-vben-admin/compare/v2.4.0...v2.4.2) (2021-06-09)
|
## [2.4.2](https://github.com/anncwb/vue-vben-admin/compare/v2.4.0...v2.4.2) (2021-06-09)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
@ -1,3 +1,44 @@
|
|||||||
|
## 2.5.0(2021-06-20)
|
||||||
|
|
||||||
|
## (破坏性更新) Breaking changes
|
||||||
|
|
||||||
|
- 将项目`windicss`改为`tailwindcss`,解决内存溢出问题
|
||||||
|
- 目前项目不兼容地方有
|
||||||
|
- `!xl:m-4` 之类的写法需要改为`xl:!m-4`,注意只有`!`这个不兼容,没用到则不用改
|
||||||
|
- `windicss`自身新增的特性需要调整,比如`Attribute`模式不兼容
|
||||||
|
|
||||||
|
### ✨ Refactor
|
||||||
|
|
||||||
|
- 移除`useExpose`,使用组件自身提供的`expose`代替
|
||||||
|
|
||||||
|
### ⚡ Performance Improvements
|
||||||
|
|
||||||
|
- **Locale** 合并多语言文件,减少文件数量
|
||||||
|
- **Utils** Mitt 默认导出由 `Class` 改为 `Function`
|
||||||
|
- **Axios** `isTransformRequestResult`更名为`isTransformResponse`
|
||||||
|
|
||||||
|
### ✨ Features
|
||||||
|
|
||||||
|
- **CropperImage** `Cropper` 头像裁剪新增圆形裁剪功能
|
||||||
|
- **CropperAvatar** 新增头像上传组件
|
||||||
|
- **Drawer** `useDrawer`新增`closeDrawer`函数
|
||||||
|
- **Preview** 新增`createImgPreview`图片预览函数
|
||||||
|
- **Setup** 新增引导页示例
|
||||||
|
- **Tests** 添加 jest 测试套件,暂不支持 Vue 组件单测
|
||||||
|
- **Axios** 新增`authenticationScheme`配置,用于指定认证方案
|
||||||
|
- **Setting** 新增 `sessionTimeoutProcessing` 项目配置项,用于配置会话超时如何处理
|
||||||
|
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
|
- **Modal** 修复全屏高度计算错误
|
||||||
|
- **Modal** 修复关闭事件触发多次问题
|
||||||
|
- **PageWrapper** 修复高度计算问题
|
||||||
|
- **FlowChart** 修复拖放菜单丢失
|
||||||
|
- 修复后台模式下,Iframe 路由错误
|
||||||
|
- **PageWrapper** 修复 footer 与全局页脚同时开启时的高度计算问题
|
||||||
|
- **Menu** 修复菜单折叠动画抖动问题
|
||||||
|
- **Store**修复 pinia 版本升级之后类型错误
|
||||||
|
|
||||||
## 2.4.2(2021-06-10)
|
## 2.4.2(2021-06-10)
|
||||||
|
|
||||||
### ✨ Refactor
|
### ✨ Refactor
|
||||||
|
@ -5,7 +5,6 @@ import vueJsx from '@vitejs/plugin-vue-jsx';
|
|||||||
import legacy from '@vitejs/plugin-legacy';
|
import legacy from '@vitejs/plugin-legacy';
|
||||||
|
|
||||||
import purgeIcons from 'vite-plugin-purge-icons';
|
import purgeIcons from 'vite-plugin-purge-icons';
|
||||||
import windiCSS from 'vite-plugin-windicss';
|
|
||||||
|
|
||||||
import { configHtmlPlugin } from './html';
|
import { configHtmlPlugin } from './html';
|
||||||
import { configPwaConfig } from './pwa';
|
import { configPwaConfig } from './pwa';
|
||||||
@ -46,9 +45,6 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
|
|||||||
// vite-plugin-svg-icons
|
// vite-plugin-svg-icons
|
||||||
vitePlugins.push(configSvgIconsPlugin(isBuild));
|
vitePlugins.push(configSvgIconsPlugin(isBuild));
|
||||||
|
|
||||||
// vite-plugin-windicss
|
|
||||||
vitePlugins.push(windiCSS());
|
|
||||||
|
|
||||||
// vite-plugin-mock
|
// vite-plugin-mock
|
||||||
VITE_USE_MOCK && vitePlugins.push(configMockPlugin(isBuild));
|
VITE_USE_MOCK && vitePlugins.push(configMockPlugin(isBuild));
|
||||||
|
|
||||||
|
36
jest.config.mjs
Normal file
36
jest.config.mjs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
export default {
|
||||||
|
preset: 'ts-jest',
|
||||||
|
roots: ['<rootDir>/tests/'],
|
||||||
|
clearMocks: true,
|
||||||
|
moduleDirectories: ['node_modules', 'src'],
|
||||||
|
moduleFileExtensions: ['js', 'ts', 'vue', 'tsx', 'jsx', 'json', 'node'],
|
||||||
|
modulePaths: ['<rootDir>/src', '<rootDir>/node_modules'],
|
||||||
|
testMatch: [
|
||||||
|
'**/tests/**/*.[jt]s?(x)',
|
||||||
|
'**/?(*.)+(spec|test).[tj]s?(x)',
|
||||||
|
'(/__tests__/.*|(\\.|/)(test|spec))\\.(js|ts)$',
|
||||||
|
],
|
||||||
|
testPathIgnorePatterns: [
|
||||||
|
'<rootDir>/tests/server/',
|
||||||
|
'<rootDir>/tests/__mocks__/',
|
||||||
|
'/node_modules/',
|
||||||
|
],
|
||||||
|
transform: {
|
||||||
|
'^.+\\.tsx?$': 'ts-jest',
|
||||||
|
},
|
||||||
|
transformIgnorePatterns: ['<rootDir>/tests/__mocks__/', '/node_modules/'],
|
||||||
|
// A map from regular expressions to module names that allow to stub out resources with a single module
|
||||||
|
moduleNameMapper: {
|
||||||
|
'\\.(vs|fs|vert|frag|glsl|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
|
||||||
|
'<rootDir>/tests/__mocks__/fileMock.ts',
|
||||||
|
'\\.(sass|s?css|less)$': '<rootDir>/tests/__mocks__/styleMock.ts',
|
||||||
|
'\\?worker$': '<rootDir>/tests/__mocks__/workerMock.ts',
|
||||||
|
'^/@/(.*)$': '<rootDir>/src/$1',
|
||||||
|
},
|
||||||
|
testEnvironment: 'jsdom',
|
||||||
|
verbose: true,
|
||||||
|
collectCoverage: false,
|
||||||
|
coverageDirectory: 'coverage',
|
||||||
|
collectCoverageFrom: ['src/**/*.{js,ts,vue}'],
|
||||||
|
coveragePathIgnorePatterns: ['^.+\\.d\\.ts$'],
|
||||||
|
};
|
@ -1,12 +1,15 @@
|
|||||||
import { MockMethod } from 'vite-plugin-mock';
|
import { MockMethod } from 'vite-plugin-mock';
|
||||||
import { resultSuccess } from '../_util';
|
import { resultSuccess } from '../_util';
|
||||||
|
|
||||||
|
const list: any[] = [];
|
||||||
const demoList = (() => {
|
const demoList = (() => {
|
||||||
const result: any[] = [];
|
const result = {
|
||||||
|
list: list,
|
||||||
|
};
|
||||||
for (let index = 0; index < 20; index++) {
|
for (let index = 0; index < 20; index++) {
|
||||||
result.push({
|
result.list.push({
|
||||||
label: `选项${index}`,
|
name: `选项${index}`,
|
||||||
value: `${index}`,
|
id: `${index}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -15,8 +18,8 @@ const demoList = (() => {
|
|||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
url: '/basic-api/select/getDemoOptions',
|
url: '/basic-api/select/getDemoOptions',
|
||||||
timeout: 2000,
|
timeout: 1000,
|
||||||
method: 'get',
|
method: 'post',
|
||||||
response: ({ query }) => {
|
response: ({ query }) => {
|
||||||
console.log(query);
|
console.log(query);
|
||||||
return resultSuccess(demoList);
|
return resultSuccess(demoList);
|
||||||
|
@ -168,6 +168,34 @@ const sysRoute = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const linkRoute = {
|
||||||
|
path: '/link',
|
||||||
|
name: 'Link',
|
||||||
|
component: 'LAYOUT',
|
||||||
|
meta: {
|
||||||
|
icon: 'ion:tv-outline',
|
||||||
|
title: 'routes.demo.iframe.frame',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'doc',
|
||||||
|
name: 'Doc',
|
||||||
|
meta: {
|
||||||
|
title: 'routes.demo.iframe.doc',
|
||||||
|
frameSrc: 'https://vvbin.cn/doc-next/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'https://vvbin.cn/doc-next/',
|
||||||
|
name: 'DocExternal',
|
||||||
|
component: 'LAYOUT',
|
||||||
|
meta: {
|
||||||
|
title: 'routes.demo.iframe.docExternal',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
url: '/basic-api/getMenuList',
|
url: '/basic-api/getMenuList',
|
||||||
@ -184,10 +212,10 @@ export default [
|
|||||||
}
|
}
|
||||||
const id = checkUser.userId;
|
const id = checkUser.userId;
|
||||||
if (!id || id === '1') {
|
if (!id || id === '1') {
|
||||||
return resultSuccess([dashboardRoute, authRoute, levelRoute, sysRoute]);
|
return resultSuccess([dashboardRoute, authRoute, levelRoute, sysRoute, linkRoute]);
|
||||||
}
|
}
|
||||||
if (id === '2') {
|
if (id === '2') {
|
||||||
return resultSuccess([dashboardRoute, authRoute, levelRoute]);
|
return resultSuccess([dashboardRoute, authRoute, levelRoute, linkRoute]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
64
package.json
64
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vben-admin",
|
"name": "vben-admin",
|
||||||
"version": "2.4.2",
|
"version": "2.5.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "vben",
|
"name": "vben",
|
||||||
"email": "anncwb@126.com",
|
"email": "anncwb@126.com",
|
||||||
@ -19,11 +19,13 @@
|
|||||||
"log": "conventional-changelog -p angular -i CHANGELOG.md -s",
|
"log": "conventional-changelog -p angular -i CHANGELOG.md -s",
|
||||||
"clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite",
|
"clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite",
|
||||||
"clean:lib": "rimraf node_modules",
|
"clean:lib": "rimraf node_modules",
|
||||||
"lint:eslint": "eslint \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
|
"lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
|
||||||
"lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
|
"lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
|
||||||
"lint:stylelint": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
|
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
|
||||||
"lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js",
|
"lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js",
|
||||||
"lint:pretty": "pretty-quick --staged",
|
"lint:pretty": "pretty-quick --staged",
|
||||||
|
"test:unit": "jest",
|
||||||
|
"test:unit-coverage": "jest --coverage",
|
||||||
"test:gzip": "http-server dist --cors --gzip -c-1",
|
"test:gzip": "http-server dist --cors --gzip -c-1",
|
||||||
"test:br": "http-server dist --cors --brotli -c-1",
|
"test:br": "http-server dist --cors --brotli -c-1",
|
||||||
"reinstall": "rimraf yarn.lock && rimraf package.lock.json && rimraf node_modules && npm run bootstrap",
|
"reinstall": "rimraf yarn.lock && rimraf package.lock.json && rimraf node_modules && npm run bootstrap",
|
||||||
@ -32,10 +34,10 @@
|
|||||||
"postinstall": "npm run install:husky"
|
"postinstall": "npm run install:husky"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify/iconify": "^2.0.1",
|
"@iconify/iconify": "^2.0.2",
|
||||||
"@vueuse/core": "^5.0.2",
|
"@vueuse/core": "^5.0.3",
|
||||||
"@zxcvbn-ts/core": "^0.3.0",
|
"@zxcvbn-ts/core": "^0.3.0",
|
||||||
"ant-design-vue": "2.1.2",
|
"ant-design-vue": "2.1.6",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"crypto-js": "^4.0.0",
|
"crypto-js": "^4.0.0",
|
||||||
"echarts": "^5.1.2",
|
"echarts": "^5.1.2",
|
||||||
@ -43,83 +45,91 @@
|
|||||||
"mockjs": "^1.1.0",
|
"mockjs": "^1.1.0",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"path-to-regexp": "^6.2.0",
|
"path-to-regexp": "^6.2.0",
|
||||||
"pinia": "2.0.0-alpha.13",
|
"pinia": "2.0.0-beta.3",
|
||||||
"qrcode": "^1.4.4",
|
"qrcode": "^1.4.4",
|
||||||
"sortablejs": "^1.13.0",
|
"sortablejs": "^1.13.0",
|
||||||
"vue": "3.0.11",
|
"vue": "3.0.11",
|
||||||
"vue-i18n": "9.1.6",
|
"vue-i18n": "9.1.6",
|
||||||
"vue-router": "^4.0.8",
|
"vue-json-pretty": "^2.0.2",
|
||||||
|
"vue-router": "^4.0.9",
|
||||||
"vue-types": "^3.0.2"
|
"vue-types": "^3.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^12.1.4",
|
"@commitlint/cli": "^12.1.4",
|
||||||
"@commitlint/config-conventional": "^12.1.4",
|
"@commitlint/config-conventional": "^12.1.4",
|
||||||
"@iconify/json": "^1.1.354",
|
"@iconify/json": "^1.1.358",
|
||||||
"@purge-icons/generated": "^0.7.0",
|
"@purge-icons/generated": "^0.7.0",
|
||||||
"@types/codemirror": "^5.60.0",
|
"@types/codemirror": "^5.60.0",
|
||||||
"@types/crypto-js": "^4.0.1",
|
"@types/crypto-js": "^4.0.1",
|
||||||
"@types/fs-extra": "^9.0.11",
|
"@types/fs-extra": "^9.0.11",
|
||||||
"@types/inquirer": "^7.3.1",
|
"@types/inquirer": "^7.3.2",
|
||||||
|
"@types/intro.js": "^3.0.1",
|
||||||
|
"@types/jest": "^26.0.23",
|
||||||
"@types/lodash-es": "^4.17.4",
|
"@types/lodash-es": "^4.17.4",
|
||||||
"@types/mockjs": "^1.0.3",
|
"@types/mockjs": "^1.0.3",
|
||||||
"@types/node": "^15.12.2",
|
"@types/node": "^15.12.4",
|
||||||
"@types/nprogress": "^0.2.0",
|
"@types/nprogress": "^0.2.0",
|
||||||
"@types/qrcode": "^1.4.0",
|
"@types/qrcode": "^1.4.0",
|
||||||
"@types/qs": "^6.9.6",
|
"@types/qs": "^6.9.6",
|
||||||
"@types/sortablejs": "^1.10.6",
|
"@types/sortablejs": "^1.10.6",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.26.1",
|
"@typescript-eslint/eslint-plugin": "^4.27.0",
|
||||||
"@typescript-eslint/parser": "^4.26.1",
|
"@typescript-eslint/parser": "^4.27.0",
|
||||||
"@vitejs/plugin-legacy": "^1.4.1",
|
"@vitejs/plugin-legacy": "^1.4.1",
|
||||||
"@vitejs/plugin-vue": "^1.2.3",
|
"@vitejs/plugin-vue": "^1.2.3",
|
||||||
"@vitejs/plugin-vue-jsx": "^1.1.5",
|
"@vitejs/plugin-vue-jsx": "^1.1.5",
|
||||||
"@vue/compiler-sfc": "3.0.11",
|
"@vue/compiler-sfc": "3.0.11",
|
||||||
|
"@vue/test-utils": "^2.0.0-rc.7",
|
||||||
"autoprefixer": "^10.2.6",
|
"autoprefixer": "^10.2.6",
|
||||||
"commitizen": "^4.2.4",
|
"commitizen": "^4.2.4",
|
||||||
"conventional-changelog-cli": "^2.1.1",
|
"conventional-changelog-cli": "^2.1.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^10.0.0",
|
||||||
"eslint": "^7.28.0",
|
"eslint": "^7.29.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"eslint-define-config": "^1.0.8",
|
"eslint-define-config": "^1.0.8",
|
||||||
|
"eslint-plugin-jest": "^24.3.6",
|
||||||
"eslint-plugin-prettier": "^3.4.0",
|
"eslint-plugin-prettier": "^3.4.0",
|
||||||
"eslint-plugin-vue": "^7.10.0",
|
"eslint-plugin-vue": "^7.11.1",
|
||||||
"esno": "^0.7.1",
|
"esno": "^0.7.3",
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
"http-server": "^0.12.3",
|
"http-server": "^0.12.3",
|
||||||
"husky": "^6.0.0",
|
"husky": "^6.0.0",
|
||||||
"inquirer": "^8.1.0",
|
"inquirer": "^8.1.1",
|
||||||
"is-ci": "^3.0.0",
|
"is-ci": "^3.0.0",
|
||||||
|
"jest": "^27.0.4",
|
||||||
"less": "^4.1.1",
|
"less": "^4.1.1",
|
||||||
"lint-staged": "^11.0.0",
|
"lint-staged": "^11.0.0",
|
||||||
"postcss": "^8.3.0",
|
"npm-run-all": "^4.1.5",
|
||||||
|
"postcss": "^8.3.5",
|
||||||
"prettier": "^2.3.1",
|
"prettier": "^2.3.1",
|
||||||
"pretty-quick": "^3.1.0",
|
"pretty-quick": "^3.1.1",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rollup-plugin-visualizer": "5.5.0",
|
"rollup-plugin-visualizer": "5.5.0",
|
||||||
"stylelint": "^13.13.1",
|
"stylelint": "^13.13.1",
|
||||||
"stylelint-config-prettier": "^8.0.2",
|
"stylelint-config-prettier": "^8.0.2",
|
||||||
"stylelint-config-standard": "^22.0.0",
|
"stylelint-config-standard": "^22.0.0",
|
||||||
"stylelint-order": "^4.1.0",
|
"stylelint-order": "^4.1.0",
|
||||||
|
"tailwindcss": "^2.2.2",
|
||||||
|
"ts-jest": "^27.0.3",
|
||||||
"ts-node": "^10.0.0",
|
"ts-node": "^10.0.0",
|
||||||
"typescript": "4.3.2",
|
"typescript": "4.3.4",
|
||||||
"vite": "2.3.3",
|
"vite": "2.3.8",
|
||||||
"vite-plugin-compression": "^0.2.5",
|
"vite-plugin-compression": "^0.2.5",
|
||||||
"vite-plugin-html": "^2.0.7",
|
"vite-plugin-html": "^2.0.7",
|
||||||
"vite-plugin-imagemin": "^0.3.2",
|
"vite-plugin-imagemin": "^0.3.2",
|
||||||
"vite-plugin-mock": "^2.7.1",
|
"vite-plugin-mock": "^2.8.0",
|
||||||
"vite-plugin-purge-icons": "^0.7.0",
|
"vite-plugin-purge-icons": "^0.7.0",
|
||||||
"vite-plugin-pwa": "^0.7.3",
|
"vite-plugin-pwa": "^0.8.1",
|
||||||
"vite-plugin-style-import": "^0.10.1",
|
"vite-plugin-style-import": "^1.0.0",
|
||||||
"vite-plugin-svg-icons": "^0.7.0",
|
"vite-plugin-svg-icons": "^0.7.1",
|
||||||
"vite-plugin-theme": "^0.8.1",
|
"vite-plugin-theme": "^0.8.1",
|
||||||
"vite-plugin-windicss": "^1.0.3",
|
|
||||||
"vue-eslint-parser": "^7.6.0",
|
"vue-eslint-parser": "^7.6.0",
|
||||||
"vue-tsc": "^0.1.7"
|
"vue-tsc": "^0.1.7"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"//": "Used to install imagemin dependencies, because imagemin may not be installed in China. If it is abroad, you can delete it",
|
"//": "Used to install imagemin dependencies, because imagemin may not be installed in China. If it is abroad, you can delete it",
|
||||||
"bin-wrapper": "npm:bin-wrapper-china",
|
"bin-wrapper": "npm:bin-wrapper-china",
|
||||||
"rollup": "^2.51.1"
|
"rollup": "^2.52.1"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: {
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
autoprefixer: {},
|
autoprefixer: {},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { RouteMeta } from '/@/router/types';
|
import type { RouteMeta } from 'vue-router';
|
||||||
export interface RouteItem {
|
export interface RouteItem {
|
||||||
path: string;
|
path: string;
|
||||||
component: any;
|
component: any;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { UploadApiResult } from './model/uploadModel';
|
import { UploadApiResult } from './model/uploadModel';
|
||||||
import { defHttp } from '/@/utils/http/axios';
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
import { UploadFileParams } from '/@/utils/http/axios/types';
|
import { UploadFileParams } from '/#/axios';
|
||||||
import { useGlobSetting } from '/@/hooks/setting';
|
import { useGlobSetting } from '/@/hooks/setting';
|
||||||
|
|
||||||
const { uploadUrl = '' } = useGlobSetting();
|
const { uploadUrl = '' } = useGlobSetting();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { defHttp } from '/@/utils/http/axios';
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
import { LoginParams, LoginResultModel, GetUserInfoModel } from './model/userModel';
|
import { LoginParams, LoginResultModel, GetUserInfoModel } from './model/userModel';
|
||||||
|
|
||||||
import { ErrorMessageMode } from '/@/utils/http/axios/types';
|
import { ErrorMessageMode } from '/#/axios';
|
||||||
|
|
||||||
enum Api {
|
enum Api {
|
||||||
Login = '/login',
|
Login = '/login',
|
||||||
|
1
src/assets/svg/preview/p-rotate.svg
Normal file
1
src/assets/svg/preview/p-rotate.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1595306944988" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1820" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><defs><style type="text/css"></style></defs><path d="M1464.3 279.7" p-id="1821" fill="#ffffff"></path><path d="M512 960c-60.5 0-119.1-11.9-174.4-35.2-53.4-22.6-101.3-54.9-142.4-96s-73.4-89-96-142.4C75.9 631.1 64 572.5 64 512s11.9-119.1 35.2-174.4c22.6-53.4 54.9-101.3 96-142.4s89-73.4 142.4-96C392.9 75.9 451.5 64 512 64s119.1 11.9 174.4 35.2c53.4 22.6 101.3 54.9 142.4 96s73.4 89 96 142.4C948.1 392.9 960 451.5 960 512c0 19.1-15.5 34.6-34.6 34.6s-34.6-15.5-34.6-34.6c0-51.2-10-100.8-29.8-147.4-19.1-45.1-46.4-85.6-81.2-120.4C745 209.4 704.5 182 659.4 163c-46.7-19.7-96.3-29.8-147.4-29.8-51.2 0-100.8 10-147.4 29.8-45.1 19.1-85.6 46.4-120.4 81.2S182 319.5 163 364.6c-19.7 46.7-29.8 96.3-29.8 147.4 0 51.2 10 100.8 29.8 147.4 19.1 45.1 46.4 85.6 81.2 120.4C279 814.6 319.5 842 364.6 861c46.7 19.7 96.3 29.8 147.4 29.8 64.6 0 128.4-16.5 184.4-47.8 54.4-30.4 100.9-74.1 134.6-126.6 10.3-16.1 31.7-20.8 47.8-10.4 16.1 10.3 20.8 31.7 10.4 47.8-39.8 62-94.8 113.7-159.1 149.6-66.2 37-141.7 56.6-218.1 56.6z" p-id="1822" fill="#ffffff"></path><path d="M924 552c-19.8 0-36-16.2-36-36V228c0-19.8 16.2-36 36-36s36 16.2 36 36v288c0 19.8-16.2 36-36 36zM275.4 575.5c9.5-2.5 19.1 2.9 22.3 12.2 3.5 10.2 9.9 17.7 19.1 22.6 7.1 3.9 15.1 5.8 24 5.8 16.6 0 30.8-6.9 42.5-20.8 11.7-13.8 20-32.7 24.9-75.1-7.7 12.2-17.3 20.8-28.7 25.8-11.4 5-23.7 7.4-36.8 7.4-26.7 0-47.7-8.3-63.3-24.9-15.5-16.6-23.3-37.9-23.3-64.1 0-25.1 7.7-47.1 23-66.2 15.3-19 37.9-28.6 67.8-28.6 40.3 0 68.1 18.1 83.4 54.4 8.5 19.9 12.7 44.9 12.7 74.9 0 33.8-5.1 63.8-15.3 89.9-16.9 43.5-45.5 65.2-85.8 65.2-27 0-47.6-7.1-61.6-21.2-10-10.1-16.4-22-19.3-35.8-2-9.6 4-19.1 13.5-21.6l0.9 0.1z m103-74.4c9.4-7.5 14.1-20.6 14.1-39.3 0-16.8-4.2-29.3-12.7-37.5S360.6 412 347.5 412c-14 0-25.2 4.7-33.4 14.1-8.2 9.4-12.4 22-12.4 37.7 0 14.9 3.6 26.7 10.9 35.5 7.2 8.8 18.8 13.1 34.6 13.1 11.4 0 21.8-3.8 31.2-11.3zM646.6 414.4c12.4 22.8 18.5 54 18.5 93.7 0 37.6-5.6 68.7-16.8 93.3-16.2 35.3-42.8 52.9-79.6 52.9-33.2 0-57.9-14.4-74.2-43.3-13.5-24.1-20.3-56.4-20.3-97 0-31.4 4.1-58.4 12.2-80.9 15.2-42 42.7-63 82.5-63 35.9 0 61.8 14.8 77.7 44.3z m-40.2 173.3c9.4-13.9 14-39.9 14-78 0-27.4-3.4-50-10.1-67.7-6.8-17.7-19.9-26.6-39.4-26.6-17.9 0-31 8.4-39.3 25.2-8.3 16.8-12.4 41.6-12.4 74.3 0 24.6 2.6 44.4 7.9 59.4 8.1 22.8 22 34.3 41.6 34.3 15.7 0 28.3-7 37.7-20.9zM803.3 387.2c11.2 11.3 16.8 25 16.8 41.2 0 16.7-5.8 30.7-17.5 41.8C791 481.4 777.4 487 762 487c-17.1 0-31.2-5.8-42.1-17.4-10.9-11.6-16.4-25.1-16.4-40.6 0-16.5 5.8-30.4 17.3-41.7 11.5-11.3 25.3-17 41.2-17 16.3 0 30.1 5.7 41.3 16.9zM739.5 451c6.2 6.2 13.7 9.3 22.5 9.3 8.4 0 15.8-3.1 22.1-9.3 6.3-6.2 9.4-13.7 9.4-22.6 0-8.5-3.1-15.9-9.3-22.1-6.2-6.2-13.6-9.3-22.2-9.3s-16.1 3.1-22.4 9.3c-6.3 6.2-9.4 13.7-9.4 22.6-0.1 8.4 3 15.8 9.3 22.1z" p-id="1823" fill="#ffffff"></path></svg>
|
After Width: | Height: | Size: 3.0 KiB |
1
src/assets/svg/preview/resume.svg
Normal file
1
src/assets/svg/preview/resume.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1595307154239" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7317" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><defs><style type="text/css"></style></defs><path d="M316 672h60c4.4 0 8-3.6 8-8V360c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v304c0 4.4 3.6 8 8 8zM512 622c22.1 0 40-17.9 40-39 0-23.1-17.9-41-40-41s-40 17.9-40 41c0 21.1 17.9 39 40 39zM512 482c22.1 0 40-17.9 40-39 0-23.1-17.9-41-40-41s-40 17.9-40 41c0 21.1 17.9 39 40 39z" p-id="7318" fill="#ffffff"></path><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32z m-40 728H184V184h656v656z" p-id="7319" fill="#ffffff"></path><path d="M648 672h60c4.4 0 8-3.6 8-8V360c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v304c0 4.4 3.6 8 8 8z" p-id="7320" fill="#ffffff"></path></svg>
|
After Width: | Height: | Size: 996 B |
1
src/assets/svg/preview/scale.svg
Normal file
1
src/assets/svg/preview/scale.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1595307195033" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8116" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><defs><style type="text/css"></style></defs><path d="M887.081 904.791a25.8 25.8 0 0 1-18.376-7.619L705.618 734.075l-4.163 3.369c-58.255 47.18-131.522 73.16-206.32 73.16-181.07 0-328.377-147.308-328.377-328.367 0-181.068 147.308-328.376 328.377-328.376 181.063 0 328.376 147.308 328.376 328.376 0 77.072-27.412 152.07-77.169 211.17l-3.522 4.173 162.719 162.744a25.846 25.846 0 0 1 7.639 18.432 26.081 26.081 0 0 1-26.051 26.045l-0.046-0.01zM495.13 205.957c-152.336 0-276.27 123.935-276.27 276.27 0 152.33 123.934 276.27 276.27 276.27 152.34 0 276.275-123.94 276.275-276.27 0-152.335-123.935-276.27-276.275-276.27z" fill="#ffffff" p-id="8117"></path><path d="M626.545 508.355h-262.83a26.127 26.127 0 0 1 0-52.255h262.83a26.127 26.127 0 0 1 0 52.255z" fill="#ffffff" p-id="8118"></path><path d="M495.13 639.77a26.127 26.127 0 0 1-26.128-26.128v-262.83a26.127 26.127 0 0 1 52.255 0v262.835a26.127 26.127 0 0 1-26.127 26.123z" fill="#ffffff" p-id="8119"></path></svg>
|
After Width: | Height: | Size: 1.3 KiB |
1
src/assets/svg/preview/unrotate.svg
Normal file
1
src/assets/svg/preview/unrotate.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1595306911635" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1352" width="48" height="48" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M924.8 337.6c-22.6-53.4-54.9-101.3-96-142.4s-89-73.4-142.4-96C631.1 75.9 572.5 64 512 64S392.9 75.9 337.6 99.2c-53.4 22.6-101.3 54.9-142.4 96-22.4 22.4-42.2 46.8-59.2 73.1V228c0-19.8-16.2-36-36-36s-36 16.2-36 36v288c0 19.8 16.2 36 36 36s36-16.2 36-36v-50.2c4.2-34.8 13.2-68.7 27-101.2 19.1-45.1 46.4-85.6 81.2-120.4C279 209.4 319.5 182 364.6 163c46.7-19.7 96.3-29.8 147.4-29.8 51.2 0 100.8 10 147.4 29.8 45.1 19.1 85.6 46.4 120.4 81.2C814.6 279 842 319.5 861 364.6c19.7 46.7 29.8 96.3 29.8 147.4 0 51.2-10 100.8-29.8 147.4-19.1 45.1-46.4 85.6-81.2 120.4C745 814.6 704.5 842 659.4 861c-46.7 19.7-96.3 29.8-147.4 29.8-64.6 0-128.4-16.5-184.4-47.8-54.4-30.4-100.9-74.1-134.6-126.6-10.3-16.1-31.7-20.8-47.8-10.4-16.1 10.3-20.8 31.7-10.4 47.8 39.8 62 94.8 113.7 159.1 149.6 66.2 37 141.7 56.6 218.1 56.6 60.5 0 119.1-11.9 174.4-35.2 53.4-22.6 101.3-54.9 142.4-96 41.1-41.1 73.4-89 96-142.4C948.1 631.1 960 572.5 960 512s-11.9-119.1-35.2-174.4z" p-id="1353" fill="#ffffff"></path><path d="M275.4 575.5c9.5-2.5 19.1 2.9 22.3 12.2 3.5 10.2 9.9 17.7 19.1 22.6 7.1 3.9 15.1 5.8 24 5.8 16.6 0 30.8-6.9 42.5-20.8 11.7-13.8 20-32.7 24.9-75.1-7.7 12.2-17.3 20.8-28.7 25.8-11.4 5-23.7 7.4-36.8 7.4-26.7 0-47.7-8.3-63.3-24.9-15.5-16.6-23.3-37.9-23.3-64.1 0-25.1 7.7-47.1 23-66.2 15.3-19 37.9-28.6 67.8-28.6 40.3 0 68.1 18.1 83.4 54.4 8.5 19.9 12.7 44.9 12.7 74.9 0 33.8-5.1 63.8-15.3 89.9-16.9 43.5-45.5 65.2-85.8 65.2-27 0-47.6-7.1-61.6-21.2-10-10.1-16.4-22-19.3-35.8-2-9.6 4-19.1 13.5-21.6l0.9 0.1z m103-74.4c9.4-7.5 14.1-20.6 14.1-39.3 0-16.8-4.2-29.3-12.7-37.5S360.6 412 347.5 412c-14 0-25.2 4.7-33.4 14.1-8.2 9.4-12.4 22-12.4 37.7 0 14.9 3.6 26.7 10.9 35.5 7.2 8.8 18.8 13.1 34.6 13.1 11.4 0 21.8-3.8 31.2-11.3zM646.6 414.4c12.4 22.8 18.5 54 18.5 93.7 0 37.6-5.6 68.7-16.8 93.3-16.2 35.3-42.8 52.9-79.6 52.9-33.2 0-57.9-14.4-74.2-43.3-13.5-24.1-20.3-56.4-20.3-97 0-31.4 4.1-58.4 12.2-80.9 15.2-42 42.7-63 82.5-63 35.9 0 61.8 14.8 77.7 44.3z m-40.2 173.3c9.4-13.9 14-39.9 14-78 0-27.4-3.4-50-10.1-67.7-6.8-17.7-19.9-26.6-39.4-26.6-17.9 0-31 8.4-39.3 25.2-8.3 16.8-12.4 41.6-12.4 74.3 0 24.6 2.6 44.4 7.9 59.4 8.1 22.8 22 34.3 41.6 34.3 15.7 0 28.3-7 37.7-20.9zM803.3 387.2c11.2 11.3 16.8 25 16.8 41.2 0 16.7-5.8 30.7-17.5 41.8C791 481.4 777.4 487 762 487c-17.1 0-31.2-5.8-42.1-17.4-10.9-11.6-16.4-25.1-16.4-40.6 0-16.5 5.8-30.4 17.3-41.7 11.5-11.3 25.3-17 41.2-17 16.3 0 30.1 5.7 41.3 16.9zM739.5 451c6.2 6.2 13.7 9.3 22.5 9.3 8.4 0 15.8-3.1 22.1-9.3 6.3-6.2 9.4-13.7 9.4-22.6 0-8.5-3.1-15.9-9.3-22.1-6.2-6.2-13.6-9.3-22.2-9.3s-16.1 3.1-22.4 9.3c-6.3 6.2-9.4 13.7-9.4 22.6-0.1 8.4 3 15.8 9.3 22.1z" p-id="1354" fill="#ffffff"></path></svg>
|
After Width: | Height: | Size: 2.9 KiB |
1
src/assets/svg/preview/unscale.svg
Normal file
1
src/assets/svg/preview/unscale.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1595308005241" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9878" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><defs><style type="text/css"></style></defs><path d="M750.3 198.7C598 46.4 351.1 46.4 198.7 198.7s-152.3 399.2 0 551.5C345.1 896.6 578.8 902.3 732 767.3l172.1 172.1 35.4-35.4-172.1-171.9c135-153.2 129.3-387-17.1-533.4z m39.3 403.8c-17.1 42.1-42.2 80-74.7 112.4-32.5 32.5-70.3 57.6-112.4 74.7-40.7 16.5-83.8 24.9-128 24.9s-87.2-8.4-128-24.9c-42.1-17.1-80-42.2-112.4-74.7s-57.6-70.3-74.7-112.4c-16.5-40.7-24.9-83.8-24.9-128s8.4-87.2 24.9-128c17.1-42.1 42.2-80 74.7-112.4s70.3-57.6 112.4-74.7c40.7-16.5 83.8-24.9 128-24.9s87.2 8.4 128 24.9c42.1 17.1 80 42.2 112.4 74.7 32.5 32.5 57.6 70.3 74.7 112.4 16.5 40.7 24.9 83.8 24.9 128s-8.4 87.3-24.9 128zM671 502H271v-50h400v50z" fill="#ffffff" p-id="9879"></path></svg>
|
After Width: | Height: | Size: 1.0 KiB |
@ -4,7 +4,7 @@
|
|||||||
<div :class="getClass" @click.stop v-if="visible">
|
<div :class="getClass" @click.stop v-if="visible">
|
||||||
<div :class="`${prefixCls}-content`" v-click-outside="handleClose">
|
<div :class="`${prefixCls}-content`" v-click-outside="handleClose">
|
||||||
<div :class="`${prefixCls}-input__wrapper`">
|
<div :class="`${prefixCls}-input__wrapper`">
|
||||||
<Input
|
<a-input
|
||||||
:class="`${prefixCls}-input`"
|
:class="`${prefixCls}-input`"
|
||||||
:placeholder="t('common.searchText')"
|
:placeholder="t('common.searchText')"
|
||||||
ref="inputRef"
|
ref="inputRef"
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<template #prefix>
|
<template #prefix>
|
||||||
<SearchOutlined />
|
<SearchOutlined />
|
||||||
</template>
|
</template>
|
||||||
</Input>
|
</a-input>
|
||||||
<span :class="`${prefixCls}-cancel`" @click="handleClose">
|
<span :class="`${prefixCls}-cancel`" @click="handleClose">
|
||||||
{{ t('common.cancelText') }}
|
{{ t('common.cancelText') }}
|
||||||
</span>
|
</span>
|
||||||
@ -59,7 +59,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, computed, unref, ref, watch, nextTick } from 'vue';
|
import { defineComponent, computed, unref, ref, watch, nextTick } from 'vue';
|
||||||
import { SearchOutlined } from '@ant-design/icons-vue';
|
import { SearchOutlined } from '@ant-design/icons-vue';
|
||||||
import { Input } from 'ant-design-vue';
|
|
||||||
import AppSearchFooter from './AppSearchFooter.vue';
|
import AppSearchFooter from './AppSearchFooter.vue';
|
||||||
import Icon from '/@/components/Icon';
|
import Icon from '/@/components/Icon';
|
||||||
import clickOutside from '/@/directives/clickOutside';
|
import clickOutside from '/@/directives/clickOutside';
|
||||||
@ -75,7 +74,7 @@
|
|||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'AppSearchModal',
|
name: 'AppSearchModal',
|
||||||
components: { Icon, SearchOutlined, AppSearchFooter, Input },
|
components: { Icon, SearchOutlined, AppSearchFooter },
|
||||||
directives: {
|
directives: {
|
||||||
clickOutside,
|
clickOutside,
|
||||||
},
|
},
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<AInput v-bind="$attrs" :class="prefixCls" :size="size" :value="state">
|
<a-input v-bind="$attrs" :class="prefixCls" :size="size" :value="state">
|
||||||
<template #addonAfter>
|
<template #addonAfter>
|
||||||
<CountButton :size="size" :count="count" :value="state" :beforeStartFunc="sendCodeApi" />
|
<CountButton :size="size" :count="count" :value="state" :beforeStartFunc="sendCodeApi" />
|
||||||
</template>
|
</template>
|
||||||
</AInput>
|
</a-input>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, PropType } from 'vue';
|
import { defineComponent, PropType } from 'vue';
|
||||||
import { Input } from 'ant-design-vue';
|
|
||||||
import CountButton from './CountButton.vue';
|
import CountButton from './CountButton.vue';
|
||||||
import { useDesign } from '/@/hooks/web/useDesign';
|
import { useDesign } from '/@/hooks/web/useDesign';
|
||||||
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
|
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
|
||||||
@ -24,7 +23,7 @@
|
|||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'CountDownInput',
|
name: 'CountDownInput',
|
||||||
components: { [Input.name]: Input, CountButton },
|
components: { CountButton },
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props,
|
props,
|
||||||
setup(props) {
|
setup(props) {
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import type Cropper from 'cropperjs';
|
import { withInstall } from '/@/utils';
|
||||||
|
import cropperImage from './src/Cropper.vue';
|
||||||
|
import avatarCropper from './src/CropperAvatar.vue';
|
||||||
|
|
||||||
export type { Cropper };
|
export * from './src/typing';
|
||||||
export { default as CropperImage } from './src/Cropper.vue';
|
export const CropperImage = withInstall(cropperImage);
|
||||||
|
export const CropperAvatar = withInstall(avatarCropper);
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div :class="$attrs.class" :style="$attrs.style"> </div>
|
|
||||||
</template>
|
|
||||||
<script lang="ts">
|
|
||||||
// TODO
|
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'AvatarCropper',
|
|
||||||
props: {},
|
|
||||||
setup() {
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
258
src/components/Cropper/src/CopperModal.vue
Normal file
258
src/components/Cropper/src/CopperModal.vue
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
<template>
|
||||||
|
<BasicModal
|
||||||
|
v-bind="$attrs"
|
||||||
|
@register="register"
|
||||||
|
:title="t('component.cropper.modalTitle')"
|
||||||
|
width="800px"
|
||||||
|
:canFullscreen="false"
|
||||||
|
@ok="handleOk"
|
||||||
|
:okText="t('component.cropper.okText')"
|
||||||
|
>
|
||||||
|
<div :class="prefixCls">
|
||||||
|
<div :class="`${prefixCls}-left`">
|
||||||
|
<div :class="`${prefixCls}-cropper`">
|
||||||
|
<CropperImage
|
||||||
|
v-if="src"
|
||||||
|
:src="src"
|
||||||
|
height="300px"
|
||||||
|
:circled="circled"
|
||||||
|
@cropend="handleCropend"
|
||||||
|
@ready="handleReady"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div :class="`${prefixCls}-toolbar`">
|
||||||
|
<Upload :fileList="[]" accept="image/*" :beforeUpload="handleBeforeUpload">
|
||||||
|
<a-button size="small" preIcon="ant-design:upload-outlined" type="primary" />
|
||||||
|
</Upload>
|
||||||
|
<Space>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
preIcon="ant-design:reload-outlined"
|
||||||
|
size="small"
|
||||||
|
@click="handlerToolbar('reset')"
|
||||||
|
/>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
preIcon="ant-design:rotate-left-outlined"
|
||||||
|
size="small"
|
||||||
|
@click="handlerToolbar('rotate', -45)"
|
||||||
|
/>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
preIcon="ant-design:rotate-right-outlined"
|
||||||
|
size="small"
|
||||||
|
@click="handlerToolbar('rotate', 45)"
|
||||||
|
/>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
preIcon="vaadin:arrows-long-h"
|
||||||
|
size="small"
|
||||||
|
@click="handlerToolbar('scaleX')"
|
||||||
|
/>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
preIcon="vaadin:arrows-long-v"
|
||||||
|
size="small"
|
||||||
|
@click="handlerToolbar('scaleY')"
|
||||||
|
/>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
preIcon="ant-design:zoom-in-outlined"
|
||||||
|
size="small"
|
||||||
|
@click="handlerToolbar('zoom', 0.1)"
|
||||||
|
/>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
preIcon="ant-design:zoom-out-outlined"
|
||||||
|
size="small"
|
||||||
|
@click="handlerToolbar('zoom', -0.1)"
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div :class="`${prefixCls}-right`">
|
||||||
|
<div :class="`${prefixCls}-preview`">
|
||||||
|
<img :src="previewSource" v-if="previewSource" />
|
||||||
|
</div>
|
||||||
|
<template v-if="previewSource">
|
||||||
|
<div :class="`${prefixCls}-group`">
|
||||||
|
<Avatar :src="previewSource" size="large" />
|
||||||
|
<Avatar :src="previewSource" :size="48" />
|
||||||
|
<Avatar :src="previewSource" :size="64" />
|
||||||
|
<Avatar :src="previewSource" :size="80" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</BasicModal>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import type { CropendResult, Cropper } from './typing';
|
||||||
|
|
||||||
|
import { defineComponent, ref } from 'vue';
|
||||||
|
import CropperImage from './Cropper.vue';
|
||||||
|
import { Space, Upload, Avatar } from 'ant-design-vue';
|
||||||
|
import { useDesign } from '/@/hooks/web/useDesign';
|
||||||
|
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||||
|
import { dataURLtoBlob } from '/@/utils/file/base64Conver';
|
||||||
|
import { isFunction } from '/@/utils/is';
|
||||||
|
import { useI18n } from '/@/hooks/web/useI18n';
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
circled: { type: Boolean, default: true },
|
||||||
|
uploadApi: {
|
||||||
|
type: Function as PropType<({ file: Blob, name: stirng, filename: string }) => Promise<any>>,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'CropperAvatar',
|
||||||
|
components: { BasicModal, Space, CropperImage, Upload, Avatar },
|
||||||
|
props,
|
||||||
|
emits: ['uploadSuccess', 'register'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
let filename = '';
|
||||||
|
const src = ref('');
|
||||||
|
const previewSource = ref('');
|
||||||
|
const cropper = ref<Cropper>();
|
||||||
|
let scaleX = 1;
|
||||||
|
let scaleY = 1;
|
||||||
|
|
||||||
|
const { prefixCls } = useDesign('cropper-am');
|
||||||
|
const [register, { closeModal, setModalProps }] = useModalInner();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
// Block upload
|
||||||
|
function handleBeforeUpload(file: File) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
src.value = '';
|
||||||
|
previewSource.value = '';
|
||||||
|
reader.onload = function (e) {
|
||||||
|
src.value = (e.target?.result as string) ?? '';
|
||||||
|
filename = file.name;
|
||||||
|
};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCropend({ imgBase64 }: CropendResult) {
|
||||||
|
previewSource.value = imgBase64;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleReady(cropperInstance: Cropper) {
|
||||||
|
cropper.value = cropperInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlerToolbar(event: string, arg?: number) {
|
||||||
|
if (event === 'scaleX') {
|
||||||
|
scaleX = arg = scaleX === -1 ? 1 : -1;
|
||||||
|
}
|
||||||
|
if (event === 'scaleY') {
|
||||||
|
scaleY = arg = scaleY === -1 ? 1 : -1;
|
||||||
|
}
|
||||||
|
cropper?.value?.[event]?.(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleOk() {
|
||||||
|
const uploadApi = props.uploadApi;
|
||||||
|
if (uploadApi && isFunction(uploadApi)) {
|
||||||
|
const blob = dataURLtoBlob(previewSource.value);
|
||||||
|
try {
|
||||||
|
setModalProps({ confirmLoading: true });
|
||||||
|
const result = await uploadApi({ name: 'file', file: blob, filename });
|
||||||
|
emit('uploadSuccess', { source: previewSource.value, data: result.data });
|
||||||
|
closeModal();
|
||||||
|
} finally {
|
||||||
|
setModalProps({ confirmLoading: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
t,
|
||||||
|
prefixCls,
|
||||||
|
src,
|
||||||
|
register,
|
||||||
|
previewSource,
|
||||||
|
handleBeforeUpload,
|
||||||
|
handleCropend,
|
||||||
|
handleReady,
|
||||||
|
handlerToolbar,
|
||||||
|
handleOk,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
@prefix-cls: ~'@{namespace}-cropper-am';
|
||||||
|
|
||||||
|
.@{prefix-cls} {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&-left,
|
||||||
|
&-right {
|
||||||
|
height: 340px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-left {
|
||||||
|
width: 55%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-right {
|
||||||
|
width: 45%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-cropper {
|
||||||
|
height: 300px;
|
||||||
|
background: #eee;
|
||||||
|
background-image: linear-gradient(
|
||||||
|
45deg,
|
||||||
|
rgba(0, 0, 0, 0.25) 25%,
|
||||||
|
transparent 0,
|
||||||
|
transparent 75%,
|
||||||
|
rgba(0, 0, 0, 0.25) 0
|
||||||
|
),
|
||||||
|
linear-gradient(
|
||||||
|
45deg,
|
||||||
|
rgba(0, 0, 0, 0.25) 25%,
|
||||||
|
transparent 0,
|
||||||
|
transparent 75%,
|
||||||
|
rgba(0, 0, 0, 0.25) 0
|
||||||
|
);
|
||||||
|
background-position: 0 0, 12px 12px;
|
||||||
|
background-size: 24px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-preview {
|
||||||
|
width: 220px;
|
||||||
|
height: 220px;
|
||||||
|
margin: 0 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid @border-color-base;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-group {
|
||||||
|
display: flex;
|
||||||
|
padding-top: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
|
border-top: 1px solid @border-color-base;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="$attrs.class" :style="getWrapperStyle">
|
<div :class="getClass" :style="getWrapperStyle">
|
||||||
<img
|
<img
|
||||||
v-show="isReady"
|
v-show="isReady"
|
||||||
ref="imgElRef"
|
ref="imgElRef"
|
||||||
@ -12,16 +12,16 @@
|
|||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { CSSProperties } from 'vue';
|
import type { CSSProperties } from 'vue';
|
||||||
|
|
||||||
import { defineComponent, onMounted, ref, unref, computed } from 'vue';
|
import { defineComponent, onMounted, ref, unref, computed } from 'vue';
|
||||||
|
|
||||||
import Cropper from 'cropperjs';
|
import Cropper from 'cropperjs';
|
||||||
import 'cropperjs/dist/cropper.css';
|
import 'cropperjs/dist/cropper.css';
|
||||||
|
import { useDesign } from '/@/hooks/web/useDesign';
|
||||||
|
import { useDebounceFn } from '@vueuse/shared';
|
||||||
|
|
||||||
type Options = Cropper.Options;
|
type Options = Cropper.Options;
|
||||||
|
|
||||||
const defaultOptions: Options = {
|
const defaultOptions: Options = {
|
||||||
aspectRatio: 16 / 9,
|
aspectRatio: 1,
|
||||||
zoomable: true,
|
zoomable: true,
|
||||||
zoomOnTouch: true,
|
zoomOnTouch: true,
|
||||||
zoomOnWheel: true,
|
zoomOnWheel: true,
|
||||||
@ -43,40 +43,32 @@
|
|||||||
rotatable: true,
|
rotatable: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
src: { type: String, required: true },
|
||||||
|
alt: { type: String },
|
||||||
|
circled: { type: Boolean, default: false },
|
||||||
|
realTimePreview: { type: Boolean, default: true },
|
||||||
|
height: { type: [String, Number], default: '360px' },
|
||||||
|
crossorigin: {
|
||||||
|
type: String as PropType<'' | 'anonymous' | 'use-credentials' | undefined>,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
imageStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
|
||||||
|
options: { type: Object as PropType<Options>, default: () => ({}) },
|
||||||
|
};
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'CropperImage',
|
name: 'CropperImage',
|
||||||
props: {
|
props,
|
||||||
src: {
|
emits: ['cropend', 'ready', 'cropendError'],
|
||||||
type: String,
|
setup(props, { attrs, emit }) {
|
||||||
required: true,
|
const imgElRef = ref<ElRef<HTMLImageElement>>();
|
||||||
},
|
const cropper = ref<Nullable<Cropper>>();
|
||||||
alt: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: [String, Number],
|
|
||||||
default: '360px',
|
|
||||||
},
|
|
||||||
crossorigin: {
|
|
||||||
type: String as PropType<'' | 'anonymous' | 'use-credentials' | undefined>,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
imageStyle: {
|
|
||||||
type: Object as PropType<CSSProperties>,
|
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
type: Object as PropType<Options>,
|
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
emits: ['cropperedInfo'],
|
|
||||||
setup(props, ctx) {
|
|
||||||
const imgElRef = ref<ElRef<HTMLImageElement>>(null);
|
|
||||||
const cropper: any = ref<Nullable<Cropper>>(null);
|
|
||||||
|
|
||||||
const isReady = ref(false);
|
const isReady = ref(false);
|
||||||
|
|
||||||
|
const { prefixCls } = useDesign('cropper-image');
|
||||||
|
const debounceRealTimeCroppered = useDebounceFn(realTimeCroppered, 80);
|
||||||
|
|
||||||
const getImageStyle = computed((): CSSProperties => {
|
const getImageStyle = computed((): CSSProperties => {
|
||||||
return {
|
return {
|
||||||
height: props.height,
|
height: props.height,
|
||||||
@ -85,11 +77,22 @@
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const getWrapperStyle = computed((): CSSProperties => {
|
const getClass = computed(() => {
|
||||||
const { height } = props;
|
return [
|
||||||
return { height: `${height}`.replace(/px/, '') + 'px' };
|
prefixCls,
|
||||||
|
attrs.class,
|
||||||
|
{
|
||||||
|
[`${prefixCls}--circled`]: props.circled,
|
||||||
|
},
|
||||||
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getWrapperStyle = computed((): CSSProperties => {
|
||||||
|
return { height: `${props.height}`.replace(/px/, '') + 'px' };
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(init);
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const imgEl = unref(imgElRef);
|
const imgEl = unref(imgElRef);
|
||||||
if (!imgEl) {
|
if (!imgEl) {
|
||||||
@ -99,29 +102,83 @@
|
|||||||
...defaultOptions,
|
...defaultOptions,
|
||||||
ready: () => {
|
ready: () => {
|
||||||
isReady.value = true;
|
isReady.value = true;
|
||||||
|
realTimeCroppered();
|
||||||
|
emit('ready', cropper.value);
|
||||||
|
},
|
||||||
|
crop() {
|
||||||
|
debounceRealTimeCroppered();
|
||||||
|
},
|
||||||
|
zoom() {
|
||||||
|
debounceRealTimeCroppered();
|
||||||
|
},
|
||||||
|
cropmove() {
|
||||||
|
debounceRealTimeCroppered();
|
||||||
},
|
},
|
||||||
...props.options,
|
...props.options,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Real-time display preview
|
||||||
|
function realTimeCroppered() {
|
||||||
|
props.realTimePreview && croppered();
|
||||||
|
}
|
||||||
|
|
||||||
// event: return base64 and width and height information after cropping
|
// event: return base64 and width and height information after cropping
|
||||||
const croppered = (): void => {
|
function croppered() {
|
||||||
|
if (!cropper.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let imgInfo = cropper.value.getData();
|
let imgInfo = cropper.value.getData();
|
||||||
cropper.value.getCroppedCanvas().toBlob((blob) => {
|
const canvas = props.circled ? getRoundedCanvas() : cropper.value.getCroppedCanvas();
|
||||||
|
canvas.toBlob((blob) => {
|
||||||
|
if (!blob) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let fileReader: FileReader = new FileReader();
|
let fileReader: FileReader = new FileReader();
|
||||||
|
fileReader.readAsDataURL(blob);
|
||||||
fileReader.onloadend = (e) => {
|
fileReader.onloadend = (e) => {
|
||||||
ctx.emit('cropperedInfo', {
|
emit('cropend', {
|
||||||
imgBase64: e.target?.result ?? '',
|
imgBase64: e.target?.result ?? '',
|
||||||
imgInfo,
|
imgInfo,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
fileReader.readAsDataURL(blob);
|
fileReader.onerror = () => {
|
||||||
}, 'image/jpeg');
|
emit('cropendError');
|
||||||
};
|
};
|
||||||
|
}, 'image/png');
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(init);
|
// Get a circular picture canvas
|
||||||
|
function getRoundedCanvas() {
|
||||||
|
const sourceCanvas = cropper.value!.getCroppedCanvas();
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const context = canvas.getContext('2d')!;
|
||||||
|
const width = sourceCanvas.width;
|
||||||
|
const height = sourceCanvas.height;
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
context.imageSmoothingEnabled = true;
|
||||||
|
context.drawImage(sourceCanvas, 0, 0, width, height);
|
||||||
|
context.globalCompositeOperation = 'destination-in';
|
||||||
|
context.beginPath();
|
||||||
|
context.arc(width / 2, height / 2, Math.min(width, height) / 2, 0, 2 * Math.PI, true);
|
||||||
|
context.fill();
|
||||||
|
return canvas;
|
||||||
|
}
|
||||||
|
|
||||||
return { imgElRef, getWrapperStyle, getImageStyle, isReady, croppered };
|
return { getClass, imgElRef, getWrapperStyle, getImageStyle, isReady, croppered };
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<style lang="less">
|
||||||
|
@prefix-cls: ~'@{namespace}-cropper-image';
|
||||||
|
|
||||||
|
.@{prefix-cls} {
|
||||||
|
&--circled {
|
||||||
|
.cropper-view-box,
|
||||||
|
.cropper-face {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
89
src/components/Cropper/src/CropperAvatar.vue
Normal file
89
src/components/Cropper/src/CropperAvatar.vue
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<template>
|
||||||
|
<div :class="getClass" :style="getStyle">
|
||||||
|
<div :class="`${prefixCls}-image-wrapper`" :style="getImageWrapperStyle" @click="openModal">
|
||||||
|
<img :src="sourceValue" v-if="sourceValue" alt="avatar" />
|
||||||
|
</div>
|
||||||
|
<a-button :class="`${prefixCls}-upload-btn`" @click="openModal">
|
||||||
|
{{ t('component.cropper.selectImage') }}
|
||||||
|
</a-button>
|
||||||
|
<CopperModal @register="register" @uploadSuccess="handleUploadSuccess" :uploadApi="uploadApi" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, computed, CSSProperties, unref, ref } from 'vue';
|
||||||
|
import CopperModal from './CopperModal.vue';
|
||||||
|
import { useDesign } from '/@/hooks/web/useDesign';
|
||||||
|
import { useModal } from '/@/components/Modal';
|
||||||
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
|
import { useI18n } from '/@/hooks/web/useI18n';
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
width: { type: [String, Number], default: '200px' },
|
||||||
|
uploadApi: { type: Function as PropType<({ file: Blob, name: string }) => Promise<void>> },
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'CropperAvatar',
|
||||||
|
components: { CopperModal },
|
||||||
|
props,
|
||||||
|
setup(props) {
|
||||||
|
const sourceValue = ref('');
|
||||||
|
const { prefixCls } = useDesign('cropper-avatar');
|
||||||
|
const [register, { openModal }] = useModal();
|
||||||
|
const { createMessage } = useMessage();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const getClass = computed(() => [prefixCls]);
|
||||||
|
|
||||||
|
const getWidth = computed(() => `${props.width}`.replace(/px/, '') + 'px');
|
||||||
|
|
||||||
|
const getStyle = computed((): CSSProperties => ({ width: unref(getWidth) }));
|
||||||
|
|
||||||
|
const getImageWrapperStyle = computed(
|
||||||
|
(): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) })
|
||||||
|
);
|
||||||
|
|
||||||
|
function handleUploadSuccess({ source }) {
|
||||||
|
sourceValue.value = source;
|
||||||
|
createMessage.success(t('component.cropper.uploadSuccess'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
t,
|
||||||
|
prefixCls,
|
||||||
|
register,
|
||||||
|
openModal,
|
||||||
|
sourceValue,
|
||||||
|
getClass,
|
||||||
|
getImageWrapperStyle,
|
||||||
|
getStyle,
|
||||||
|
handleUploadSuccess,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@prefix-cls: ~'@{namespace}-cropper-avatar';
|
||||||
|
|
||||||
|
.@{prefix-cls} {
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&-image-wrapper {
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
background: @component-background;
|
||||||
|
border: 1px solid @border-color-base;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-upload-btn {
|
||||||
|
margin: 10px auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
8
src/components/Cropper/src/typing.ts
Normal file
8
src/components/Cropper/src/typing.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import type Cropper from 'cropperjs';
|
||||||
|
|
||||||
|
export interface CropendResult {
|
||||||
|
imgBase64: string;
|
||||||
|
imgInfo: Cropper.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type { Cropper };
|
@ -1,5 +1,6 @@
|
|||||||
import Description from './src/Description.vue';
|
import { withInstall } from '/@/utils';
|
||||||
|
import description from './src/Description.vue';
|
||||||
|
|
||||||
export { Description };
|
export * from './src/typing';
|
||||||
export * from './src/types';
|
|
||||||
export { useDescription } from './src/useDescription';
|
export { useDescription } from './src/useDescription';
|
||||||
|
export const Description = withInstall(description);
|
||||||
|
@ -1,28 +1,49 @@
|
|||||||
<script lang="tsx">
|
<script lang="tsx">
|
||||||
import type { DescOptions, DescInstance, DescItem } from './types';
|
import type { DescriptionProps, DescInstance, DescItem } from './typing';
|
||||||
import type { DescriptionsProps } from 'ant-design-vue/es/descriptions/index';
|
import type { DescriptionsProps } from 'ant-design-vue/es/descriptions/index';
|
||||||
import type { CSSProperties } from 'vue';
|
import type { CSSProperties } from 'vue';
|
||||||
import type { CollapseContainerOptions } from '/@/components/Container/index';
|
import type { CollapseContainerOptions } from '/@/components/Container/index';
|
||||||
|
|
||||||
import { defineComponent, computed, ref, unref } from 'vue';
|
import { defineComponent, computed, ref, unref } from 'vue';
|
||||||
import { get } from 'lodash-es';
|
import { get } from 'lodash-es';
|
||||||
import { Descriptions } from 'ant-design-vue';
|
import { Descriptions } from 'ant-design-vue';
|
||||||
import { CollapseContainer } from '/@/components/Container/index';
|
import { CollapseContainer } from '/@/components/Container/index';
|
||||||
|
|
||||||
import { useDesign } from '/@/hooks/web/useDesign';
|
import { useDesign } from '/@/hooks/web/useDesign';
|
||||||
|
|
||||||
import { isFunction } from '/@/utils/is';
|
import { isFunction } from '/@/utils/is';
|
||||||
import { getSlot } from '/@/utils/helper/tsxHelper';
|
import { getSlot } from '/@/utils/helper/tsxHelper';
|
||||||
|
|
||||||
import descProps from './props';
|
|
||||||
import { useAttrs } from '/@/hooks/core/useAttrs';
|
import { useAttrs } from '/@/hooks/core/useAttrs';
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
useCollapse: { type: Boolean, default: true },
|
||||||
|
title: { type: String, default: '' },
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
validator: (v) => ['small', 'default', 'middle', undefined].includes(v),
|
||||||
|
default: 'small',
|
||||||
|
},
|
||||||
|
bordered: { type: Boolean, default: true },
|
||||||
|
column: {
|
||||||
|
type: [Number, Object] as PropType<number | Recordable>,
|
||||||
|
default: () => {
|
||||||
|
return { xxl: 4, xl: 3, lg: 3, md: 3, sm: 2, xs: 1 };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
collapseOptions: {
|
||||||
|
type: Object as PropType<CollapseContainerOptions>,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
type: Array as PropType<DescItem[]>,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
data: { type: Object },
|
||||||
|
};
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Description',
|
name: 'Description',
|
||||||
props: descProps,
|
props,
|
||||||
emits: ['register'],
|
emits: ['register'],
|
||||||
setup(props, { slots, emit }) {
|
setup(props, { slots, emit }) {
|
||||||
const propsRef = ref<Partial<DescOptions> | null>(null);
|
const propsRef = ref<Partial<DescriptionProps> | null>(null);
|
||||||
|
|
||||||
const { prefixCls } = useDesign('description');
|
const { prefixCls } = useDesign('description');
|
||||||
const attrs = useAttrs();
|
const attrs = useAttrs();
|
||||||
@ -32,7 +53,7 @@
|
|||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
...(unref(propsRef) as Recordable),
|
...(unref(propsRef) as Recordable),
|
||||||
} as DescOptions;
|
} as DescriptionProps;
|
||||||
});
|
});
|
||||||
|
|
||||||
const getProps = computed(() => {
|
const getProps = computed(() => {
|
||||||
@ -40,7 +61,7 @@
|
|||||||
...unref(getMergeProps),
|
...unref(getMergeProps),
|
||||||
title: undefined,
|
title: undefined,
|
||||||
};
|
};
|
||||||
return opt as DescOptions;
|
return opt as DescriptionProps;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,7 +87,7 @@
|
|||||||
/**
|
/**
|
||||||
* @description:设置desc
|
* @description:设置desc
|
||||||
*/
|
*/
|
||||||
function setDescProps(descProps: Partial<DescOptions>): void {
|
function setDescProps(descProps: Partial<DescriptionProps>): void {
|
||||||
// Keep the last setDrawerProps
|
// Keep the last setDrawerProps
|
||||||
propsRef.value = { ...(unref(propsRef) as Recordable), ...descProps } as Recordable;
|
propsRef.value = { ...(unref(propsRef) as Recordable), ...descProps } as Recordable;
|
||||||
}
|
}
|
||||||
@ -79,7 +100,6 @@
|
|||||||
|
|
||||||
const labelStyles: CSSProperties = {
|
const labelStyles: CSSProperties = {
|
||||||
...labelStyle,
|
...labelStyle,
|
||||||
|
|
||||||
minWidth: `${labelMinWidth}px `,
|
minWidth: `${labelMinWidth}px `,
|
||||||
};
|
};
|
||||||
return <div style={labelStyles}>{label}</div>;
|
return <div style={labelStyles}>{label}</div>;
|
||||||
@ -97,7 +117,9 @@
|
|||||||
|
|
||||||
const getContent = () => {
|
const getContent = () => {
|
||||||
const _data = unref(getProps)?.data;
|
const _data = unref(getProps)?.data;
|
||||||
if (!_data) return null;
|
if (!_data) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const getField = get(_data, field);
|
const getField = get(_data, field);
|
||||||
return isFunction(render) ? render(getField, _data) : getField ?? '';
|
return isFunction(render) ? render(getField, _data) : getField ?? '';
|
||||||
};
|
};
|
||||||
@ -131,7 +153,6 @@
|
|||||||
const renderContainer = () => {
|
const renderContainer = () => {
|
||||||
const content = props.useCollapse ? renderDesc() : <div>{renderDesc()}</div>;
|
const content = props.useCollapse ? renderDesc() : <div>{renderDesc()}</div>;
|
||||||
// Reduce the dom level
|
// Reduce the dom level
|
||||||
|
|
||||||
if (!props.useCollapse) {
|
if (!props.useCollapse) {
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
import type { PropType } from 'vue';
|
|
||||||
import type { CollapseContainerOptions } from '/@/components/Container';
|
|
||||||
import type { DescItem } from './types';
|
|
||||||
import { propTypes } from '/@/utils/propTypes';
|
|
||||||
export default {
|
|
||||||
useCollapse: propTypes.bool.def(true),
|
|
||||||
title: propTypes.string.def(''),
|
|
||||||
size: propTypes.oneOf(['small', 'default', 'middle', undefined]).def('small'),
|
|
||||||
bordered: propTypes.bool.def(true),
|
|
||||||
column: {
|
|
||||||
type: [Number, Object] as PropType<number | Recordable>,
|
|
||||||
default: () => {
|
|
||||||
return { xxl: 4, xl: 3, lg: 3, md: 3, sm: 2, xs: 1 };
|
|
||||||
},
|
|
||||||
},
|
|
||||||
collapseOptions: {
|
|
||||||
type: Object as PropType<CollapseContainerOptions>,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
schema: {
|
|
||||||
type: Array as PropType<Array<DescItem>>,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
data: propTypes.object,
|
|
||||||
};
|
|
@ -4,11 +4,8 @@ import type { DescriptionsProps } from 'ant-design-vue/es/descriptions/index';
|
|||||||
|
|
||||||
export interface DescItem {
|
export interface DescItem {
|
||||||
labelMinWidth?: number;
|
labelMinWidth?: number;
|
||||||
|
|
||||||
contentMinWidth?: number;
|
contentMinWidth?: number;
|
||||||
|
|
||||||
labelStyle?: CSSProperties;
|
labelStyle?: CSSProperties;
|
||||||
|
|
||||||
field: string;
|
field: string;
|
||||||
label: string | VNode | JSX.Element;
|
label: string | VNode | JSX.Element;
|
||||||
// Merge column
|
// Merge column
|
||||||
@ -21,7 +18,7 @@ export interface DescItem {
|
|||||||
) => VNode | undefined | JSX.Element | Element | string | number;
|
) => VNode | undefined | JSX.Element | Element | string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DescOptions extends DescriptionsProps {
|
export interface DescriptionProps extends DescriptionsProps {
|
||||||
// Whether to include the collapse component
|
// Whether to include the collapse component
|
||||||
useCollapse?: boolean;
|
useCollapse?: boolean;
|
||||||
/**
|
/**
|
||||||
@ -42,7 +39,7 @@ export interface DescOptions extends DescriptionsProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DescInstance {
|
export interface DescInstance {
|
||||||
setDescProps(descProps: Partial<DescOptions>): void;
|
setDescProps(descProps: Partial<DescriptionProps>): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Register = (descInstance: DescInstance) => void;
|
export type Register = (descInstance: DescInstance) => void;
|
@ -1,25 +1,26 @@
|
|||||||
|
import type { DescriptionProps, DescInstance, UseDescReturnType } from './typing';
|
||||||
import { ref, getCurrentInstance, unref } from 'vue';
|
import { ref, getCurrentInstance, unref } from 'vue';
|
||||||
import { isProdMode } from '/@/utils/env';
|
import { isProdMode } from '/@/utils/env';
|
||||||
|
|
||||||
import type { DescOptions, DescInstance, UseDescReturnType } from './types';
|
export function useDescription(props?: Partial<DescriptionProps>): UseDescReturnType {
|
||||||
|
|
||||||
export function useDescription(props?: Partial<DescOptions>): UseDescReturnType {
|
|
||||||
if (!getCurrentInstance()) {
|
if (!getCurrentInstance()) {
|
||||||
throw new Error('Please put useDescription function in the setup function!');
|
throw new Error('useDescription() can only be used inside setup() or functional components!');
|
||||||
}
|
}
|
||||||
const descRef = ref<Nullable<DescInstance>>(null);
|
const desc = ref<Nullable<DescInstance>>(null);
|
||||||
const loadedRef = ref(false);
|
const loaded = ref(false);
|
||||||
|
|
||||||
function register(instance: DescInstance) {
|
function register(instance: DescInstance) {
|
||||||
if (unref(loadedRef) && isProdMode()) return;
|
if (unref(loaded) && isProdMode()) {
|
||||||
descRef.value = instance;
|
return;
|
||||||
|
}
|
||||||
|
desc.value = instance;
|
||||||
props && instance.setDescProps(props);
|
props && instance.setDescProps(props);
|
||||||
loadedRef.value = true;
|
loaded.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const methods: DescInstance = {
|
const methods: DescInstance = {
|
||||||
setDescProps: (descProps: Partial<DescOptions>): void => {
|
setDescProps: (descProps: Partial<DescriptionProps>): void => {
|
||||||
unref(descRef)?.setDescProps(descProps);
|
unref(desc)?.setDescProps(descProps);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import BasicDrawer from './src/BasicDrawer.vue';
|
import { withInstall } from '/@/utils';
|
||||||
|
import basicDrawer from './src/BasicDrawer.vue';
|
||||||
|
|
||||||
export { BasicDrawer };
|
export const BasicDrawer = withInstall(basicDrawer);
|
||||||
export * from './src/types';
|
export * from './src/typing';
|
||||||
export { useDrawer, useDrawerInner } from './src/useDrawer';
|
export { useDrawer, useDrawerInner } from './src/useDrawer';
|
||||||
|
@ -31,9 +31,8 @@
|
|||||||
</Drawer>
|
</Drawer>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { DrawerInstance, DrawerProps } from './types';
|
import type { DrawerInstance, DrawerProps } from './typing';
|
||||||
import type { CSSProperties } from 'vue';
|
import type { CSSProperties } from 'vue';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
defineComponent,
|
defineComponent,
|
||||||
ref,
|
ref,
|
||||||
@ -46,15 +45,12 @@
|
|||||||
getCurrentInstance,
|
getCurrentInstance,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import { Drawer } from 'ant-design-vue';
|
import { Drawer } from 'ant-design-vue';
|
||||||
|
|
||||||
import { useI18n } from '/@/hooks/web/useI18n';
|
import { useI18n } from '/@/hooks/web/useI18n';
|
||||||
|
|
||||||
import { isFunction, isNumber } from '/@/utils/is';
|
import { isFunction, isNumber } from '/@/utils/is';
|
||||||
import { deepMerge } from '/@/utils';
|
import { deepMerge } from '/@/utils';
|
||||||
import DrawerFooter from './components/DrawerFooter.vue';
|
import DrawerFooter from './components/DrawerFooter.vue';
|
||||||
import DrawerHeader from './components/DrawerHeader.vue';
|
import DrawerHeader from './components/DrawerHeader.vue';
|
||||||
import { ScrollContainer } from '/@/components/Container';
|
import { ScrollContainer } from '/@/components/Container';
|
||||||
|
|
||||||
import { basicProps } from './props';
|
import { basicProps } from './props';
|
||||||
import { useDesign } from '/@/hooks/web/useDesign';
|
import { useDesign } from '/@/hooks/web/useDesign';
|
||||||
import { useAttrs } from '/@/hooks/core/useAttrs';
|
import { useAttrs } from '/@/hooks/core/useAttrs';
|
||||||
@ -167,7 +163,7 @@
|
|||||||
|
|
||||||
function setDrawerProps(props: Partial<DrawerProps>): void {
|
function setDrawerProps(props: Partial<DrawerProps>): void {
|
||||||
// Keep the last setDrawerProps
|
// Keep the last setDrawerProps
|
||||||
propsRef.value = deepMerge(unref(propsRef) || {}, props);
|
propsRef.value = deepMerge(unref(propsRef) || ({} as any), props);
|
||||||
|
|
||||||
if (Reflect.has(props, 'visible')) {
|
if (Reflect.has(props, 'visible')) {
|
||||||
visibleRef.value = !!props.visible;
|
visibleRef.value = !!props.visible;
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
function handleClose() {
|
function handleClose() {
|
||||||
emit('close');
|
emit('close');
|
||||||
}
|
}
|
||||||
|
|
||||||
return { prefixCls, handleClose };
|
return { prefixCls, handleClose };
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1,50 +1,44 @@
|
|||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
|
|
||||||
import { useI18n } from '/@/hooks/web/useI18n';
|
import { useI18n } from '/@/hooks/web/useI18n';
|
||||||
import { propTypes } from '/@/utils/propTypes';
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
export const footerProps = {
|
export const footerProps = {
|
||||||
confirmLoading: propTypes.bool,
|
confirmLoading: { type: Boolean },
|
||||||
/**
|
/**
|
||||||
* @description: Show close button
|
* @description: Show close button
|
||||||
*/
|
*/
|
||||||
showCancelBtn: propTypes.bool.def(true),
|
showCancelBtn: { type: Boolean, default: true },
|
||||||
cancelButtonProps: Object as PropType<Recordable>,
|
cancelButtonProps: Object as PropType<Recordable>,
|
||||||
cancelText: propTypes.string.def(t('common.cancelText')),
|
cancelText: { type: String, default: t('common.cancelText') },
|
||||||
/**
|
/**
|
||||||
* @description: Show confirmation button
|
* @description: Show confirmation button
|
||||||
*/
|
*/
|
||||||
showOkBtn: propTypes.bool.def(true),
|
showOkBtn: { type: Boolean, default: true },
|
||||||
okButtonProps: Object as PropType<Recordable>,
|
okButtonProps: Object as PropType<Recordable>,
|
||||||
okText: propTypes.string.def(t('common.okText')),
|
okText: { type: String, default: t('common.okText') },
|
||||||
okType: propTypes.string.def('primary'),
|
okType: { type: String, default: 'primary' },
|
||||||
showFooter: propTypes.bool,
|
showFooter: { type: Boolean },
|
||||||
footerHeight: {
|
footerHeight: {
|
||||||
type: [String, Number] as PropType<string | number>,
|
type: [String, Number] as PropType<string | number>,
|
||||||
default: 60,
|
default: 60,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
export const basicProps = {
|
export const basicProps = {
|
||||||
isDetail: propTypes.bool,
|
isDetail: { type: Boolean },
|
||||||
title: propTypes.string.def(''),
|
title: { type: String, default: '' },
|
||||||
loadingText: propTypes.string,
|
loadingText: { type: String },
|
||||||
showDetailBack: propTypes.bool.def(true),
|
showDetailBack: { type: Boolean, default: true },
|
||||||
visible: propTypes.bool,
|
visible: { type: Boolean },
|
||||||
loading: propTypes.bool,
|
loading: { type: Boolean },
|
||||||
maskClosable: propTypes.bool.def(true),
|
maskClosable: { type: Boolean, default: true },
|
||||||
getContainer: {
|
getContainer: {
|
||||||
type: [Object, String] as PropType<any>,
|
type: [Object, String] as PropType<any>,
|
||||||
},
|
},
|
||||||
scrollOptions: {
|
|
||||||
type: Object as PropType<any>,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
closeFunc: {
|
closeFunc: {
|
||||||
type: [Function, Object] as PropType<any>,
|
type: [Function, Object] as PropType<any>,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
triggerWindowResize: propTypes.bool,
|
destroyOnClose: { type: Boolean },
|
||||||
destroyOnClose: propTypes.bool,
|
|
||||||
...footerProps,
|
...footerProps,
|
||||||
};
|
};
|
||||||
|
@ -9,6 +9,7 @@ export interface DrawerInstance {
|
|||||||
|
|
||||||
export interface ReturnMethods extends DrawerInstance {
|
export interface ReturnMethods extends DrawerInstance {
|
||||||
openDrawer: <T = any>(visible?: boolean, data?: T, openOnSet?: boolean) => void;
|
openDrawer: <T = any>(visible?: boolean, data?: T, openOnSet?: boolean) => void;
|
||||||
|
closeDrawer: () => void;
|
||||||
getVisible?: ComputedRef<boolean>;
|
getVisible?: ComputedRef<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +182,6 @@ export interface DrawerProps extends DrawerFooterProps {
|
|||||||
placement?: 'top' | 'right' | 'bottom' | 'left';
|
placement?: 'top' | 'right' | 'bottom' | 'left';
|
||||||
afterVisibleChange?: (visible?: boolean) => void;
|
afterVisibleChange?: (visible?: boolean) => void;
|
||||||
keyboard?: boolean;
|
keyboard?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify a callback that will be called when a user clicks mask, close button or Cancel button.
|
* Specify a callback that will be called when a user clicks mask, close button or Cancel button.
|
||||||
*/
|
*/
|
@ -4,8 +4,7 @@ import type {
|
|||||||
ReturnMethods,
|
ReturnMethods,
|
||||||
DrawerProps,
|
DrawerProps,
|
||||||
UseDrawerInnerReturnType,
|
UseDrawerInnerReturnType,
|
||||||
} from './types';
|
} from './typing';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ref,
|
ref,
|
||||||
getCurrentInstance,
|
getCurrentInstance,
|
||||||
@ -16,11 +15,9 @@ import {
|
|||||||
toRaw,
|
toRaw,
|
||||||
computed,
|
computed,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
|
|
||||||
import { isProdMode } from '/@/utils/env';
|
import { isProdMode } from '/@/utils/env';
|
||||||
import { isFunction } from '/@/utils/is';
|
import { isFunction } from '/@/utils/is';
|
||||||
import { tryOnUnmounted } from '@vueuse/core';
|
import { tryOnUnmounted } from '@vueuse/core';
|
||||||
|
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
import { error } from '/@/utils/log';
|
import { error } from '/@/utils/log';
|
||||||
|
|
||||||
@ -32,24 +29,27 @@ const visibleData = reactive<{ [key: number]: boolean }>({});
|
|||||||
* @description: Applicable to separate drawer and call outside
|
* @description: Applicable to separate drawer and call outside
|
||||||
*/
|
*/
|
||||||
export function useDrawer(): UseDrawerReturnType {
|
export function useDrawer(): UseDrawerReturnType {
|
||||||
const drawerRef = ref<DrawerInstance | null>(null);
|
if (!getCurrentInstance()) {
|
||||||
const loadedRef = ref<Nullable<boolean>>(false);
|
throw new Error('useDrawer() can only be used inside setup() or functional components!');
|
||||||
const uidRef = ref<string>('');
|
}
|
||||||
|
const drawer = ref<DrawerInstance | null>(null);
|
||||||
|
const loaded = ref<Nullable<boolean>>(false);
|
||||||
|
const uid = ref<string>('');
|
||||||
|
|
||||||
function register(drawerInstance: DrawerInstance, uuid: string) {
|
function register(drawerInstance: DrawerInstance, uuid: string) {
|
||||||
isProdMode() &&
|
isProdMode() &&
|
||||||
tryOnUnmounted(() => {
|
tryOnUnmounted(() => {
|
||||||
drawerRef.value = null;
|
drawer.value = null;
|
||||||
loadedRef.value = null;
|
loaded.value = null;
|
||||||
dataTransferRef[unref(uidRef)] = null;
|
dataTransferRef[unref(uid)] = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (unref(loadedRef) && isProdMode() && drawerInstance === unref(drawerRef)) {
|
if (unref(loaded) && isProdMode() && drawerInstance === unref(drawer)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uidRef.value = uuid;
|
uid.value = uuid;
|
||||||
drawerRef.value = drawerInstance;
|
drawer.value = drawerInstance;
|
||||||
loadedRef.value = true;
|
loaded.value = true;
|
||||||
|
|
||||||
drawerInstance.emitVisible = (visible: boolean, uid: number) => {
|
drawerInstance.emitVisible = (visible: boolean, uid: number) => {
|
||||||
visibleData[uid] = visible;
|
visibleData[uid] = visible;
|
||||||
@ -57,7 +57,7 @@ export function useDrawer(): UseDrawerReturnType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getInstance = () => {
|
const getInstance = () => {
|
||||||
const instance = unref(drawerRef);
|
const instance = unref(drawer);
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
error('useDrawer instance is undefined!');
|
error('useDrawer instance is undefined!');
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ export function useDrawer(): UseDrawerReturnType {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getVisible: computed((): boolean => {
|
getVisible: computed((): boolean => {
|
||||||
return visibleData[~~unref(uidRef)];
|
return visibleData[~~unref(uid)];
|
||||||
}),
|
}),
|
||||||
|
|
||||||
openDrawer: <T = any>(visible = true, data?: T, openOnSet = true): void => {
|
openDrawer: <T = any>(visible = true, data?: T, openOnSet = true): void => {
|
||||||
@ -80,15 +80,18 @@ export function useDrawer(): UseDrawerReturnType {
|
|||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
if (openOnSet) {
|
if (openOnSet) {
|
||||||
dataTransferRef[unref(uidRef)] = null;
|
dataTransferRef[unref(uid)] = null;
|
||||||
dataTransferRef[unref(uidRef)] = toRaw(data);
|
dataTransferRef[unref(uid)] = toRaw(data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const equal = isEqual(toRaw(dataTransferRef[unref(uidRef)]), toRaw(data));
|
const equal = isEqual(toRaw(dataTransferRef[unref(uid)]), toRaw(data));
|
||||||
if (!equal) {
|
if (!equal) {
|
||||||
dataTransferRef[unref(uidRef)] = toRaw(data);
|
dataTransferRef[unref(uid)] = toRaw(data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
closeDrawer: () => {
|
||||||
|
getInstance()?.setDrawerProps({ visible: false });
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return [register, methods];
|
return [register, methods];
|
||||||
@ -99,8 +102,8 @@ export const useDrawerInner = (callbackFn?: Fn): UseDrawerInnerReturnType => {
|
|||||||
const currentInstance = getCurrentInstance();
|
const currentInstance = getCurrentInstance();
|
||||||
const uidRef = ref<string>('');
|
const uidRef = ref<string>('');
|
||||||
|
|
||||||
if (!currentInstance) {
|
if (!getCurrentInstance()) {
|
||||||
error('useDrawerInner instance is undefined!');
|
throw new Error('useDrawerInner() can only be used inside setup() or functional components!');
|
||||||
}
|
}
|
||||||
|
|
||||||
const getInstance = () => {
|
const getInstance = () => {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import Dropdown from './src/Dropdown.vue';
|
import { withInstall } from '/@/utils';
|
||||||
|
import dropdown from './src/Dropdown.vue';
|
||||||
|
|
||||||
export * from './src/types';
|
export * from './src/typing';
|
||||||
export { Dropdown };
|
export const Dropdown = withInstall(dropdown);
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-dropdown :trigger="trigger" v-bind="$attrs">
|
<Dropdown :trigger="trigger" v-bind="$attrs">
|
||||||
<span>
|
<span>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</span>
|
</span>
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<a-menu :selectedKeys="selectedKeys">
|
<Menu :selectedKeys="selectedKeys">
|
||||||
<template v-for="item in dropMenuList" :key="`${item.event}`">
|
<template v-for="item in dropMenuList" :key="`${item.event}`">
|
||||||
<a-menu-item
|
<MenuItem
|
||||||
v-bind="getAttr(item.event)"
|
v-bind="getAttr(item.event)"
|
||||||
@click="handleClickMenu(item)"
|
@click="handleClickMenu(item)"
|
||||||
:disabled="item.disabled"
|
:disabled="item.disabled"
|
||||||
@ -19,17 +19,17 @@
|
|||||||
<Icon :icon="item.icon" v-if="item.icon" />
|
<Icon :icon="item.icon" v-if="item.icon" />
|
||||||
<span class="ml-1">{{ item.text }}</span>
|
<span class="ml-1">{{ item.text }}</span>
|
||||||
</template>
|
</template>
|
||||||
</a-menu-item>
|
</MenuItem>
|
||||||
<a-menu-divider v-if="item.divider" :key="`d-${item.event}`" />
|
<MenuDivider v-if="item.divider" :key="`d-${item.event}`" />
|
||||||
</template>
|
</template>
|
||||||
</a-menu>
|
</Menu>
|
||||||
</template>
|
</template>
|
||||||
</a-dropdown>
|
</Dropdown>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
import type { DropMenu } from './types';
|
import type { DropMenu } from './typing';
|
||||||
|
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { Dropdown, Menu, Popconfirm } from 'ant-design-vue';
|
import { Dropdown, Menu, Popconfirm } from 'ant-design-vue';
|
||||||
@ -38,10 +38,10 @@
|
|||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'BasicDropdown',
|
name: 'BasicDropdown',
|
||||||
components: {
|
components: {
|
||||||
[Dropdown.name]: Dropdown,
|
Dropdown,
|
||||||
[Menu.name]: Menu,
|
Menu,
|
||||||
[Menu.Item.name]: Menu.Item,
|
MenuItem: Menu.Item,
|
||||||
[Menu.Divider.name]: Menu.Divider,
|
MenuDivider: Menu.Divider,
|
||||||
Icon,
|
Icon,
|
||||||
Popconfirm,
|
Popconfirm,
|
||||||
},
|
},
|
||||||
@ -53,7 +53,7 @@
|
|||||||
* @type string[]
|
* @type string[]
|
||||||
*/
|
*/
|
||||||
trigger: {
|
trigger: {
|
||||||
type: [Array] as PropType<string[]>,
|
type: [Array] as PropType<('contextmenu' | 'click' | 'hover')[]>,
|
||||||
default: () => {
|
default: () => {
|
||||||
return ['contextmenu'];
|
return ['contextmenu'];
|
||||||
},
|
},
|
||||||
@ -75,6 +75,7 @@
|
|||||||
emit('menuEvent', menu);
|
emit('menuEvent', menu);
|
||||||
item.onClick?.();
|
item.onClick?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handleClickMenu,
|
handleClickMenu,
|
||||||
getAttr: (key: string | number) => ({ key }),
|
getAttr: (key: string | number) => ({ key }),
|
||||||
|
@ -7,5 +7,3 @@ export interface DropMenu {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
divider?: boolean;
|
divider?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// export type Trigger = 'click' | 'hover' | 'contextMenu';
|
|
@ -1,8 +1,8 @@
|
|||||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
import { withInstall } from '/@/utils';
|
||||||
|
import impExcel from './src/ImportExcel.vue';
|
||||||
export const ImpExcel = createAsyncComponent(() => import('./src/ImportExcel.vue'));
|
import expExcelModal from './src/ExportExcelModal.vue';
|
||||||
export const ExpExcelModel = createAsyncComponent(() => import('./src/ExportExcelModel.vue'));
|
|
||||||
|
|
||||||
export * from './src/types';
|
|
||||||
|
|
||||||
|
export const ImpExcel = withInstall(impExcel);
|
||||||
|
export const ExpExcelModal = withInstall(expExcelModal);
|
||||||
|
export * from './src/typing';
|
||||||
export { jsonToSheetXlsx, aoaToSheetXlsx } from './src/Export2Excel';
|
export { jsonToSheetXlsx, aoaToSheetXlsx } from './src/Export2Excel';
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import xlsx from 'xlsx';
|
import xlsx from 'xlsx';
|
||||||
import type { WorkBook } from 'xlsx';
|
import type { WorkBook } from 'xlsx';
|
||||||
import type { JsonToSheet, AoAToSheet } from './types';
|
import type { JsonToSheet, AoAToSheet } from './typing';
|
||||||
|
|
||||||
const { utils, writeFile } = xlsx;
|
const { utils, writeFile } = xlsx;
|
||||||
|
|
||||||
const DEF_FILE_NAME = 'excel-list.xlsx';
|
const DEF_FILE_NAME = 'excel-list.xlsx';
|
||||||
|
|
||||||
export function jsonToSheetXlsx<T = any>({
|
export function jsonToSheetXlsx<T = any>({
|
||||||
data,
|
data,
|
||||||
header,
|
header,
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
</BasicModal>
|
</BasicModal>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { ExportModalResult } from './types';
|
import type { ExportModalResult } from './typing';
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||||
import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
|
import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
|
@ -16,7 +16,7 @@
|
|||||||
import { defineComponent, ref, unref } from 'vue';
|
import { defineComponent, ref, unref } from 'vue';
|
||||||
import XLSX from 'xlsx';
|
import XLSX from 'xlsx';
|
||||||
|
|
||||||
import type { ExcelData } from './types';
|
import type { ExcelData } from './typing';
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'ImportExcel',
|
name: 'ImportExcel',
|
||||||
emits: ['success', 'error'],
|
emits: ['success', 'error'],
|
||||||
|
@ -6,10 +6,6 @@ export interface ExcelData<T = any> {
|
|||||||
meta: { sheetName: string };
|
meta: { sheetName: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
// export interface ImportProps {
|
|
||||||
// beforeUpload: (file: File) => boolean;
|
|
||||||
// }
|
|
||||||
|
|
||||||
export interface JsonToSheet<T = any> {
|
export interface JsonToSheet<T = any> {
|
||||||
data: T[];
|
data: T[];
|
||||||
header?: T;
|
header?: T;
|
@ -9,23 +9,20 @@
|
|||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Definition } from '@logicflow/core';
|
import type { Definition } from '@logicflow/core';
|
||||||
|
|
||||||
import { defineComponent, ref, onMounted, unref, nextTick, computed, watch } from 'vue';
|
import { defineComponent, ref, onMounted, unref, nextTick, computed, watch } from 'vue';
|
||||||
|
|
||||||
import FlowChartToolbar from './FlowChartToolbar.vue';
|
import FlowChartToolbar from './FlowChartToolbar.vue';
|
||||||
import LogicFlow from '@logicflow/core';
|
import LogicFlow from '@logicflow/core';
|
||||||
import { Snapshot, BpmnElement, Menu, DndPanel } from '@logicflow/extension';
|
import { Snapshot, BpmnElement, Menu, DndPanel, SelectionSelect } from '@logicflow/extension';
|
||||||
|
|
||||||
import { useDesign } from '/@/hooks/web/useDesign';
|
import { useDesign } from '/@/hooks/web/useDesign';
|
||||||
import { useAppStore } from '/@/store/modules/app';
|
import { useAppStore } from '/@/store/modules/app';
|
||||||
import { createFlowChartContext } from './useFlowContext';
|
import { createFlowChartContext } from './useFlowContext';
|
||||||
|
|
||||||
import { toLogicFlowData } from './adpterForTurbo';
|
import { toLogicFlowData } from './adpterForTurbo';
|
||||||
import { useModal, BasicModal } from '/@/components/Modal';
|
import { useModal, BasicModal } from '/@/components/Modal';
|
||||||
import { JsonPreview } from '/@/components/CodeEditor';
|
import { JsonPreview } from '/@/components/CodeEditor';
|
||||||
|
import { configDefaultDndPanel } from './config';
|
||||||
import '@logicflow/core/dist/style/index.css';
|
import '@logicflow/core/dist/style/index.css';
|
||||||
import '@logicflow/extension/lib/style/index.css';
|
import '@logicflow/extension/lib/style/index.css';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'FlowChart',
|
name: 'FlowChart',
|
||||||
components: { BasicModal, FlowChartToolbar, JsonPreview },
|
components: { BasicModal, FlowChartToolbar, JsonPreview },
|
||||||
@ -44,6 +41,9 @@
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
patternItems: {
|
||||||
|
type: Array,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const lfElRef = ref<ElRef>(null);
|
const lfElRef = ref<ElRef>(null);
|
||||||
@ -104,6 +104,7 @@
|
|||||||
if (!lfEl) {
|
if (!lfEl) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
LogicFlow.use(DndPanel);
|
||||||
|
|
||||||
// Canvas configuration
|
// Canvas configuration
|
||||||
LogicFlow.use(Snapshot);
|
LogicFlow.use(Snapshot);
|
||||||
@ -111,14 +112,16 @@
|
|||||||
LogicFlow.use(BpmnElement);
|
LogicFlow.use(BpmnElement);
|
||||||
// Start the right-click menu
|
// Start the right-click menu
|
||||||
LogicFlow.use(Menu);
|
LogicFlow.use(Menu);
|
||||||
LogicFlow.use(DndPanel);
|
LogicFlow.use(SelectionSelect);
|
||||||
|
|
||||||
lfInstance.value = new LogicFlow({
|
lfInstance.value = new LogicFlow({
|
||||||
...unref(getFlowOptions),
|
...unref(getFlowOptions),
|
||||||
container: lfEl,
|
container: lfEl,
|
||||||
});
|
});
|
||||||
unref(lfInstance)?.setDefaultEdgeType('line');
|
const lf = unref(lfInstance)!;
|
||||||
|
lf?.setDefaultEdgeType('line');
|
||||||
onRender();
|
onRender();
|
||||||
|
lf?.setPatternItems(props.patternItems || configDefaultDndPanel(lf));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onRender() {
|
async function onRender() {
|
||||||
@ -137,7 +140,6 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
graphData.value = unref(lf).getGraphData();
|
graphData.value = unref(lf).getGraphData();
|
||||||
|
|
||||||
openModal();
|
openModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="`${prefixCls}-toolbar`" class="flex items-center px-2 py-1">
|
<div :class="`${prefixCls}-toolbar`" class="flex items-center px-2 py-1">
|
||||||
<template v-for="(item, index) in toolbarItemList" :key="item.type || index">
|
<template v-for="item in toolbarItemList" :key="item.type">
|
||||||
<Tooltip placement="bottom" v-bind="item.disabled ? { visible: false } : {}">
|
<Tooltip placement="bottom" v-bind="item.disabled ? { visible: false } : {}">
|
||||||
<template #title>{{ item.tooltip }}</template>
|
<template #title>{{ item.tooltip }}</template>
|
||||||
<span :class="`${prefixCls}-toolbar__icon`" v-if="item.icon" @click="onControl(item)">
|
<span :class="`${prefixCls}-toolbar__icon`" v-if="item.icon" @click="onControl(item)">
|
||||||
|
@ -53,3 +53,44 @@ export const BpmnNode = [
|
|||||||
class: 'bpmn-user',
|
class: 'bpmn-user',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export function configDefaultDndPanel(lf) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
text: '选区',
|
||||||
|
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAOVJREFUOBGtVMENwzAIjKP++2026ETdpv10iy7WFbqFyyW6GBywLCv5gI+Dw2Bluj1znuSjhb99Gkn6QILDY2imo60p8nsnc9bEo3+QJ+AKHfMdZHnl78wyTnyHZD53Zzx73MRSgYvnqgCUHj6gwdck7Zsp1VOrz0Uz8NbKunzAW+Gu4fYW28bUYutYlzSa7B84Fh7d1kjLwhcSdYAYrdkMQVpsBr5XgDGuXwQfQr0y9zwLda+DUYXLaGKdd2ZTtvbolaO87pdo24hP7ov16N0zArH1ur3iwJpXxm+v7oAJNR4JEP8DoAuSFEkYH7cAAAAASUVORK5CYII=',
|
||||||
|
callback: () => {
|
||||||
|
lf.updateEditConfig({
|
||||||
|
stopMoveGraph: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'circle',
|
||||||
|
text: '开始',
|
||||||
|
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAnBJREFUOBGdVL1rU1EcPfdGBddmaZLiEhdx1MHZQXApraCzQ7GKLgoRBxMfcRELuihWKcXFRcEWF8HBf0DdDCKYRZpnl7p0svLe9Zzbd29eQhTbC8nv+9zf130AT63jvooOGS8Vf9Nt5zxba7sXQwODfkWpkbjTQfCGUd9gIp3uuPP8bZ946g56dYQvnBg+b1HB8VIQmMFrazKcKSvFW2dQTxJnJdQ77urmXWOMBCmXM2Rke4S7UAW+/8ywwFoewmBps2tu7mbTdp8VMOkIRAkKfrVawalJTtIliclFbaOBqa0M2xImHeVIfd/nKAfVq/LGnPss5Kh00VEdSzfwnBXPUpmykNss4lUI9C1ga+8PNrBD5YeqRY2Zz8PhjooIbfJXjowvQJBqkmEkVnktWhwu2SM7SMx7Cj0N9IC0oQXRo8xwAGzQms+xrB/nNSUWVveI48ayrFGyC2+E2C+aWrZHXvOuz+CiV6iycWe1Rd1Q6+QUG07nb5SbPrL4426d+9E1axKjY3AoRrlEeSQo2Eu0T6BWAAr6COhTcWjRaYfKG5csnvytvUr/WY4rrPMB53Uo7jZRjXaG6/CFfNMaXEu75nG47X+oepU7PKJvvzGDY1YLSKHJrK7vFUwXKkaxwhCW3u+sDFMVrIju54RYYbFKpALZAo7sB6wcKyyrd+aBMryMT2gPyD6GsQoRFkGHr14TthZni9ck0z+Pnmee460mHXbRAypKNy3nuMdrWgVKj8YVV8E7PSzp1BZ9SJnJAsXdryw/h5ctboUVi4AFiCd+lQaYMw5z3LGTBKjLQOeUF35k89f58Vv/tGh+l+PE/wG0rgfIUbZK5AAAAABJRU5ErkJggg==',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'rect',
|
||||||
|
text: '用户任务',
|
||||||
|
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAEFVwZaAAAABGdBTUEAALGPC/xhBQAAAqlJREFUOBF9VM9rE0EUfrMJNUKLihGbpLGtaCOIR8VjQMGDePCgCCIiCNqzCAp2MyYUCXhUtF5E0D+g1t48qAd7CCLqQUQKEWkStcEfVGlLdp/fm3aW2QQdyLzf33zz5m2IsAZ9XhDpyaaIZkTS4ASzK41TFao88GuJ3hsr2pAbipHxuSYyKRugagICGANkfFnNh3HeE2N0b3nN2cgnpcictw5veJIzxmDamSlxxQZicq/mflxhbaH8BLRbuRwNtZp0JAhoplVRUdzmCe/vO27wFuuA3S5qXruGdboy5/PRGFsbFGKo/haRtQHIrM83bVeTrOgNhZReWaYGnE4aUQgTJNvijJFF4jQ8BxJE5xfKatZWmZcTQ+BVgh7s8SgPlCkcec4mGTmieTP4xd7PcpIEg1TX6gdeLW8rTVMVLVvb7ctXoH0Cydl2QOPJBG21STE5OsnbweVYzAnD3A7PVILuY0yiiyDwSm2g441r6rMSgp6iK42yqroI2QoXeJVeA+YeZSa47gZdXaZWQKTrG93rukk/l2Al6Kzh5AZEl7dDQy+JjgFahQjRopSxPbrbvK7GRe9ePWBo1wcU7sYrFZtavXALwGw/7Dnc50urrHJuTPSoO2IMV3gUQGNg87IbSOIY9BpiT9HV7FCZ94nPXb3MSnwHn/FFFE1vG6DTby+r31KAkUktB3Qf6ikUPWxW1BkXSPQeMHHiW0+HAd2GelJsZz1OJegCxqzl+CLVHa/IibuHeJ1HAKzhuDR+ymNaRFM+4jU6UWKXorRmbyqkq/D76FffevwdCp+jN3UAN/C9JRVTDuOxC/oh+EdMnqIOrlYteKSfadVRGLJFJPSB/ti/6K8f0CNymg/iH2gO/f0DwE0yjAFO6l8JaR5j0VPwPwfaYHqOqrCI319WzwhwzNW/aQAAAABJRU5ErkJggg==',
|
||||||
|
cls: 'important-node',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'rect',
|
||||||
|
text: '系统任务',
|
||||||
|
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAEFVwZaAAAABGdBTUEAALGPC/xhBQAAAqlJREFUOBF9VM9rE0EUfrMJNUKLihGbpLGtaCOIR8VjQMGDePCgCCIiCNqzCAp2MyYUCXhUtF5E0D+g1t48qAd7CCLqQUQKEWkStcEfVGlLdp/fm3aW2QQdyLzf33zz5m2IsAZ9XhDpyaaIZkTS4ASzK41TFao88GuJ3hsr2pAbipHxuSYyKRugagICGANkfFnNh3HeE2N0b3nN2cgnpcictw5veJIzxmDamSlxxQZicq/mflxhbaH8BLRbuRwNtZp0JAhoplVRUdzmCe/vO27wFuuA3S5qXruGdboy5/PRGFsbFGKo/haRtQHIrM83bVeTrOgNhZReWaYGnE4aUQgTJNvijJFF4jQ8BxJE5xfKatZWmZcTQ+BVgh7s8SgPlCkcec4mGTmieTP4xd7PcpIEg1TX6gdeLW8rTVMVLVvb7ctXoH0Cydl2QOPJBG21STE5OsnbweVYzAnD3A7PVILuY0yiiyDwSm2g441r6rMSgp6iK42yqroI2QoXeJVeA+YeZSa47gZdXaZWQKTrG93rukk/l2Al6Kzh5AZEl7dDQy+JjgFahQjRopSxPbrbvK7GRe9ePWBo1wcU7sYrFZtavXALwGw/7Dnc50urrHJuTPSoO2IMV3gUQGNg87IbSOIY9BpiT9HV7FCZ94nPXb3MSnwHn/FFFE1vG6DTby+r31KAkUktB3Qf6ikUPWxW1BkXSPQeMHHiW0+HAd2GelJsZz1OJegCxqzl+CLVHa/IibuHeJ1HAKzhuDR+ymNaRFM+4jU6UWKXorRmbyqkq/D76FffevwdCp+jN3UAN/C9JRVTDuOxC/oh+EdMnqIOrlYteKSfadVRGLJFJPSB/ti/6K8f0CNymg/iH2gO/f0DwE0yjAFO6l8JaR5j0VPwPwfaYHqOqrCI319WzwhwzNW/aQAAAABJRU5ErkJggg==',
|
||||||
|
cls: 'import_icon',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'diamond',
|
||||||
|
text: '条件判断',
|
||||||
|
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAAHeEJUAAAAABGdBTUEAALGPC/xhBQAAAvVJREFUOBGNVEFrE0EU/mY3bQoiFlOkaUJrQUQoWMGePLX24EH0IIoHKQiCV0G8iE1covgLiqA/QTzVm1JPogc9tIJYFaQtlhQxqYjSpunu+L7JvmUTU3AgmTfvffPNN++9WSA1DO182f6xwILzD5btfAoQmwL5KJEwiQyVbSVZ0IgRyV6PTpIJ81E5ZvqfHQR0HUOBHW4L5Et2kQ6Zf7iAOhTFAA8s0pEP7AXO1uAA52SbqGk6h/6J45LaLhO64ByfcUzM39V7ZiAdS2yCePPEIQYvTUHqM/n7dgQNfBKWPjpF4ISk8q3J4nB11qw6X8l+FsF3EhlkEMfrjIer3wJTLwS2aCNcj4DbGxXTw00JmAuO+Ni6bBxVUCvS5d9aa04+so4pHW5jLTywuXAL7jJ+D06sl82Sgl2JuVBQn498zkc2bGKxULHjCnSMadBKYDYYHAtsby1EQ5lNGrQd4Y3v4Zo0XdGEmDno46yCM9Tk+RiJmUYHS/aXHPNTcjxcbTFna000PFJHIVZ5lFRqRpJWk9/+QtlOUYJj9HG5pVFEU7zqIYDVsw2s+AJaD8wTd2umgSCCyUxgGsS1Y6TBwXQQTFuZaHcd8gAGioE90hlsY+wMcs30RduYtxanjMGal8H5dMW67dmT1JFtYUEe8LiQLRsPZ6IIc7A4J5tqco3T0pnv/4u0kyzrYUq7gASuEyI8VXKvB9Odytv6jS/PNaZBln0nioJG/AVQRZvApOdhjj3Jt8QC8Im09SafwdBdvIpztpxWxpeKCC+EsFdS8DCyuCn2munFpL7ctHKp+Xc5cMybeIyMAN33SPL3ZR9QV1XVwLyzHm6Iv0/yeUuUb7PPlZC4D4HZkeu6dpF4v9j9MreGtMbxMMRLIcjJic9yHi7WQ3yVKzZVWUr5UrViJvn1FfUlwe/KYVfYyWRLSGNu16hR01U9IacajXPei0wx/5BqgInvJN+MMNtNme7ReU9SBbgntovn0kKHpFg7UogZvaZiOue/q1SBo9ktHzQAAAAASUVORK5CYII=',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'circle',
|
||||||
|
text: '结束',
|
||||||
|
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAA1BJREFUOBFtVE1IVUEYPXOf+tq40Y3vPcmFIdSjIorWoRG0ERWUgnb5FwVhYQSl72oUoZAboxKNFtWiwKRN0M+jpfSzqJAQclHo001tKkjl3emc8V69igP3znzfnO/M9zcDcKT67azmjYWTwl9Vn7Vumeqzj1DVb6cleQY4oAVnIOPb+mKAGxQmKI5CWNJ2aLPatxWa3aB9K7/fB+/Z0jUF6TmMlFLQqrkECWQzOZxYGjTlOl8eeKaIY5yHnFn486xBustDjWT6dG7pmjHOJd+33t0iitTPkK6tEvjxq4h2MozQ6WFSX/LkDUGfFwfhEZj1Auz/U4pyAi5Sznd7uKzznXeVHlI/Aywmk6j7fsUsEuCGADrWARXXwjxWQsUbIupDHJI7kF5dRktg0eN81IbiZXiTESic50iwS+t1oJgL83jAiBupLDCQqwziaWSoAFSeIR3P5Xv5az00wyIn35QRYTwdSYbz8pH8fxUUAtxnFvYmEmgI0wYXUXcCCSpeEVpXlsRhBnCEATxWylL9+EKCAYhe1NGstUa6356kS9NVvt3DU2fd+Wtbm/+lSbylJqsqkSm9CRhvoJVlvKPvF1RKY/FcPn5j4UfIMLn8D4UYb54BNsilTDXKnF4CfTobA0FpoW/LSp306wkXM+XaOJhZaFkcNM82ASNAWMrhrUbRfmyeI1FvRBTpN06WKxa9BK0o2E4Pd3zfBBEwPsv9sQBnmLVbLEIZ/Xe9LYwJu/Er17W6HYVBc7vmuk0xUQ+pqxdom5Fnp55SiytXLPYoMXNM4u4SNSCFWnrVIzKG3EGyMXo6n/BQOe+bX3FClY4PwydVhthOZ9NnS+ntiLh0fxtlUJHAuGaFoVmttpVMeum0p3WEXbcll94l1wM/gZ0Ccczop77VvN2I7TlsZCsuXf1WHvWEhjO8DPtyOVg2/mvK9QqboEth+7pD6NUQC1HN/TwvydGBARi9MZSzLE4b8Ru3XhX2PBxf8E1er2A6516o0w4sIA+lwURhAON82Kwe2iDAC1Watq4XHaGQ7skLcFOtI5lDxuM2gZe6WFIotPAhbaeYlU4to5cuarF1QrcZ/lwrLaCJl66JBocYZnrNlvm2+MBCTmUymPrYZVbjdlr/BxlMjmNmNI3SAAAAAElFTkSuQmCC',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<Form
|
<Form
|
||||||
v-bind="{ ...$attrs, ...$props, ...getProps }"
|
v-bind="getBindValue"
|
||||||
:class="getFormClass"
|
:class="getFormClass"
|
||||||
ref="formElRef"
|
ref="formElRef"
|
||||||
:model="formModel"
|
:model="formModel"
|
||||||
@keypress.enter="handleEnterPress"
|
@keypress.enter="handleEnterPress"
|
||||||
>
|
>
|
||||||
<Row v-bind="{ ...getRow }">
|
<Row v-bind="getRow">
|
||||||
<slot name="formHeader"></slot>
|
<slot name="formHeader"></slot>
|
||||||
<template v-for="schema in getSchema" :key="schema.field">
|
<template v-for="schema in getSchema" :key="schema.field">
|
||||||
<FormItem
|
<FormItem
|
||||||
@ -39,7 +39,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { FormActionType, FormProps, FormSchema } from './types/form';
|
import type { FormActionType, FormProps, FormSchema } from './types/form';
|
||||||
import type { AdvanceState } from './types/hooks';
|
import type { AdvanceState } from './types/hooks';
|
||||||
import type { CSSProperties, Ref } from 'vue';
|
import type { Ref } from 'vue';
|
||||||
|
|
||||||
import { defineComponent, reactive, ref, computed, unref, onMounted, watch, nextTick } from 'vue';
|
import { defineComponent, reactive, ref, computed, unref, onMounted, watch, nextTick } from 'vue';
|
||||||
import { Form, Row } from 'ant-design-vue';
|
import { Form, Row } from 'ant-design-vue';
|
||||||
@ -69,7 +69,7 @@
|
|||||||
components: { FormItem, Form, Row, FormAction },
|
components: { FormItem, Form, Row, FormAction },
|
||||||
props: basicProps,
|
props: basicProps,
|
||||||
emits: ['advanced-change', 'reset', 'submit', 'register'],
|
emits: ['advanced-change', 'reset', 'submit', 'register'],
|
||||||
setup(props, { emit }) {
|
setup(props, { emit, attrs }) {
|
||||||
const formModel = reactive<Recordable>({});
|
const formModel = reactive<Recordable>({});
|
||||||
const modalFn = useModalContext();
|
const modalFn = useModalContext();
|
||||||
|
|
||||||
@ -103,7 +103,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Get uniform row style and Row configuration for the entire form
|
// Get uniform row style and Row configuration for the entire form
|
||||||
const getRow = computed((): CSSProperties | RowProps => {
|
const getRow = computed((): RowProps => {
|
||||||
const { baseRowStyle = {}, rowProps } = unref(getProps);
|
const { baseRowStyle = {}, rowProps } = unref(getProps);
|
||||||
return {
|
return {
|
||||||
style: baseRowStyle,
|
style: baseRowStyle,
|
||||||
@ -111,6 +111,10 @@
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getBindValue = computed(
|
||||||
|
() => ({ ...attrs, ...props, ...unref(getProps) } as Recordable)
|
||||||
|
);
|
||||||
|
|
||||||
const getSchema = computed((): FormSchema[] => {
|
const getSchema = computed((): FormSchema[] => {
|
||||||
const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
|
const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
|
||||||
for (const schema of schemas) {
|
for (const schema of schemas) {
|
||||||
@ -260,6 +264,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
getBindValue,
|
||||||
handleToggleAdvanced,
|
handleToggleAdvanced,
|
||||||
handleEnterPress,
|
handleEnterPress,
|
||||||
formModel,
|
formModel,
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
|
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
|
||||||
import { useAttrs } from '/@/hooks/core/useAttrs';
|
import { useAttrs } from '/@/hooks/core/useAttrs';
|
||||||
import { get, omit } from 'lodash-es';
|
import { get, omit } from 'lodash-es';
|
||||||
|
|
||||||
import { LoadingOutlined } from '@ant-design/icons-vue';
|
import { LoadingOutlined } from '@ant-design/icons-vue';
|
||||||
import { useI18n } from '/@/hooks/web/useI18n';
|
import { useI18n } from '/@/hooks/web/useI18n';
|
||||||
import { propTypes } from '/@/utils/propTypes';
|
import { propTypes } from '/@/utils/propTypes';
|
||||||
|
@ -40,13 +40,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { ColEx } from '../types/index';
|
import type { ColEx } from '../types/index';
|
||||||
import type { ButtonProps } from 'ant-design-vue/es/button/buttonTypes';
|
import type { ButtonProps } from 'ant-design-vue/es/button/buttonTypes';
|
||||||
|
|
||||||
import { defineComponent, computed, PropType } from 'vue';
|
import { defineComponent, computed, PropType } from 'vue';
|
||||||
import { Form, Col } from 'ant-design-vue';
|
import { Form, Col } from 'ant-design-vue';
|
||||||
import { Button } from '/@/components/Button';
|
import { Button } from '/@/components/Button';
|
||||||
import { BasicArrow } from '/@/components/Basic/index';
|
import { BasicArrow } from '/@/components/Basic/index';
|
||||||
import { useFormContext } from '../hooks/useFormContext';
|
import { useFormContext } from '../hooks/useFormContext';
|
||||||
|
|
||||||
import { useI18n } from '/@/hooks/web/useI18n';
|
import { useI18n } from '/@/hooks/web/useI18n';
|
||||||
import { propTypes } from '/@/utils/propTypes';
|
import { propTypes } from '/@/utils/propTypes';
|
||||||
|
|
||||||
|
@ -4,17 +4,14 @@
|
|||||||
import type { FormSchema } from '../types/form';
|
import type { FormSchema } from '../types/form';
|
||||||
import type { ValidationRule } from 'ant-design-vue/lib/form/Form';
|
import type { ValidationRule } from 'ant-design-vue/lib/form/Form';
|
||||||
import type { TableActionType } from '/@/components/Table';
|
import type { TableActionType } from '/@/components/Table';
|
||||||
|
|
||||||
import { defineComponent, computed, unref, toRefs } from 'vue';
|
import { defineComponent, computed, unref, toRefs } from 'vue';
|
||||||
import { Form, Col } from 'ant-design-vue';
|
import { Form, Col } from 'ant-design-vue';
|
||||||
import { componentMap } from '../componentMap';
|
import { componentMap } from '../componentMap';
|
||||||
import { BasicHelp } from '/@/components/Basic';
|
import { BasicHelp } from '/@/components/Basic';
|
||||||
|
|
||||||
import { isBoolean, isFunction, isNull } from '/@/utils/is';
|
import { isBoolean, isFunction, isNull } from '/@/utils/is';
|
||||||
import { getSlot } from '/@/utils/helper/tsxHelper';
|
import { getSlot } from '/@/utils/helper/tsxHelper';
|
||||||
import { createPlaceholderMessage, setComponentRuleType } from '../helper';
|
import { createPlaceholderMessage, setComponentRuleType } from '../helper';
|
||||||
import { upperFirst, cloneDeep } from 'lodash-es';
|
import { upperFirst, cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
import { useItemLabelWidth } from '../hooks/useLabelWidth';
|
import { useItemLabelWidth } from '../hooks/useLabelWidth';
|
||||||
import { useI18n } from '/@/hooks/web/useI18n';
|
import { useI18n } from '/@/hooks/web/useI18n';
|
||||||
|
|
||||||
@ -91,7 +88,6 @@
|
|||||||
if (isBoolean(dynamicDisabled)) {
|
if (isBoolean(dynamicDisabled)) {
|
||||||
disabled = dynamicDisabled;
|
disabled = dynamicDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFunction(dynamicDisabled)) {
|
if (isFunction(dynamicDisabled)) {
|
||||||
disabled = dynamicDisabled(unref(getValues));
|
disabled = dynamicDisabled(unref(getValues));
|
||||||
}
|
}
|
||||||
@ -229,9 +225,10 @@
|
|||||||
const eventKey = `on${upperFirst(changeEvent)}`;
|
const eventKey = `on${upperFirst(changeEvent)}`;
|
||||||
|
|
||||||
const on = {
|
const on = {
|
||||||
[eventKey]: (e: Nullable<Recordable>) => {
|
[eventKey]: (...args: Nullable<Recordable>[]) => {
|
||||||
|
const [e] = args;
|
||||||
if (propsData[eventKey]) {
|
if (propsData[eventKey]) {
|
||||||
propsData[eventKey](e);
|
propsData[eventKey](...args);
|
||||||
}
|
}
|
||||||
const target = e ? e.target : null;
|
const target = e ? e.target : null;
|
||||||
const value = target ? (isCheck ? target.checked : target.value) : e;
|
const value = target ? (isCheck ? target.checked : target.value) : e;
|
||||||
@ -276,7 +273,6 @@
|
|||||||
: {
|
: {
|
||||||
default: () => renderComponentContent,
|
default: () => renderComponentContent,
|
||||||
};
|
};
|
||||||
|
|
||||||
return <Comp {...compAttr}>{compSlot}</Comp>;
|
return <Comp {...compAttr}>{compSlot}</Comp>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,7 +313,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const showSuffix = !!suffix;
|
const showSuffix = !!suffix;
|
||||||
|
|
||||||
const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix;
|
const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -338,16 +333,18 @@
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
const { colProps = {}, colSlot, renderColContent, component } = props.schema;
|
const { colProps = {}, colSlot, renderColContent, component } = props.schema;
|
||||||
if (!componentMap.has(component)) return null;
|
if (!componentMap.has(component)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const { baseColProps = {} } = props.formProps;
|
const { baseColProps = {} } = props.formProps;
|
||||||
|
|
||||||
const realColProps = { ...baseColProps, ...colProps };
|
const realColProps = { ...baseColProps, ...colProps };
|
||||||
const { isIfShow, isShow } = getShow();
|
const { isIfShow, isShow } = getShow();
|
||||||
|
|
||||||
const values = unref(getValues);
|
const values = unref(getValues);
|
||||||
|
|
||||||
const getContent = () => {
|
const getContent = () => {
|
||||||
return colSlot
|
return colSlot
|
||||||
? getSlot(slots, colSlot, values)
|
? getSlot(slots, colSlot, values)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
<!--
|
<!--
|
||||||
* @Description:It is troublesome to implement radio button group in the form. So it is extracted independently as a separate component
|
* @Description:It is troublesome to implement radio button group in the form. So it is extracted independently as a separate component
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<RadioGroup v-bind="attrs" v-model:value="state" button-style="solid">
|
<RadioGroup v-bind="attrs" v-model:value="state" button-style="solid">
|
||||||
<template v-for="item in getOptions" :key="`${item.value}`">
|
<template v-for="item in getOptions" :key="`${item.value}`">
|
||||||
@ -17,6 +16,7 @@
|
|||||||
import { isString } from '/@/utils/is';
|
import { isString } from '/@/utils/is';
|
||||||
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
|
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
|
||||||
import { useAttrs } from '/@/hooks/core/useAttrs';
|
import { useAttrs } from '/@/hooks/core/useAttrs';
|
||||||
|
|
||||||
type OptionsItem = { label: string; value: string | number | boolean; disabled?: boolean };
|
type OptionsItem = { label: string; value: string | number | boolean; disabled?: boolean };
|
||||||
type RadioItem = string | OptionsItem;
|
type RadioItem = string | OptionsItem;
|
||||||
|
|
||||||
@ -39,6 +39,7 @@
|
|||||||
const attrs = useAttrs();
|
const attrs = useAttrs();
|
||||||
// Embedded in the form, just use the hook binding to perform form verification
|
// Embedded in the form, just use the hook binding to perform form verification
|
||||||
const [state] = useRuleFormItem(props);
|
const [state] = useRuleFormItem(props);
|
||||||
|
|
||||||
// Processing options value
|
// Processing options value
|
||||||
const getOptions = computed((): OptionsItem[] => {
|
const getOptions = computed((): OptionsItem[] => {
|
||||||
const { options } = props;
|
const { options } = props;
|
||||||
|
@ -2,10 +2,8 @@ import type { ColEx } from '../types';
|
|||||||
import type { AdvanceState } from '../types/hooks';
|
import type { AdvanceState } from '../types/hooks';
|
||||||
import type { ComputedRef, Ref } from 'vue';
|
import type { ComputedRef, Ref } from 'vue';
|
||||||
import type { FormProps, FormSchema } from '../types/form';
|
import type { FormProps, FormSchema } from '../types/form';
|
||||||
|
|
||||||
import { computed, unref, watch } from 'vue';
|
import { computed, unref, watch } from 'vue';
|
||||||
import { isBoolean, isFunction, isNumber, isObject } from '/@/utils/is';
|
import { isBoolean, isFunction, isNumber, isObject } from '/@/utils/is';
|
||||||
|
|
||||||
import { useBreakpoint } from '/@/hooks/event/useBreakpoint';
|
import { useBreakpoint } from '/@/hooks/event/useBreakpoint';
|
||||||
import { useDebounceFn } from '@vueuse/core';
|
import { useDebounceFn } from '@vueuse/core';
|
||||||
|
|
||||||
|
@ -16,16 +16,22 @@ export async function useAutoFocus({
|
|||||||
isInitedDefault,
|
isInitedDefault,
|
||||||
}: UseAutoFocusContext) {
|
}: UseAutoFocusContext) {
|
||||||
watchEffect(async () => {
|
watchEffect(async () => {
|
||||||
if (unref(isInitedDefault) || !unref(getProps).autoFocusFirstItem) return;
|
if (unref(isInitedDefault) || !unref(getProps).autoFocusFirstItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
await nextTick();
|
await nextTick();
|
||||||
const schemas = unref(getSchema);
|
const schemas = unref(getSchema);
|
||||||
const formEl = unref(formElRef);
|
const formEl = unref(formElRef);
|
||||||
const el = (formEl as any)?.$el as HTMLElement;
|
const el = (formEl as any)?.$el as HTMLElement;
|
||||||
if (!formEl || !el || !schemas || schemas.length === 0) return;
|
if (!formEl || !el || !schemas || schemas.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const firstItem = schemas[0];
|
const firstItem = schemas[0];
|
||||||
// Only open when the first form item is input type
|
// Only open when the first form item is input type
|
||||||
if (!firstItem.component.includes('Input')) return;
|
if (!firstItem.component.includes('Input')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const inputEl = el.querySelector('.ant-row:first-child input') as Nullable<HTMLInputElement>;
|
const inputEl = el.querySelector('.ant-row:first-child input') as Nullable<HTMLInputElement>;
|
||||||
if (!inputEl) return;
|
if (!inputEl) return;
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import { ref, onUnmounted, unref, nextTick, watch } from 'vue';
|
|
||||||
|
|
||||||
import { isProdMode } from '/@/utils/env';
|
|
||||||
import { error } from '/@/utils/log';
|
|
||||||
import { getDynamicProps } from '/@/utils';
|
|
||||||
|
|
||||||
import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form';
|
import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form';
|
||||||
import type { NamePath } from 'ant-design-vue/lib/form/interface';
|
import type { NamePath } from 'ant-design-vue/lib/form/interface';
|
||||||
import type { DynamicProps } from '/#/utils';
|
import type { DynamicProps } from '/#/utils';
|
||||||
|
import { ref, onUnmounted, unref, nextTick, watch } from 'vue';
|
||||||
|
import { isProdMode } from '/@/utils/env';
|
||||||
|
import { error } from '/@/utils/log';
|
||||||
|
import { getDynamicProps } from '/@/utils';
|
||||||
|
|
||||||
export declare type ValidateFields = (nameList?: NamePath[]) => Promise<Recordable>;
|
export declare type ValidateFields = (nameList?: NamePath[]) => Promise<Recordable>;
|
||||||
|
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import type { ComputedRef, Ref } from 'vue';
|
import type { ComputedRef, Ref } from 'vue';
|
||||||
import type { FormProps, FormSchema, FormActionType } from '../types/form';
|
import type { FormProps, FormSchema, FormActionType } from '../types/form';
|
||||||
import type { NamePath } from 'ant-design-vue/lib/form/interface';
|
import type { NamePath } from 'ant-design-vue/lib/form/interface';
|
||||||
|
|
||||||
import { unref, toRaw } from 'vue';
|
import { unref, toRaw } from 'vue';
|
||||||
|
|
||||||
import { isArray, isFunction, isObject, isString } from '/@/utils/is';
|
import { isArray, isFunction, isObject, isString } from '/@/utils/is';
|
||||||
import { deepMerge } from '/@/utils';
|
import { deepMerge } from '/@/utils';
|
||||||
import { dateItemType, handleInputNumberValue } from '../helper';
|
import { dateItemType, handleInputNumberValue } from '../helper';
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import { isArray, isFunction, isObject, isString, isNullOrUnDef } from '/@/utils/is';
|
import { isArray, isFunction, isObject, isString, isNullOrUnDef } from '/@/utils/is';
|
||||||
import { dateUtil } from '/@/utils/dateUtil';
|
import { dateUtil } from '/@/utils/dateUtil';
|
||||||
|
|
||||||
import { unref } from 'vue';
|
import { unref } from 'vue';
|
||||||
import type { Ref, ComputedRef } from 'vue';
|
import type { Ref, ComputedRef } from 'vue';
|
||||||
import type { FormProps, FormSchema } from '../types/form';
|
import type { FormProps, FormSchema } from '../types/form';
|
||||||
|
|
||||||
import { set } from 'lodash-es';
|
import { set } from 'lodash-es';
|
||||||
|
|
||||||
interface UseFormValuesContext {
|
interface UseFormValuesContext {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import type { NamePath, RuleObject } from 'ant-design-vue/lib/form/interface';
|
import type { NamePath, RuleObject } from 'ant-design-vue/lib/form/interface';
|
||||||
import type { VNode } from 'vue';
|
import type { VNode } from 'vue';
|
||||||
import type { ButtonProps as AntdButtonProps } from 'ant-design-vue/es/button/buttonTypes';
|
import type { ButtonProps as AntdButtonProps } from 'ant-design-vue/es/button/buttonTypes';
|
||||||
|
|
||||||
import type { FormItem } from './formItem';
|
import type { FormItem } from './formItem';
|
||||||
import type { ColEx, ComponentType } from './index';
|
import type { ColEx, ComponentType } from './index';
|
||||||
import type { TableActionType } from '/@/components/Table/src/types/table';
|
import type { TableActionType } from '/@/components/Table/src/types/table';
|
||||||
|
@ -90,9 +90,7 @@ export type ComponentType =
|
|||||||
| 'InputCountDown'
|
| 'InputCountDown'
|
||||||
| 'Select'
|
| 'Select'
|
||||||
| 'ApiSelect'
|
| 'ApiSelect'
|
||||||
| 'SelectOptGroup'
|
|
||||||
| 'TreeSelect'
|
| 'TreeSelect'
|
||||||
| 'Transfer'
|
|
||||||
| 'RadioButtonGroup'
|
| 'RadioButtonGroup'
|
||||||
| 'RadioGroup'
|
| 'RadioGroup'
|
||||||
| 'Checkbox'
|
| 'Checkbox'
|
||||||
|
@ -5,10 +5,8 @@
|
|||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { PropType } from 'vue';
|
import { PropType } from 'vue';
|
||||||
|
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { Spin } from 'ant-design-vue';
|
import { Spin } from 'ant-design-vue';
|
||||||
|
|
||||||
import { SizeEnum } from '/@/enums/sizeEnum';
|
import { SizeEnum } from '/@/enums/sizeEnum';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { VNode, defineComponent } from 'vue';
|
import { VNode, defineComponent } from 'vue';
|
||||||
import type { LoadingProps } from './types';
|
import type { LoadingProps } from './typing';
|
||||||
|
|
||||||
import { createVNode, render, reactive, h } from 'vue';
|
import { createVNode, render, reactive, h } from 'vue';
|
||||||
import Loading from './Loading.vue';
|
import Loading from './Loading.vue';
|
||||||
@ -20,8 +20,8 @@ export function createLoading(props?: Partial<LoadingProps>, target?: HTMLElemen
|
|||||||
|
|
||||||
vm = createVNode(LoadingWrap);
|
vm = createVNode(LoadingWrap);
|
||||||
|
|
||||||
// TODO fix https://github.com/anncwb/vue-vben-admin/issues/438
|
|
||||||
if (wait) {
|
if (wait) {
|
||||||
|
// TODO fix https://github.com/anncwb/vue-vben-admin/issues/438
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
render(vm, document.createElement('div'));
|
render(vm, document.createElement('div'));
|
||||||
}, 0);
|
}, 0);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { unref } from 'vue';
|
import { unref } from 'vue';
|
||||||
import { createLoading } from './createLoading';
|
import { createLoading } from './createLoading';
|
||||||
import type { LoadingProps } from './types';
|
import type { LoadingProps } from './typing';
|
||||||
import type { Ref } from 'vue';
|
import type { Ref } from 'vue';
|
||||||
|
|
||||||
export interface UseLoadingOptions {
|
export interface UseLoadingOptions {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
import { withInstall } from '/@/utils';
|
||||||
export const MarkDown = createAsyncComponent(() => import('./src/Markdown.vue'));
|
import markDown from './src/Markdown.vue';
|
||||||
|
|
||||||
export * from './src/types';
|
export const MarkDown = withInstall(markDown);
|
||||||
|
export * from './src/typing';
|
||||||
|
@ -14,18 +14,17 @@
|
|||||||
} from 'vue';
|
} from 'vue';
|
||||||
import Vditor from 'vditor';
|
import Vditor from 'vditor';
|
||||||
import 'vditor/dist/index.css';
|
import 'vditor/dist/index.css';
|
||||||
|
|
||||||
import { propTypes } from '/@/utils/propTypes';
|
|
||||||
import { useLocale } from '/@/locales/useLocale';
|
import { useLocale } from '/@/locales/useLocale';
|
||||||
import { useModalContext } from '../../Modal';
|
import { useModalContext } from '../../Modal';
|
||||||
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
||||||
|
|
||||||
type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined;
|
type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined;
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
height: propTypes.number.def(360),
|
height: { type: Number, default: 360 },
|
||||||
value: propTypes.string.def(''),
|
value: { type: String, default: '' },
|
||||||
},
|
},
|
||||||
emits: ['change', 'get'],
|
emits: ['change', 'get'],
|
||||||
setup(props, { attrs, emit }) {
|
setup(props, { attrs, emit }) {
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
import { withInstall } from '/@/utils';
|
||||||
import './src/index.less';
|
import './src/index.less';
|
||||||
import BasicModal from './src/BasicModal.vue';
|
import basicModal from './src/BasicModal.vue';
|
||||||
|
|
||||||
export { BasicModal };
|
export const BasicModal = withInstall(basicModal);
|
||||||
export { useModalContext } from './src/hooks/useModalContext';
|
export { useModalContext } from './src/hooks/useModalContext';
|
||||||
export { useModal, useModalInner } from './src/hooks/useModal';
|
export { useModal, useModalInner } from './src/hooks/useModal';
|
||||||
export * from './src/types';
|
export * from './src/typing';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<Modal @cancel="handleCancel" v-bind="getBindValue">
|
<Modal v-bind="getBindValue">
|
||||||
<template #closeIcon v-if="!$slots.closeIcon">
|
<template #closeIcon v-if="!$slots.closeIcon">
|
||||||
<ModalClose
|
<ModalClose
|
||||||
:canFullscreen="getProps.canFullscreen"
|
:canFullscreen="getProps.canFullscreen"
|
||||||
@ -49,7 +49,7 @@
|
|||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { ModalProps, ModalMethods } from './types';
|
import type { ModalProps, ModalMethods } from './typing';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
defineComponent,
|
defineComponent,
|
||||||
@ -62,26 +62,23 @@
|
|||||||
getCurrentInstance,
|
getCurrentInstance,
|
||||||
nextTick,
|
nextTick,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
|
|
||||||
import Modal from './components/Modal';
|
import Modal from './components/Modal';
|
||||||
import ModalWrapper from './components/ModalWrapper.vue';
|
import ModalWrapper from './components/ModalWrapper.vue';
|
||||||
import ModalClose from './components/ModalClose.vue';
|
import ModalClose from './components/ModalClose.vue';
|
||||||
import ModalFooter from './components/ModalFooter.vue';
|
import ModalFooter from './components/ModalFooter.vue';
|
||||||
import ModalHeader from './components/ModalHeader.vue';
|
import ModalHeader from './components/ModalHeader.vue';
|
||||||
|
|
||||||
import { isFunction } from '/@/utils/is';
|
import { isFunction } from '/@/utils/is';
|
||||||
import { deepMerge } from '/@/utils';
|
import { deepMerge } from '/@/utils';
|
||||||
|
|
||||||
import { basicProps } from './props';
|
import { basicProps } from './props';
|
||||||
import { useFullScreen } from './hooks/useModalFullScreen';
|
import { useFullScreen } from './hooks/useModalFullScreen';
|
||||||
|
|
||||||
import { omit } from 'lodash-es';
|
import { omit } from 'lodash-es';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'BasicModal',
|
name: 'BasicModal',
|
||||||
components: { Modal, ModalWrapper, ModalClose, ModalFooter, ModalHeader },
|
components: { Modal, ModalWrapper, ModalClose, ModalFooter, ModalHeader },
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: basicProps,
|
props: basicProps,
|
||||||
emits: ['visible-change', 'height-change', 'cancel', 'ok', 'register'],
|
emits: ['visible-change', 'height-change', 'cancel', 'ok', 'register', 'update:visible'],
|
||||||
setup(props, { emit, attrs }) {
|
setup(props, { emit, attrs }) {
|
||||||
const visibleRef = ref(false);
|
const visibleRef = ref(false);
|
||||||
const propsRef = ref<Partial<ModalProps> | null>(null);
|
const propsRef = ref<Partial<ModalProps> | null>(null);
|
||||||
@ -157,6 +154,7 @@
|
|||||||
() => unref(visibleRef),
|
() => unref(visibleRef),
|
||||||
(v) => {
|
(v) => {
|
||||||
emit('visible-change', v);
|
emit('visible-change', v);
|
||||||
|
emit('update:visible', v);
|
||||||
instance && modalMethods.emitVisible?.(v, instance.uid);
|
instance && modalMethods.emitVisible?.(v, instance.uid);
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (props.scrollTop && v && unref(modalWrapperRef)) {
|
if (props.scrollTop && v && unref(modalWrapperRef)) {
|
||||||
@ -180,7 +178,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
visibleRef.value = false;
|
visibleRef.value = false;
|
||||||
emit('cancel');
|
emit('cancel', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -188,13 +186,13 @@
|
|||||||
*/
|
*/
|
||||||
function setModalProps(props: Partial<ModalProps>): void {
|
function setModalProps(props: Partial<ModalProps>): void {
|
||||||
// Keep the last setModalProps
|
// Keep the last setModalProps
|
||||||
propsRef.value = deepMerge(unref(propsRef) || {}, props);
|
propsRef.value = deepMerge(unref(propsRef) || ({} as any), props);
|
||||||
if (!Reflect.has(props, 'visible')) return;
|
if (!Reflect.has(props, 'visible')) return;
|
||||||
visibleRef.value = !!props.visible;
|
visibleRef.value = !!props.visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleOk() {
|
function handleOk(e: Event) {
|
||||||
emit('ok');
|
emit('ok', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleHeightChange(height: string) {
|
function handleHeightChange(height: string) {
|
||||||
|
@ -20,7 +20,6 @@ export default defineComponent({
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
const propsData = { ...unref(attrs), ...props } as Recordable;
|
const propsData = { ...unref(attrs), ...props } as Recordable;
|
||||||
|
|
||||||
return <Modal {...propsData}>{extendSlots(slots)}</Modal>;
|
return <Modal {...propsData}>{extendSlots(slots)}</Modal>;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
<div :class="getClass">
|
<div :class="getClass">
|
||||||
<template v-if="canFullscreen">
|
<template v-if="canFullscreen">
|
||||||
<FullscreenExitOutlined role="full" @click="handleFullScreen" v-if="fullScreen" />
|
<FullscreenExitOutlined role="full" @click="handleFullScreen" v-if="fullScreen" />
|
||||||
|
|
||||||
<FullscreenOutlined role="close" @click="handleFullScreen" v-else />
|
<FullscreenOutlined role="close" @click="handleFullScreen" v-else />
|
||||||
</template>
|
</template>
|
||||||
<CloseOutlined @click="handleCancel" />
|
<CloseOutlined @click="handleCancel" />
|
||||||
@ -12,14 +11,13 @@
|
|||||||
import { defineComponent, computed } from 'vue';
|
import { defineComponent, computed } from 'vue';
|
||||||
import { FullscreenExitOutlined, FullscreenOutlined, CloseOutlined } from '@ant-design/icons-vue';
|
import { FullscreenExitOutlined, FullscreenOutlined, CloseOutlined } from '@ant-design/icons-vue';
|
||||||
import { useDesign } from '/@/hooks/web/useDesign';
|
import { useDesign } from '/@/hooks/web/useDesign';
|
||||||
import { propTypes } from '/@/utils/propTypes';
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'ModalClose',
|
name: 'ModalClose',
|
||||||
components: { FullscreenExitOutlined, FullscreenOutlined, CloseOutlined },
|
components: { FullscreenExitOutlined, FullscreenOutlined, CloseOutlined },
|
||||||
props: {
|
props: {
|
||||||
canFullscreen: propTypes.bool.def(true),
|
canFullscreen: { type: Boolean, default: true },
|
||||||
fullScreen: propTypes.bool,
|
fullScreen: { type: Boolean },
|
||||||
},
|
},
|
||||||
emits: ['cancel', 'fullscreen'],
|
emits: ['cancel', 'fullscreen'],
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
@ -35,9 +33,10 @@
|
|||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleCancel() {
|
function handleCancel(e: Event) {
|
||||||
emit('cancel');
|
emit('cancel', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFullScreen(e: Event) {
|
function handleFullScreen(e: Event) {
|
||||||
e?.stopPropagation();
|
e?.stopPropagation();
|
||||||
e?.preventDefault();
|
e?.preventDefault();
|
||||||
|
@ -26,13 +26,14 @@
|
|||||||
props: basicProps,
|
props: basicProps,
|
||||||
emits: ['ok', 'cancel'],
|
emits: ['ok', 'cancel'],
|
||||||
setup(_, { emit }) {
|
setup(_, { emit }) {
|
||||||
function handleOk() {
|
function handleOk(e: Event) {
|
||||||
emit('ok');
|
emit('ok', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCancel() {
|
function handleCancel(e: Event) {
|
||||||
emit('cancel');
|
emit('cancel', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { handleOk, handleCancel };
|
return { handleOk, handleCancel };
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { BasicTitle } from '/@/components/Basic';
|
import { BasicTitle } from '/@/components/Basic';
|
||||||
|
|
||||||
import { propTypes } from '/@/utils/propTypes';
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'BasicModalHeader',
|
name: 'BasicModalHeader',
|
||||||
components: { BasicTitle },
|
components: { BasicTitle },
|
||||||
@ -16,7 +15,7 @@
|
|||||||
helpMessage: {
|
helpMessage: {
|
||||||
type: [String, Array] as PropType<string | string[]>,
|
type: [String, Array] as PropType<string | string[]>,
|
||||||
},
|
},
|
||||||
title: propTypes.string,
|
title: { type: String },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -6,9 +6,7 @@
|
|||||||
</ScrollContainer>
|
</ScrollContainer>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { ModalWrapperProps } from '../types';
|
|
||||||
import type { CSSProperties } from 'vue';
|
import type { CSSProperties } from 'vue';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
defineComponent,
|
defineComponent,
|
||||||
computed,
|
computed,
|
||||||
@ -20,31 +18,31 @@
|
|||||||
nextTick,
|
nextTick,
|
||||||
onUnmounted,
|
onUnmounted,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
|
|
||||||
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
|
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
|
||||||
import { ScrollContainer } from '/@/components/Container';
|
import { ScrollContainer } from '/@/components/Container';
|
||||||
|
|
||||||
import { propTypes } from '/@/utils/propTypes';
|
|
||||||
import { createModalContext } from '../hooks/useModalContext';
|
import { createModalContext } from '../hooks/useModalContext';
|
||||||
|
import { useMutationObserver } from '@vueuse/core';
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
loading: { type: Boolean },
|
||||||
|
useWrapper: { type: Boolean, default: true },
|
||||||
|
modalHeaderHeight: { type: Number, default: 57 },
|
||||||
|
modalFooterHeight: { type: Number, default: 74 },
|
||||||
|
minHeight: { type: Number, default: 200 },
|
||||||
|
height: { type: Number },
|
||||||
|
footerOffset: { type: Number, default: 0 },
|
||||||
|
visible: { type: Boolean },
|
||||||
|
fullScreen: { type: Boolean },
|
||||||
|
loadingTip: { type: String },
|
||||||
|
};
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'ModalWrapper',
|
name: 'ModalWrapper',
|
||||||
components: { ScrollContainer },
|
components: { ScrollContainer },
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: {
|
props,
|
||||||
loading: propTypes.bool,
|
|
||||||
useWrapper: propTypes.bool.def(true),
|
|
||||||
modalHeaderHeight: propTypes.number.def(57),
|
|
||||||
modalFooterHeight: propTypes.number.def(74),
|
|
||||||
minHeight: propTypes.number.def(200),
|
|
||||||
height: propTypes.number,
|
|
||||||
footerOffset: propTypes.number.def(0),
|
|
||||||
visible: propTypes.bool,
|
|
||||||
fullScreen: propTypes.bool,
|
|
||||||
loadingTip: propTypes.string,
|
|
||||||
},
|
|
||||||
emits: ['height-change', 'ext-height'],
|
emits: ['height-change', 'ext-height'],
|
||||||
setup(props: ModalWrapperProps, { emit }) {
|
setup(props, { emit }) {
|
||||||
const wrapperRef = ref<ComponentRef>(null);
|
const wrapperRef = ref<ComponentRef>(null);
|
||||||
const spinRef = ref<ElRef>(null);
|
const spinRef = ref<ElRef>(null);
|
||||||
const realHeightRef = ref(0);
|
const realHeightRef = ref(0);
|
||||||
@ -56,6 +54,17 @@
|
|||||||
|
|
||||||
useWindowSizeFn(setModalHeight.bind(null, false));
|
useWindowSizeFn(setModalHeight.bind(null, false));
|
||||||
|
|
||||||
|
useMutationObserver(
|
||||||
|
spinRef,
|
||||||
|
() => {
|
||||||
|
setModalHeight();
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attributes: true,
|
||||||
|
subtree: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
createModalContext({
|
createModalContext({
|
||||||
redoModalHeight: setModalHeight,
|
redoModalHeight: setModalHeight,
|
||||||
});
|
});
|
||||||
@ -63,8 +72,7 @@
|
|||||||
const spinStyle = computed((): CSSProperties => {
|
const spinStyle = computed((): CSSProperties => {
|
||||||
return {
|
return {
|
||||||
minHeight: `${props.minHeight}px`,
|
minHeight: `${props.minHeight}px`,
|
||||||
// padding 28
|
[props.fullScreen ? 'height' : 'maxHeight']: `${unref(realHeightRef)}px`,
|
||||||
maxHeight: `${unref(realHeightRef)}px`,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -87,7 +95,6 @@
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const { modalHeaderHeight, modalFooterHeight } = props;
|
const { modalHeaderHeight, modalFooterHeight } = props;
|
||||||
emit('ext-height', modalHeaderHeight + modalFooterHeight);
|
emit('ext-height', modalHeaderHeight + modalFooterHeight);
|
||||||
// listenElResize();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
@ -4,8 +4,7 @@ import type {
|
|||||||
ModalProps,
|
ModalProps,
|
||||||
ReturnMethods,
|
ReturnMethods,
|
||||||
UseModalInnerReturnType,
|
UseModalInnerReturnType,
|
||||||
} from '../types';
|
} from '../typing';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ref,
|
ref,
|
||||||
onUnmounted,
|
onUnmounted,
|
||||||
@ -20,10 +19,10 @@ import { isProdMode } from '/@/utils/env';
|
|||||||
import { isFunction } from '/@/utils/is';
|
import { isFunction } from '/@/utils/is';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
import { tryOnUnmounted } from '@vueuse/core';
|
import { tryOnUnmounted } from '@vueuse/core';
|
||||||
|
|
||||||
import { error } from '/@/utils/log';
|
import { error } from '/@/utils/log';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
const dataTransferRef = reactive<any>({});
|
|
||||||
|
const dataTransfer = reactive<any>({});
|
||||||
|
|
||||||
const visibleData = reactive<{ [key: number]: boolean }>({});
|
const visibleData = reactive<{ [key: number]: boolean }>({});
|
||||||
|
|
||||||
@ -31,29 +30,31 @@ const visibleData = reactive<{ [key: number]: boolean }>({});
|
|||||||
* @description: Applicable to independent modal and call outside
|
* @description: Applicable to independent modal and call outside
|
||||||
*/
|
*/
|
||||||
export function useModal(): UseModalReturnType {
|
export function useModal(): UseModalReturnType {
|
||||||
const modalRef = ref<Nullable<ModalMethods>>(null);
|
const modal = ref<Nullable<ModalMethods>>(null);
|
||||||
const loadedRef = ref<Nullable<boolean>>(false);
|
const loaded = ref<Nullable<boolean>>(false);
|
||||||
const uidRef = ref<string>('');
|
const uid = ref<string>('');
|
||||||
|
|
||||||
function register(modalMethod: ModalMethods, uuid: string) {
|
function register(modalMethod: ModalMethods, uuid: string) {
|
||||||
uidRef.value = uuid;
|
if (!getCurrentInstance()) {
|
||||||
|
throw new Error('useModal() can only be used inside setup() or functional components!');
|
||||||
|
}
|
||||||
|
uid.value = uuid;
|
||||||
isProdMode() &&
|
isProdMode() &&
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
modalRef.value = null;
|
modal.value = null;
|
||||||
loadedRef.value = false;
|
loaded.value = false;
|
||||||
dataTransferRef[unref(uidRef)] = null;
|
dataTransfer[unref(uid)] = null;
|
||||||
});
|
});
|
||||||
if (unref(loadedRef) && isProdMode() && modalMethod === unref(modalRef)) return;
|
if (unref(loaded) && isProdMode() && modalMethod === unref(modal)) return;
|
||||||
|
|
||||||
modalRef.value = modalMethod;
|
modal.value = modalMethod;
|
||||||
modalMethod.emitVisible = (visible: boolean, uid: number) => {
|
modalMethod.emitVisible = (visible: boolean, uid: number) => {
|
||||||
visibleData[uid] = visible;
|
visibleData[uid] = visible;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const getInstance = () => {
|
const getInstance = () => {
|
||||||
const instance = unref(modalRef);
|
const instance = unref(modal);
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
error('useModal instance is undefined!');
|
error('useModal instance is undefined!');
|
||||||
}
|
}
|
||||||
@ -66,7 +67,7 @@ export function useModal(): UseModalReturnType {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getVisible: computed((): boolean => {
|
getVisible: computed((): boolean => {
|
||||||
return visibleData[~~unref(uidRef)];
|
return visibleData[~~unref(uid)];
|
||||||
}),
|
}),
|
||||||
|
|
||||||
redoModalHeight: () => {
|
redoModalHeight: () => {
|
||||||
@ -79,15 +80,15 @@ export function useModal(): UseModalReturnType {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
const id = unref(uid);
|
||||||
if (openOnSet) {
|
if (openOnSet) {
|
||||||
dataTransferRef[unref(uidRef)] = null;
|
dataTransfer[id] = null;
|
||||||
dataTransferRef[unref(uidRef)] = toRaw(data);
|
dataTransfer[id] = toRaw(data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const equal = isEqual(toRaw(dataTransferRef[unref(uidRef)]), toRaw(data));
|
const equal = isEqual(toRaw(dataTransfer[id]), toRaw(data));
|
||||||
if (!equal) {
|
if (!equal) {
|
||||||
dataTransferRef[unref(uidRef)] = toRaw(data);
|
dataTransfer[id] = toRaw(data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -103,9 +104,6 @@ export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => {
|
|||||||
const currentInstance = getCurrentInstance();
|
const currentInstance = getCurrentInstance();
|
||||||
const uidRef = ref<string>('');
|
const uidRef = ref<string>('');
|
||||||
|
|
||||||
// currentInstall.type.emits = [...currentInstall.type.emits, 'register'];
|
|
||||||
// Object.assign(currentInstall.type.emits, ['register']);
|
|
||||||
|
|
||||||
const getInstance = () => {
|
const getInstance = () => {
|
||||||
const instance = unref(modalInstanceRef);
|
const instance = unref(modalInstanceRef);
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
@ -125,7 +123,7 @@ export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
const data = dataTransferRef[unref(uidRef)];
|
const data = dataTransfer[unref(uidRef)];
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
if (!callbackFn || !isFunction(callbackFn)) return;
|
if (!callbackFn || !isFunction(callbackFn)) return;
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
|
@ -12,7 +12,6 @@ export function useFullScreen(context: UseFullScreenContext) {
|
|||||||
|
|
||||||
const getWrapClassName = computed(() => {
|
const getWrapClassName = computed(() => {
|
||||||
const clsName = unref(context.wrapClassName) || '';
|
const clsName = unref(context.wrapClassName) || '';
|
||||||
|
|
||||||
return unref(fullScreenRef) ? `fullscreen-modal ${clsName} ` : unref(clsName);
|
return unref(fullScreenRef) ? `fullscreen-modal ${clsName} ` : unref(clsName);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,45 +1,44 @@
|
|||||||
import type { PropType, CSSProperties } from 'vue';
|
import type { PropType, CSSProperties } from 'vue';
|
||||||
|
import type { ModalWrapperProps } from './typing';
|
||||||
import { ButtonProps } from 'ant-design-vue/es/button/buttonTypes';
|
import { ButtonProps } from 'ant-design-vue/es/button/buttonTypes';
|
||||||
|
|
||||||
import { useI18n } from '/@/hooks/web/useI18n';
|
import { useI18n } from '/@/hooks/web/useI18n';
|
||||||
import { propTypes, VueNode } from '/@/utils/propTypes';
|
|
||||||
import type { ModalWrapperProps } from './types';
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
export const modalProps = {
|
export const modalProps = {
|
||||||
visible: propTypes.bool,
|
visible: { type: Boolean },
|
||||||
scrollTop: propTypes.bool.def(true),
|
scrollTop: { type: Boolean, default: true },
|
||||||
height: propTypes.number,
|
height: { type: Number },
|
||||||
minHeight: propTypes.number,
|
minHeight: { type: Number },
|
||||||
// open drag
|
// open drag
|
||||||
draggable: propTypes.bool.def(true),
|
draggable: { type: Boolean, default: true },
|
||||||
centered: propTypes.bool,
|
centered: { type: Boolean },
|
||||||
cancelText: propTypes.string.def(t('common.cancelText')),
|
cancelText: { type: String, default: t('common.cancelText') },
|
||||||
okText: propTypes.string.def(t('common.okText')),
|
okText: { type: String, default: t('common.okText') },
|
||||||
|
|
||||||
closeFunc: Function as PropType<() => Promise<boolean>>,
|
closeFunc: Function as PropType<() => Promise<boolean>>,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const basicProps = Object.assign({}, modalProps, {
|
export const basicProps = Object.assign({}, modalProps, {
|
||||||
defaultFullscreen: propTypes.bool,
|
defaultFullscreen: { type: Boolean },
|
||||||
// Can it be full screen
|
// Can it be full screen
|
||||||
canFullscreen: propTypes.bool.def(true),
|
canFullscreen: { type: Boolean, default: true },
|
||||||
// After enabling the wrapper, the bottom can be increased in height
|
// After enabling the wrapper, the bottom can be increased in height
|
||||||
wrapperFooterOffset: propTypes.number.def(0),
|
wrapperFooterOffset: { type: Number, default: 0 },
|
||||||
// Warm reminder message
|
// Warm reminder message
|
||||||
helpMessage: [String, Array] as PropType<string | string[]>,
|
helpMessage: [String, Array] as PropType<string | string[]>,
|
||||||
// Whether to setting wrapper
|
// Whether to setting wrapper
|
||||||
useWrapper: propTypes.bool.def(true),
|
useWrapper: { type: Boolean, default: true },
|
||||||
loading: propTypes.bool,
|
loading: { type: Boolean },
|
||||||
loadingTip: propTypes.string,
|
loadingTip: { type: String },
|
||||||
/**
|
/**
|
||||||
* @description: Show close button
|
* @description: Show close button
|
||||||
*/
|
*/
|
||||||
showCancelBtn: propTypes.bool.def(true),
|
showCancelBtn: { type: Boolean, default: true },
|
||||||
/**
|
/**
|
||||||
* @description: Show confirmation button
|
* @description: Show confirmation button
|
||||||
*/
|
*/
|
||||||
showOkBtn: propTypes.bool.def(true),
|
showOkBtn: { type: Boolean, default: true },
|
||||||
|
|
||||||
wrapperProps: Object as PropType<Partial<ModalWrapperProps>>,
|
wrapperProps: Object as PropType<Partial<ModalWrapperProps>>,
|
||||||
|
|
||||||
@ -47,38 +46,38 @@ export const basicProps = Object.assign({}, modalProps, {
|
|||||||
|
|
||||||
bodyStyle: Object as PropType<CSSProperties>,
|
bodyStyle: Object as PropType<CSSProperties>,
|
||||||
|
|
||||||
closable: propTypes.bool.def(true),
|
closable: { type: Boolean, default: true },
|
||||||
|
|
||||||
closeIcon: Object as PropType<VueNode>,
|
closeIcon: Object as PropType<VueNode>,
|
||||||
|
|
||||||
confirmLoading: propTypes.bool,
|
confirmLoading: { type: Boolean },
|
||||||
|
|
||||||
destroyOnClose: propTypes.bool,
|
destroyOnClose: { type: Boolean },
|
||||||
|
|
||||||
footer: Object as PropType<VueNode>,
|
footer: Object as PropType<VueNode>,
|
||||||
|
|
||||||
getContainer: Function as PropType<() => any>,
|
getContainer: Function as PropType<() => any>,
|
||||||
|
|
||||||
mask: propTypes.bool.def(true),
|
mask: { type: Boolean, default: true },
|
||||||
|
|
||||||
maskClosable: propTypes.bool.def(true),
|
maskClosable: { type: Boolean, default: true },
|
||||||
keyboard: propTypes.bool.def(true),
|
keyboard: { type: Boolean, default: true },
|
||||||
|
|
||||||
maskStyle: Object as PropType<CSSProperties>,
|
maskStyle: Object as PropType<CSSProperties>,
|
||||||
|
|
||||||
okType: propTypes.string.def('primary'),
|
okType: { type: String, default: 'primary' },
|
||||||
|
|
||||||
okButtonProps: Object as PropType<ButtonProps>,
|
okButtonProps: Object as PropType<ButtonProps>,
|
||||||
|
|
||||||
cancelButtonProps: Object as PropType<ButtonProps>,
|
cancelButtonProps: Object as PropType<ButtonProps>,
|
||||||
|
|
||||||
title: propTypes.string,
|
title: { type: String },
|
||||||
|
|
||||||
visible: propTypes.bool,
|
visible: { type: Boolean },
|
||||||
|
|
||||||
width: [String, Number] as PropType<string | number>,
|
width: [String, Number] as PropType<string | number>,
|
||||||
|
|
||||||
wrapClassName: propTypes.string,
|
wrapClassName: { type: String },
|
||||||
|
|
||||||
zIndex: propTypes.number,
|
zIndex: { type: Number },
|
||||||
});
|
});
|
||||||
|
@ -1,2 +1,7 @@
|
|||||||
export { default as PageFooter } from './src/PageFooter.vue';
|
import { withInstall } from '/@/utils';
|
||||||
export { default as PageWrapper } from './src/PageWrapper.vue';
|
|
||||||
|
import pageFooter from './src/PageFooter.vue';
|
||||||
|
import pageWrapper from './src/PageWrapper.vue';
|
||||||
|
|
||||||
|
export const PageFooter = withInstall(pageFooter);
|
||||||
|
export const PageWrapper = withInstall(pageWrapper);
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||||
import { useDesign } from '/@/hooks/web/useDesign';
|
import { useDesign } from '/@/hooks/web/useDesign';
|
||||||
|
|
||||||
|
@ -44,6 +44,8 @@
|
|||||||
import { omit } from 'lodash-es';
|
import { omit } from 'lodash-es';
|
||||||
import { PageHeader } from 'ant-design-vue';
|
import { PageHeader } from 'ant-design-vue';
|
||||||
import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
|
import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
|
||||||
|
import { useLayoutHeight } from '/@/layouts/default/content/useContentViewHeight';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'PageWrapper',
|
name: 'PageWrapper',
|
||||||
components: { PageFooter, PageHeader },
|
components: { PageFooter, PageHeader },
|
||||||
@ -67,6 +69,7 @@
|
|||||||
const footerHeight = ref(0);
|
const footerHeight = ref(0);
|
||||||
const { prefixCls, prefixVar } = useDesign('page-wrapper');
|
const { prefixCls, prefixVar } = useDesign('page-wrapper');
|
||||||
const { contentHeight, setPageHeight, pageHeight } = usePageContext();
|
const { contentHeight, setPageHeight, pageHeight } = usePageContext();
|
||||||
|
const { footerHeightRef } = useLayoutHeight();
|
||||||
|
|
||||||
const getClass = computed(() => {
|
const getClass = computed(() => {
|
||||||
return [
|
return [
|
||||||
@ -93,7 +96,6 @@
|
|||||||
...contentStyle,
|
...contentStyle,
|
||||||
minHeight: height,
|
minHeight: height,
|
||||||
...(fixedHeight ? { height } : {}),
|
...(fixedHeight ? { height } : {}),
|
||||||
paddingBottom: `${unref(footerHeight)}px`,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -109,7 +111,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => [contentHeight?.value, getShowFooter.value],
|
() => [contentHeight?.value, getShowFooter.value, footerHeightRef.value],
|
||||||
() => {
|
() => {
|
||||||
calcContentHeight();
|
calcContentHeight();
|
||||||
},
|
},
|
||||||
@ -163,7 +165,36 @@
|
|||||||
const contentMarginTop = Number(marginTop.replace(/[^\d]/g, ''));
|
const contentMarginTop = Number(marginTop.replace(/[^\d]/g, ''));
|
||||||
subtractHeight += contentMarginTop;
|
subtractHeight += contentMarginTop;
|
||||||
}
|
}
|
||||||
setPageHeight?.(unref(contentHeight) - unref(footerHeight) - headerHeight - subtractHeight);
|
|
||||||
|
// fix: wrapper marginTop and marginBottom value
|
||||||
|
let wrapperSubtractHeight = 0;
|
||||||
|
let wrapperMarginBottom = ZERO_PX;
|
||||||
|
let wrapperMarginTop = ZERO_PX;
|
||||||
|
const wrapperClassElments = document.querySelectorAll(`.${prefixVar}-page-wrapper`);
|
||||||
|
if (wrapperClassElments && wrapperClassElments.length > 0) {
|
||||||
|
const contentEl = wrapperClassElments[0];
|
||||||
|
const cssStyle = getComputedStyle(contentEl);
|
||||||
|
wrapperMarginBottom = cssStyle?.marginBottom ?? ZERO_PX;
|
||||||
|
wrapperMarginTop = cssStyle?.marginTop ?? ZERO_PX;
|
||||||
|
}
|
||||||
|
if (wrapperMarginBottom) {
|
||||||
|
const contentMarginBottom = Number(wrapperMarginBottom.replace(/[^\d]/g, ''));
|
||||||
|
wrapperSubtractHeight += contentMarginBottom;
|
||||||
|
}
|
||||||
|
if (wrapperMarginTop) {
|
||||||
|
const contentMarginTop = Number(wrapperMarginTop.replace(/[^\d]/g, ''));
|
||||||
|
wrapperSubtractHeight += contentMarginTop;
|
||||||
|
}
|
||||||
|
let height =
|
||||||
|
unref(contentHeight) -
|
||||||
|
unref(footerHeight) -
|
||||||
|
headerHeight -
|
||||||
|
subtractHeight -
|
||||||
|
wrapperSubtractHeight;
|
||||||
|
if (unref(getShowFooter)) {
|
||||||
|
height -= unref(footerHeightRef);
|
||||||
|
}
|
||||||
|
setPageHeight?.(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -1 +1,2 @@
|
|||||||
export { default as ImagePreview } from './src/Preview.vue';
|
export { default as ImagePreview } from './src/Preview.vue';
|
||||||
|
export { createImgPreview } from './src/functional';
|
||||||
|
436
src/components/Preview/src/Functional.vue
Normal file
436
src/components/Preview/src/Functional.vue
Normal file
@ -0,0 +1,436 @@
|
|||||||
|
<script lang="tsx">
|
||||||
|
import { defineComponent, ref, unref, computed, reactive, watchEffect } from 'vue';
|
||||||
|
import { Props } from './typing';
|
||||||
|
import { CloseOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
|
||||||
|
import resumeSvg from '/@/assets/svg/preview/resume.svg';
|
||||||
|
import rotateSvg from '/@/assets/svg/preview/p-rotate.svg';
|
||||||
|
import scaleSvg from '/@/assets/svg/preview/scale.svg';
|
||||||
|
import unScaleSvg from '/@/assets/svg/preview/unscale.svg';
|
||||||
|
import unRotateSvg from '/@/assets/svg/preview/unrotate.svg';
|
||||||
|
|
||||||
|
enum StatueEnum {
|
||||||
|
LOADING,
|
||||||
|
DONE,
|
||||||
|
FAIL,
|
||||||
|
}
|
||||||
|
interface ImgState {
|
||||||
|
currentUrl: string;
|
||||||
|
imgScale: number;
|
||||||
|
imgRotate: number;
|
||||||
|
imgTop: number;
|
||||||
|
imgLeft: number;
|
||||||
|
currentIndex: number;
|
||||||
|
status: StatueEnum;
|
||||||
|
moveX: number;
|
||||||
|
moveY: number;
|
||||||
|
show: boolean;
|
||||||
|
}
|
||||||
|
const props = {
|
||||||
|
show: {
|
||||||
|
type: Boolean as PropType<boolean>,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
imageList: {
|
||||||
|
type: [Array] as PropType<string[]>,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
index: {
|
||||||
|
type: Number as PropType<number>,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const prefixCls = 'img-preview';
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ImagePreview',
|
||||||
|
props,
|
||||||
|
setup(props: Props) {
|
||||||
|
const imgState = reactive<ImgState>({
|
||||||
|
currentUrl: '',
|
||||||
|
imgScale: 1,
|
||||||
|
imgRotate: 0,
|
||||||
|
imgTop: 0,
|
||||||
|
imgLeft: 0,
|
||||||
|
status: StatueEnum.LOADING,
|
||||||
|
currentIndex: 0,
|
||||||
|
moveX: 0,
|
||||||
|
moveY: 0,
|
||||||
|
show: props.show,
|
||||||
|
});
|
||||||
|
|
||||||
|
const wrapElRef = ref<HTMLDivElement | null>(null);
|
||||||
|
const imgElRef = ref<HTMLImageElement | null>(null);
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
function init() {
|
||||||
|
initMouseWheel();
|
||||||
|
const { index, imageList } = props;
|
||||||
|
|
||||||
|
if (!imageList || !imageList.length) {
|
||||||
|
throw new Error('imageList is undefined');
|
||||||
|
}
|
||||||
|
imgState.currentIndex = index;
|
||||||
|
handleIChangeImage(imageList[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
function initState() {
|
||||||
|
imgState.imgScale = 1;
|
||||||
|
imgState.imgRotate = 0;
|
||||||
|
imgState.imgTop = 0;
|
||||||
|
imgState.imgLeft = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化鼠标滚轮事件
|
||||||
|
function initMouseWheel() {
|
||||||
|
const wrapEl = unref(wrapElRef);
|
||||||
|
if (!wrapEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(wrapEl as any).onmousewheel = scrollFunc;
|
||||||
|
// 火狐浏览器没有onmousewheel事件,用DOMMouseScroll代替
|
||||||
|
document.body.addEventListener('DOMMouseScroll', scrollFunc);
|
||||||
|
// 禁止火狐浏览器下拖拽图片的默认事件
|
||||||
|
document.ondragstart = function () {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听鼠标滚轮
|
||||||
|
function scrollFunc(e: any) {
|
||||||
|
e = e || window.event;
|
||||||
|
e.delta = e.wheelDelta || -e.detail;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.delta > 0) {
|
||||||
|
// 滑轮向上滚动
|
||||||
|
scaleFunc(0.015);
|
||||||
|
}
|
||||||
|
if (e.delta < 0) {
|
||||||
|
// 滑轮向下滚动
|
||||||
|
scaleFunc(-0.015);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 缩放函数
|
||||||
|
function scaleFunc(num: number) {
|
||||||
|
if (imgState.imgScale <= 0.2 && num < 0) return;
|
||||||
|
imgState.imgScale += num;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 旋转图片
|
||||||
|
function rotateFunc(deg: number) {
|
||||||
|
imgState.imgRotate += deg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 鼠标事件
|
||||||
|
function handleMouseUp() {
|
||||||
|
const imgEl = unref(imgElRef);
|
||||||
|
if (!imgEl) return;
|
||||||
|
imgEl.onmousemove = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更换图片
|
||||||
|
function handleIChangeImage(url: string) {
|
||||||
|
imgState.status = StatueEnum.LOADING;
|
||||||
|
const img = new Image();
|
||||||
|
img.src = url;
|
||||||
|
img.onload = () => {
|
||||||
|
imgState.currentUrl = url;
|
||||||
|
imgState.status = StatueEnum.DONE;
|
||||||
|
};
|
||||||
|
img.onerror = () => {
|
||||||
|
imgState.status = StatueEnum.FAIL;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭
|
||||||
|
function handleClose(e: MouseEvent) {
|
||||||
|
e && e.stopPropagation();
|
||||||
|
imgState.show = false;
|
||||||
|
// 移除火狐浏览器下的鼠标滚动事件
|
||||||
|
document.body.removeEventListener('DOMMouseScroll', scrollFunc);
|
||||||
|
// 恢复火狐及Safari浏览器下的图片拖拽
|
||||||
|
document.ondragstart = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图片复原
|
||||||
|
function resume() {
|
||||||
|
initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上一页下一页
|
||||||
|
function handleChange(direction: 'left' | 'right') {
|
||||||
|
const { currentIndex } = imgState;
|
||||||
|
const { imageList } = props;
|
||||||
|
if (direction === 'left') {
|
||||||
|
imgState.currentIndex--;
|
||||||
|
if (currentIndex <= 0) {
|
||||||
|
imgState.currentIndex = imageList.length - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (direction === 'right') {
|
||||||
|
imgState.currentIndex++;
|
||||||
|
if (currentIndex >= imageList.length - 1) {
|
||||||
|
imgState.currentIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleIChangeImage(imageList[imgState.currentIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAddMoveListener(e: MouseEvent) {
|
||||||
|
e = e || window.event;
|
||||||
|
imgState.moveX = e.clientX;
|
||||||
|
imgState.moveY = e.clientY;
|
||||||
|
const imgEl = unref(imgElRef);
|
||||||
|
if (imgEl) {
|
||||||
|
imgEl.onmousemove = moveFunc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveFunc(e: MouseEvent) {
|
||||||
|
e = e || window.event;
|
||||||
|
e.preventDefault();
|
||||||
|
const movementX = e.clientX - imgState.moveX;
|
||||||
|
const movementY = e.clientY - imgState.moveY;
|
||||||
|
imgState.imgLeft += movementX;
|
||||||
|
imgState.imgTop += movementY;
|
||||||
|
imgState.moveX = e.clientX;
|
||||||
|
imgState.moveY = e.clientY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取图片样式
|
||||||
|
const getImageStyle = computed(() => {
|
||||||
|
const { imgScale, imgRotate, imgTop, imgLeft } = imgState;
|
||||||
|
return {
|
||||||
|
transform: `scale(${imgScale}) rotate(${imgRotate}deg)`,
|
||||||
|
marginTop: `${imgTop}px`,
|
||||||
|
marginLeft: `${imgLeft}px`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const getIsMultipleImage = computed(() => {
|
||||||
|
const { imageList } = props;
|
||||||
|
return imageList.length > 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (props.show) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
if (props.imageList) {
|
||||||
|
initState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const renderClose = () => {
|
||||||
|
return (
|
||||||
|
<div class={`${prefixCls}__close`} onClick={handleClose}>
|
||||||
|
<CloseOutlined class={`${prefixCls}__close-icon`} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderIndex = () => {
|
||||||
|
if (!unref(getIsMultipleImage)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const { currentIndex } = imgState;
|
||||||
|
const { imageList } = props;
|
||||||
|
return (
|
||||||
|
<div class={`${prefixCls}__index`}>
|
||||||
|
{currentIndex + 1} / {imageList.length}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderController = () => {
|
||||||
|
return (
|
||||||
|
<div class={`${prefixCls}__controller`}>
|
||||||
|
<div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(-0.15)}>
|
||||||
|
<img src={unScaleSvg} />
|
||||||
|
</div>
|
||||||
|
<div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(0.15)}>
|
||||||
|
<img src={scaleSvg} />
|
||||||
|
</div>
|
||||||
|
<div class={`${prefixCls}__controller-item`} onClick={resume}>
|
||||||
|
<img src={resumeSvg} />
|
||||||
|
</div>
|
||||||
|
<div class={`${prefixCls}__controller-item`} onClick={() => rotateFunc(-90)}>
|
||||||
|
<img src={unRotateSvg} />
|
||||||
|
</div>
|
||||||
|
<div class={`${prefixCls}__controller-item`} onClick={() => rotateFunc(90)}>
|
||||||
|
<img src={rotateSvg} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderArrow = (direction: 'left' | 'right') => {
|
||||||
|
if (!unref(getIsMultipleImage)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div class={[`${prefixCls}__arrow`, direction]} onClick={() => handleChange(direction)}>
|
||||||
|
{direction === 'left' ? <LeftOutlined /> : <RightOutlined />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
return (
|
||||||
|
imgState.show && (
|
||||||
|
<div class={prefixCls} ref={wrapElRef} onMouseup={handleMouseUp}>
|
||||||
|
<div class={`${prefixCls}-content`}>
|
||||||
|
{/*<Spin*/}
|
||||||
|
{/* indicator={<LoadingOutlined style="font-size: 24px" spin />}*/}
|
||||||
|
{/* spinning={true}*/}
|
||||||
|
{/* class={[*/}
|
||||||
|
{/* `${prefixCls}-image`,*/}
|
||||||
|
{/* {*/}
|
||||||
|
{/* hidden: imgState.status !== StatueEnum.LOADING,*/}
|
||||||
|
{/* },*/}
|
||||||
|
{/* ]}*/}
|
||||||
|
{/*/>*/}
|
||||||
|
<img
|
||||||
|
style={unref(getImageStyle)}
|
||||||
|
class={[
|
||||||
|
`${prefixCls}-image`,
|
||||||
|
imgState.status === StatueEnum.DONE ? '' : 'hidden',
|
||||||
|
]}
|
||||||
|
ref={imgElRef}
|
||||||
|
src={imgState.currentUrl}
|
||||||
|
onMousedown={handleAddMoveListener}
|
||||||
|
/>
|
||||||
|
{renderClose()}
|
||||||
|
{renderIndex()}
|
||||||
|
{renderController()}
|
||||||
|
{renderArrow('left')}
|
||||||
|
{renderArrow('right')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="less">
|
||||||
|
.img-preview {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: @preview-comp-z-index;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
color: @white;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-image {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__close {
|
||||||
|
position: absolute;
|
||||||
|
top: -40px;
|
||||||
|
right: -40px;
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
overflow: hidden;
|
||||||
|
color: @white;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 46px;
|
||||||
|
left: 16px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__index {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 5%;
|
||||||
|
left: 50%;
|
||||||
|
padding: 0 22px;
|
||||||
|
font-size: 16px;
|
||||||
|
background: rgba(109, 109, 109, 0.6);
|
||||||
|
border-radius: 15px;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__controller {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10%;
|
||||||
|
left: 50%;
|
||||||
|
display: flex;
|
||||||
|
width: 260px;
|
||||||
|
height: 44px;
|
||||||
|
padding: 0 22px;
|
||||||
|
margin-left: -139px;
|
||||||
|
background: rgba(109, 109, 109, 0.6);
|
||||||
|
border-radius: 22px;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 9px;
|
||||||
|
font-size: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__arrow {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
font-size: 28px;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.left {
|
||||||
|
left: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.right {
|
||||||
|
right: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
20
src/components/Preview/src/functional.ts
Normal file
20
src/components/Preview/src/functional.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import type { Options, Props } from './typing';
|
||||||
|
import ImgPreview from './Functional.vue';
|
||||||
|
import { isClient } from '/@/utils/is';
|
||||||
|
import { createVNode, render } from 'vue';
|
||||||
|
|
||||||
|
let instance: ReturnType<typeof createVNode> | null = null;
|
||||||
|
export function createImgPreview(options: Options) {
|
||||||
|
if (!isClient) return;
|
||||||
|
const { imageList, show = true, index = 0 } = options;
|
||||||
|
|
||||||
|
const propsData: Partial<Props> = {};
|
||||||
|
const container = document.createElement('div');
|
||||||
|
propsData.imageList = imageList;
|
||||||
|
propsData.show = show;
|
||||||
|
propsData.index = index;
|
||||||
|
|
||||||
|
instance = createVNode(ImgPreview, propsData);
|
||||||
|
render(instance, container);
|
||||||
|
document.body.appendChild(container);
|
||||||
|
}
|
30
src/components/Preview/src/typing.ts
Normal file
30
src/components/Preview/src/typing.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
export interface Options {
|
||||||
|
show?: boolean;
|
||||||
|
imageList: string[];
|
||||||
|
index?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
show: boolean;
|
||||||
|
instance: Props;
|
||||||
|
imageList: string[];
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ImageProps {
|
||||||
|
alt?: string;
|
||||||
|
fallback?: string;
|
||||||
|
src: string;
|
||||||
|
width: string | number;
|
||||||
|
height?: string | number;
|
||||||
|
placeholder?: string | boolean;
|
||||||
|
preview?:
|
||||||
|
| boolean
|
||||||
|
| {
|
||||||
|
visible?: boolean;
|
||||||
|
onVisibleChange?: (visible: boolean, prevVisible: boolean) => void;
|
||||||
|
getContainer: string | HTMLElement | (() => HTMLElement);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ImageItem = string | ImageProps;
|
@ -1,3 +1,5 @@
|
|||||||
export { default as QrCode } from './src/Qrcode.vue';
|
import { withInstall } from '/@/utils';
|
||||||
|
import qrCode from './src/Qrcode.vue';
|
||||||
|
|
||||||
export * from './src/types';
|
export const QrCode = withInstall(qrCode);
|
||||||
|
export * from './src/typing';
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
import { toCanvas, QRCodeRenderersOptions, LogoType } from './qrcodePlus';
|
import { toCanvas, QRCodeRenderersOptions, LogoType } from './qrcodePlus';
|
||||||
import { toDataURL } from 'qrcode';
|
import { toDataURL } from 'qrcode';
|
||||||
import { downloadByUrl } from '/@/utils/file/download';
|
import { downloadByUrl } from '/@/utils/file/download';
|
||||||
import { QrcodeDoneEventParams } from './types';
|
import { QrcodeDoneEventParams } from './typing';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'QrCode',
|
name: 'QrCode',
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { toCanvas } from 'qrcode';
|
import { toCanvas } from 'qrcode';
|
||||||
import type { QRCodeRenderersOptions } from 'qrcode';
|
import type { QRCodeRenderersOptions } from 'qrcode';
|
||||||
import { RenderQrCodeParams, ContentType } from './types';
|
import { RenderQrCodeParams, ContentType } from './typing';
|
||||||
export const renderQrCode = ({ canvas, content, width = 0, options = {} }: RenderQrCodeParams) => {
|
export const renderQrCode = ({ canvas, content, width = 0, options = {} }: RenderQrCodeParams) => {
|
||||||
// 容错率,默认对内容少的二维码采用高容错率,内容多的二维码采用低容错率
|
// 容错率,默认对内容少的二维码采用高容错率,内容多的二维码采用低容错率
|
||||||
options.errorCorrectionLevel = options.errorCorrectionLevel || getErrorCorrectionLevel(content);
|
options.errorCorrectionLevel = options.errorCorrectionLevel || getErrorCorrectionLevel(content);
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import { isString } from '/@/utils/is';
|
import { isString } from '/@/utils/is';
|
||||||
import { RenderQrCodeParams, LogoType } from './types';
|
import { RenderQrCodeParams, LogoType } from './typing';
|
||||||
export const drawLogo = ({ canvas, logo }: RenderQrCodeParams) => {
|
export const drawLogo = ({ canvas, logo }: RenderQrCodeParams) => {
|
||||||
if (!logo) {
|
if (!logo) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
resolve((canvas as HTMLCanvasElement).toDataURL());
|
resolve((canvas as HTMLCanvasElement).toDataURL());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const canvasWidth = (canvas as HTMLCanvasElement).width;
|
const canvasWidth = (canvas as HTMLCanvasElement).width;
|
||||||
const {
|
const {
|
||||||
logoSize = 0.15,
|
logoSize = 0.15,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
// 参考 qr-code-with-logo 进行ts版本修改
|
// 参考 qr-code-with-logo 进行ts版本修改
|
||||||
import { toCanvas } from './toCanvas';
|
import { toCanvas } from './toCanvas';
|
||||||
export * from './types';
|
export * from './typing';
|
||||||
|
|
||||||
export { toCanvas };
|
export { toCanvas };
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { renderQrCode } from './drawCanvas';
|
import { renderQrCode } from './drawCanvas';
|
||||||
import { drawLogo } from './drawLogo';
|
import { drawLogo } from './drawLogo';
|
||||||
import { RenderQrCodeParams } from './types';
|
import { RenderQrCodeParams } from './typing';
|
||||||
export const toCanvas = (options: RenderQrCodeParams) => {
|
export const toCanvas = (options: RenderQrCodeParams) => {
|
||||||
return renderQrCode(options)
|
return renderQrCode(options)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
import { useDesign } from '/@/hooks/web/useDesign';
|
import { useDesign } from '/@/hooks/web/useDesign';
|
||||||
import { propTypes } from '/@/utils/propTypes';
|
import { propTypes } from '/@/utils/propTypes';
|
||||||
import { createSimpleRootMenuContext } from './useSimpleMenuContext';
|
import { createSimpleRootMenuContext } from './useSimpleMenuContext';
|
||||||
import Mitt from '/@/utils/mitt';
|
import mitt from '/@/utils/mitt';
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Menu',
|
name: 'Menu',
|
||||||
props: {
|
props: {
|
||||||
@ -44,7 +44,7 @@
|
|||||||
},
|
},
|
||||||
emits: ['select', 'open-change'],
|
emits: ['select', 'open-change'],
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
const rootMenuEmitter = new Mitt();
|
const rootMenuEmitter = mitt();
|
||||||
const instance = getCurrentInstance();
|
const instance = getCurrentInstance();
|
||||||
|
|
||||||
const currentActiveName = ref<string | number>('');
|
const currentActiveName = ref<string | number>('');
|
||||||
|
@ -77,7 +77,7 @@
|
|||||||
import Icon from '/@/components/Icon';
|
import Icon from '/@/components/Icon';
|
||||||
import { Popover } from 'ant-design-vue';
|
import { Popover } from 'ant-design-vue';
|
||||||
import { isBoolean, isObject } from '/@/utils/is';
|
import { isBoolean, isObject } from '/@/utils/is';
|
||||||
import Mitt from '/@/utils/mitt';
|
import mitt from '/@/utils/mitt';
|
||||||
|
|
||||||
const DELAY = 200;
|
const DELAY = 200;
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@ -114,7 +114,7 @@
|
|||||||
|
|
||||||
const { prefixCls } = useDesign('menu');
|
const { prefixCls } = useDesign('menu');
|
||||||
|
|
||||||
const subMenuEmitter = new Mitt();
|
const subMenuEmitter = mitt();
|
||||||
|
|
||||||
const { rootMenuEmitter } = useSimpleRootMenuContext();
|
const { rootMenuEmitter } = useSimpleRootMenuContext();
|
||||||
|
|
||||||
@ -273,8 +273,7 @@
|
|||||||
state.opened = data;
|
state.opened = data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (isObject(data) && rootProps.accordion) {
|
||||||
if (isObject(data)) {
|
|
||||||
const { opend, parent, uidList } = data as Recordable;
|
const { opend, parent, uidList } = data as Recordable;
|
||||||
if (parent === instance?.parent) {
|
if (parent === instance?.parent) {
|
||||||
state.opened = opend;
|
state.opened = opend;
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import type { InjectionKey, Ref } from 'vue';
|
import type { InjectionKey, Ref } from 'vue';
|
||||||
import { createContext, useContext } from '/@/hooks/core/useContext';
|
import { createContext, useContext } from '/@/hooks/core/useContext';
|
||||||
import Mitt from '/@/utils/mitt';
|
import mitt from '/@/utils/mitt';
|
||||||
|
|
||||||
export interface SimpleRootMenuContextProps {
|
export interface SimpleRootMenuContextProps {
|
||||||
rootMenuEmitter: Mitt;
|
rootMenuEmitter: typeof mitt;
|
||||||
activeName: Ref<string | number>;
|
activeName: Ref<string | number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1 +1,4 @@
|
|||||||
export { default as StrengthMeter } from './src/StrengthMeter.vue';
|
import { withInstall } from '/@/utils';
|
||||||
|
import strengthMeter from './src/StrengthMeter.vue';
|
||||||
|
|
||||||
|
export const StrengthMeter = withInstall(strengthMeter);
|
||||||
|
@ -20,10 +20,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, computed, ref, watch, unref, watchEffect } from 'vue';
|
import { defineComponent, computed, ref, watch, unref, watchEffect } from 'vue';
|
||||||
|
|
||||||
import { Input } from 'ant-design-vue';
|
import { Input } from 'ant-design-vue';
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
import { zxcvbn } from '@zxcvbn-ts/core';
|
import { zxcvbn } from '@zxcvbn-ts/core';
|
||||||
import { useDesign } from '/@/hooks/web/useDesign';
|
import { useDesign } from '/@/hooks/web/useDesign';
|
||||||
import { propTypes } from '/@/utils/propTypes';
|
import { propTypes } from '/@/utils/propTypes';
|
||||||
|
@ -2,13 +2,9 @@ export { default as BasicTable } from './src/BasicTable.vue';
|
|||||||
export { default as TableAction } from './src/components/TableAction.vue';
|
export { default as TableAction } from './src/components/TableAction.vue';
|
||||||
export { default as EditTableHeaderIcon } from './src/components/EditTableHeaderIcon.vue';
|
export { default as EditTableHeaderIcon } from './src/components/EditTableHeaderIcon.vue';
|
||||||
export { default as TableImg } from './src/components/TableImg.vue';
|
export { default as TableImg } from './src/components/TableImg.vue';
|
||||||
|
|
||||||
export * from './src/types/table';
|
export * from './src/types/table';
|
||||||
export * from './src/types/pagination';
|
export * from './src/types/pagination';
|
||||||
export * from './src/types/tableAction';
|
export * from './src/types/tableAction';
|
||||||
|
|
||||||
export { useTable } from './src/hooks/useTable';
|
export { useTable } from './src/hooks/useTable';
|
||||||
|
|
||||||
export type { FormSchema, FormProps } from '/@/components/Form/src/types/form';
|
export type { FormSchema, FormProps } from '/@/components/Form/src/types/form';
|
||||||
|
|
||||||
export type { EditRecordRow } from './src/components/editable';
|
export type { EditRecordRow } from './src/components/editable';
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user