mirror of
https://github.com/vbenjs/vben-admin-thin-next.git
synced 2025-02-02 18:08: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',
|
||||
'prettier',
|
||||
'plugin:prettier/recommended',
|
||||
'plugin:jest/recommended',
|
||||
],
|
||||
rules: {
|
||||
'@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: |
|
||||
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_DROP_CONSOLE\s*=.*#VITE_DROP_CONSOLE = true#g" ./.env.production
|
||||
cat ./.env.production
|
||||
|
||||
- name: use Node.js 14
|
||||
- name: use Node.js 15
|
||||
uses: actions/setup-node@v2.1.2
|
||||
with:
|
||||
node-version: '14.x'
|
||||
node-version: '15.x'
|
||||
|
||||
- name: Get yarn cache
|
||||
id: yarn-cache
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,6 +10,7 @@ test/server/static
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
.eslintcache
|
||||
|
||||
# Log files
|
||||
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.enablePromptUseWorkspaceTsdk": true,
|
||||
"volar.tsPlugin": true,
|
||||
"volar.tsPluginStatus": false,
|
||||
//===========================================
|
||||
@ -10,23 +10,14 @@
|
||||
"editor.tabSize": 2,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"diffEditor.ignoreTrimWhitespace": false,
|
||||
"editor.trimAutoWhitespace": true,
|
||||
//===========================================
|
||||
//============= Other =======================
|
||||
//===========================================
|
||||
"breadcrumbs.enabled": true,
|
||||
"open-in-browser.default": "chrome",
|
||||
//===========================================
|
||||
//============= emmet =======================
|
||||
//===========================================
|
||||
"emmet.triggerExpansionOnTab": true,
|
||||
"emmet.showAbbreviationSuggestions": true,
|
||||
//===========================================
|
||||
//============= files =======================
|
||||
//===========================================
|
||||
"files.trimTrailingWhitespace": true,
|
||||
// "files.insertFinalNewline": true,
|
||||
// "files.trimFinalNewlines": true,
|
||||
"files.eol": "\n",
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
@ -49,9 +40,14 @@
|
||||
"CHANGELOG.md": true,
|
||||
"examples": true,
|
||||
"res": true,
|
||||
"screenshots": true
|
||||
"screenshots": true,
|
||||
"yarn-error.log": true,
|
||||
"**/.yarn": true
|
||||
},
|
||||
"files.exclude": {
|
||||
"**/.cache": true,
|
||||
"**/.editorconfig": true,
|
||||
"**/.eslintcache": true,
|
||||
"**/bower_components": true,
|
||||
"**/.idea": true,
|
||||
"**/tmp": true,
|
||||
@ -73,17 +69,6 @@
|
||||
},
|
||||
"stylelint.enable": true,
|
||||
"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,
|
||||
"telemetry.enableCrashReporter": false,
|
||||
"workbench.settings.enableNaturalLanguageSearch": false,
|
||||
@ -132,5 +117,18 @@
|
||||
"i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}",
|
||||
"i18n-ally.enabledParsers": ["ts"],
|
||||
"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)
|
||||
|
||||
### 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)
|
||||
|
||||
### ✨ Refactor
|
||||
|
@ -5,7 +5,6 @@ import vueJsx from '@vitejs/plugin-vue-jsx';
|
||||
import legacy from '@vitejs/plugin-legacy';
|
||||
|
||||
import purgeIcons from 'vite-plugin-purge-icons';
|
||||
import windiCSS from 'vite-plugin-windicss';
|
||||
|
||||
import { configHtmlPlugin } from './html';
|
||||
import { configPwaConfig } from './pwa';
|
||||
@ -46,9 +45,6 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
|
||||
// vite-plugin-svg-icons
|
||||
vitePlugins.push(configSvgIconsPlugin(isBuild));
|
||||
|
||||
// vite-plugin-windicss
|
||||
vitePlugins.push(windiCSS());
|
||||
|
||||
// vite-plugin-mock
|
||||
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 { resultSuccess } from '../_util';
|
||||
|
||||
const list: any[] = [];
|
||||
const demoList = (() => {
|
||||
const result: any[] = [];
|
||||
const result = {
|
||||
list: list,
|
||||
};
|
||||
for (let index = 0; index < 20; index++) {
|
||||
result.push({
|
||||
label: `选项${index}`,
|
||||
value: `${index}`,
|
||||
result.list.push({
|
||||
name: `选项${index}`,
|
||||
id: `${index}`,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
@ -15,8 +18,8 @@ const demoList = (() => {
|
||||
export default [
|
||||
{
|
||||
url: '/basic-api/select/getDemoOptions',
|
||||
timeout: 2000,
|
||||
method: 'get',
|
||||
timeout: 1000,
|
||||
method: 'post',
|
||||
response: ({ query }) => {
|
||||
console.log(query);
|
||||
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 [
|
||||
{
|
||||
url: '/basic-api/getMenuList',
|
||||
@ -184,10 +212,10 @@ export default [
|
||||
}
|
||||
const id = checkUser.userId;
|
||||
if (!id || id === '1') {
|
||||
return resultSuccess([dashboardRoute, authRoute, levelRoute, sysRoute]);
|
||||
return resultSuccess([dashboardRoute, authRoute, levelRoute, sysRoute, linkRoute]);
|
||||
}
|
||||
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",
|
||||
"version": "2.4.2",
|
||||
"version": "2.5.0",
|
||||
"author": {
|
||||
"name": "vben",
|
||||
"email": "anncwb@126.com",
|
||||
@ -19,11 +19,13 @@
|
||||
"log": "conventional-changelog -p angular -i CHANGELOG.md -s",
|
||||
"clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite",
|
||||
"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: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:pretty": "pretty-quick --staged",
|
||||
"test:unit": "jest",
|
||||
"test:unit-coverage": "jest --coverage",
|
||||
"test:gzip": "http-server dist --cors --gzip -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",
|
||||
@ -32,10 +34,10 @@
|
||||
"postinstall": "npm run install:husky"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify/iconify": "^2.0.1",
|
||||
"@vueuse/core": "^5.0.2",
|
||||
"@iconify/iconify": "^2.0.2",
|
||||
"@vueuse/core": "^5.0.3",
|
||||
"@zxcvbn-ts/core": "^0.3.0",
|
||||
"ant-design-vue": "2.1.2",
|
||||
"ant-design-vue": "2.1.6",
|
||||
"axios": "^0.21.1",
|
||||
"crypto-js": "^4.0.0",
|
||||
"echarts": "^5.1.2",
|
||||
@ -43,83 +45,91 @@
|
||||
"mockjs": "^1.1.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"path-to-regexp": "^6.2.0",
|
||||
"pinia": "2.0.0-alpha.13",
|
||||
"pinia": "2.0.0-beta.3",
|
||||
"qrcode": "^1.4.4",
|
||||
"sortablejs": "^1.13.0",
|
||||
"vue": "3.0.11",
|
||||
"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"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^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",
|
||||
"@types/codemirror": "^5.60.0",
|
||||
"@types/crypto-js": "^4.0.1",
|
||||
"@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/mockjs": "^1.0.3",
|
||||
"@types/node": "^15.12.2",
|
||||
"@types/node": "^15.12.4",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/qrcode": "^1.4.0",
|
||||
"@types/qs": "^6.9.6",
|
||||
"@types/sortablejs": "^1.10.6",
|
||||
"@typescript-eslint/eslint-plugin": "^4.26.1",
|
||||
"@typescript-eslint/parser": "^4.26.1",
|
||||
"@typescript-eslint/eslint-plugin": "^4.27.0",
|
||||
"@typescript-eslint/parser": "^4.27.0",
|
||||
"@vitejs/plugin-legacy": "^1.4.1",
|
||||
"@vitejs/plugin-vue": "^1.2.3",
|
||||
"@vitejs/plugin-vue-jsx": "^1.1.5",
|
||||
"@vue/compiler-sfc": "3.0.11",
|
||||
"@vue/test-utils": "^2.0.0-rc.7",
|
||||
"autoprefixer": "^10.2.6",
|
||||
"commitizen": "^4.2.4",
|
||||
"conventional-changelog-cli": "^2.1.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"dotenv": "^10.0.0",
|
||||
"eslint": "^7.28.0",
|
||||
"eslint": "^7.29.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-define-config": "^1.0.8",
|
||||
"eslint-plugin-jest": "^24.3.6",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-vue": "^7.10.0",
|
||||
"esno": "^0.7.1",
|
||||
"eslint-plugin-vue": "^7.11.1",
|
||||
"esno": "^0.7.3",
|
||||
"fs-extra": "^10.0.0",
|
||||
"http-server": "^0.12.3",
|
||||
"husky": "^6.0.0",
|
||||
"inquirer": "^8.1.0",
|
||||
"inquirer": "^8.1.1",
|
||||
"is-ci": "^3.0.0",
|
||||
"jest": "^27.0.4",
|
||||
"less": "^4.1.1",
|
||||
"lint-staged": "^11.0.0",
|
||||
"postcss": "^8.3.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.3.5",
|
||||
"prettier": "^2.3.1",
|
||||
"pretty-quick": "^3.1.0",
|
||||
"pretty-quick": "^3.1.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup-plugin-visualizer": "5.5.0",
|
||||
"stylelint": "^13.13.1",
|
||||
"stylelint-config-prettier": "^8.0.2",
|
||||
"stylelint-config-standard": "^22.0.0",
|
||||
"stylelint-order": "^4.1.0",
|
||||
"tailwindcss": "^2.2.2",
|
||||
"ts-jest": "^27.0.3",
|
||||
"ts-node": "^10.0.0",
|
||||
"typescript": "4.3.2",
|
||||
"vite": "2.3.3",
|
||||
"typescript": "4.3.4",
|
||||
"vite": "2.3.8",
|
||||
"vite-plugin-compression": "^0.2.5",
|
||||
"vite-plugin-html": "^2.0.7",
|
||||
"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-pwa": "^0.7.3",
|
||||
"vite-plugin-style-import": "^0.10.1",
|
||||
"vite-plugin-svg-icons": "^0.7.0",
|
||||
"vite-plugin-pwa": "^0.8.1",
|
||||
"vite-plugin-style-import": "^1.0.0",
|
||||
"vite-plugin-svg-icons": "^0.7.1",
|
||||
"vite-plugin-theme": "^0.8.1",
|
||||
"vite-plugin-windicss": "^1.0.3",
|
||||
"vue-eslint-parser": "^7.6.0",
|
||||
"vue-tsc": "^0.1.7"
|
||||
},
|
||||
"resolutions": {
|
||||
"//": "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",
|
||||
"rollup": "^2.51.1"
|
||||
"rollup": "^2.52.1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -1,5 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { RouteMeta } from '/@/router/types';
|
||||
import type { RouteMeta } from 'vue-router';
|
||||
export interface RouteItem {
|
||||
path: string;
|
||||
component: any;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { UploadApiResult } from './model/uploadModel';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { UploadFileParams } from '/@/utils/http/axios/types';
|
||||
import { UploadFileParams } from '/#/axios';
|
||||
import { useGlobSetting } from '/@/hooks/setting';
|
||||
|
||||
const { uploadUrl = '' } = useGlobSetting();
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { LoginParams, LoginResultModel, GetUserInfoModel } from './model/userModel';
|
||||
|
||||
import { ErrorMessageMode } from '/@/utils/http/axios/types';
|
||||
import { ErrorMessageMode } from '/#/axios';
|
||||
|
||||
enum Api {
|
||||
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="`${prefixCls}-content`" v-click-outside="handleClose">
|
||||
<div :class="`${prefixCls}-input__wrapper`">
|
||||
<Input
|
||||
<a-input
|
||||
:class="`${prefixCls}-input`"
|
||||
:placeholder="t('common.searchText')"
|
||||
ref="inputRef"
|
||||
@ -14,7 +14,7 @@
|
||||
<template #prefix>
|
||||
<SearchOutlined />
|
||||
</template>
|
||||
</Input>
|
||||
</a-input>
|
||||
<span :class="`${prefixCls}-cancel`" @click="handleClose">
|
||||
{{ t('common.cancelText') }}
|
||||
</span>
|
||||
@ -59,7 +59,6 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, unref, ref, watch, nextTick } from 'vue';
|
||||
import { SearchOutlined } from '@ant-design/icons-vue';
|
||||
import { Input } from 'ant-design-vue';
|
||||
import AppSearchFooter from './AppSearchFooter.vue';
|
||||
import Icon from '/@/components/Icon';
|
||||
import clickOutside from '/@/directives/clickOutside';
|
||||
@ -75,7 +74,7 @@
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AppSearchModal',
|
||||
components: { Icon, SearchOutlined, AppSearchFooter, Input },
|
||||
components: { Icon, SearchOutlined, AppSearchFooter },
|
||||
directives: {
|
||||
clickOutside,
|
||||
},
|
||||
|
@ -1,13 +1,12 @@
|
||||
<template>
|
||||
<AInput v-bind="$attrs" :class="prefixCls" :size="size" :value="state">
|
||||
<a-input v-bind="$attrs" :class="prefixCls" :size="size" :value="state">
|
||||
<template #addonAfter>
|
||||
<CountButton :size="size" :count="count" :value="state" :beforeStartFunc="sendCodeApi" />
|
||||
</template>
|
||||
</AInput>
|
||||
</a-input>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import { Input } from 'ant-design-vue';
|
||||
import CountButton from './CountButton.vue';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
|
||||
@ -24,7 +23,7 @@
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CountDownInput',
|
||||
components: { [Input.name]: Input, CountButton },
|
||||
components: { CountButton },
|
||||
inheritAttrs: false,
|
||||
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 { default as CropperImage } from './src/Cropper.vue';
|
||||
export * from './src/typing';
|
||||
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>
|
||||
<div :class="$attrs.class" :style="getWrapperStyle">
|
||||
<div :class="getClass" :style="getWrapperStyle">
|
||||
<img
|
||||
v-show="isReady"
|
||||
ref="imgElRef"
|
||||
@ -12,16 +12,16 @@
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { CSSProperties } from 'vue';
|
||||
|
||||
import { defineComponent, onMounted, ref, unref, computed } from 'vue';
|
||||
|
||||
import Cropper from 'cropperjs';
|
||||
import 'cropperjs/dist/cropper.css';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { useDebounceFn } from '@vueuse/shared';
|
||||
|
||||
type Options = Cropper.Options;
|
||||
|
||||
const defaultOptions: Options = {
|
||||
aspectRatio: 16 / 9,
|
||||
aspectRatio: 1,
|
||||
zoomable: true,
|
||||
zoomOnTouch: true,
|
||||
zoomOnWheel: true,
|
||||
@ -43,40 +43,32 @@
|
||||
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({
|
||||
name: 'CropperImage',
|
||||
props: {
|
||||
src: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
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);
|
||||
|
||||
props,
|
||||
emits: ['cropend', 'ready', 'cropendError'],
|
||||
setup(props, { attrs, emit }) {
|
||||
const imgElRef = ref<ElRef<HTMLImageElement>>();
|
||||
const cropper = ref<Nullable<Cropper>>();
|
||||
const isReady = ref(false);
|
||||
|
||||
const { prefixCls } = useDesign('cropper-image');
|
||||
const debounceRealTimeCroppered = useDebounceFn(realTimeCroppered, 80);
|
||||
|
||||
const getImageStyle = computed((): CSSProperties => {
|
||||
return {
|
||||
height: props.height,
|
||||
@ -85,11 +77,22 @@
|
||||
};
|
||||
});
|
||||
|
||||
const getWrapperStyle = computed((): CSSProperties => {
|
||||
const { height } = props;
|
||||
return { height: `${height}`.replace(/px/, '') + 'px' };
|
||||
const getClass = computed(() => {
|
||||
return [
|
||||
prefixCls,
|
||||
attrs.class,
|
||||
{
|
||||
[`${prefixCls}--circled`]: props.circled,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const getWrapperStyle = computed((): CSSProperties => {
|
||||
return { height: `${props.height}`.replace(/px/, '') + 'px' };
|
||||
});
|
||||
|
||||
onMounted(init);
|
||||
|
||||
async function init() {
|
||||
const imgEl = unref(imgElRef);
|
||||
if (!imgEl) {
|
||||
@ -99,29 +102,83 @@
|
||||
...defaultOptions,
|
||||
ready: () => {
|
||||
isReady.value = true;
|
||||
realTimeCroppered();
|
||||
emit('ready', cropper.value);
|
||||
},
|
||||
crop() {
|
||||
debounceRealTimeCroppered();
|
||||
},
|
||||
zoom() {
|
||||
debounceRealTimeCroppered();
|
||||
},
|
||||
cropmove() {
|
||||
debounceRealTimeCroppered();
|
||||
},
|
||||
...props.options,
|
||||
});
|
||||
}
|
||||
|
||||
// Real-time display preview
|
||||
function realTimeCroppered() {
|
||||
props.realTimePreview && croppered();
|
||||
}
|
||||
|
||||
// event: return base64 and width and height information after cropping
|
||||
const croppered = (): void => {
|
||||
function croppered() {
|
||||
if (!cropper.value) {
|
||||
return;
|
||||
}
|
||||
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();
|
||||
fileReader.readAsDataURL(blob);
|
||||
fileReader.onloadend = (e) => {
|
||||
ctx.emit('cropperedInfo', {
|
||||
emit('cropend', {
|
||||
imgBase64: e.target?.result ?? '',
|
||||
imgInfo,
|
||||
});
|
||||
};
|
||||
fileReader.readAsDataURL(blob);
|
||||
}, 'image/jpeg');
|
||||
};
|
||||
fileReader.onerror = () => {
|
||||
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>
|
||||
<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/types';
|
||||
export * from './src/typing';
|
||||
export { useDescription } from './src/useDescription';
|
||||
export const Description = withInstall(description);
|
||||
|
@ -1,28 +1,49 @@
|
||||
<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 { CSSProperties } from 'vue';
|
||||
import type { CollapseContainerOptions } from '/@/components/Container/index';
|
||||
|
||||
import { defineComponent, computed, ref, unref } from 'vue';
|
||||
import { get } from 'lodash-es';
|
||||
import { Descriptions } from 'ant-design-vue';
|
||||
import { CollapseContainer } from '/@/components/Container/index';
|
||||
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
import { isFunction } from '/@/utils/is';
|
||||
import { getSlot } from '/@/utils/helper/tsxHelper';
|
||||
|
||||
import descProps from './props';
|
||||
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({
|
||||
name: 'Description',
|
||||
props: descProps,
|
||||
props,
|
||||
emits: ['register'],
|
||||
setup(props, { slots, emit }) {
|
||||
const propsRef = ref<Partial<DescOptions> | null>(null);
|
||||
const propsRef = ref<Partial<DescriptionProps> | null>(null);
|
||||
|
||||
const { prefixCls } = useDesign('description');
|
||||
const attrs = useAttrs();
|
||||
@ -32,7 +53,7 @@
|
||||
return {
|
||||
...props,
|
||||
...(unref(propsRef) as Recordable),
|
||||
} as DescOptions;
|
||||
} as DescriptionProps;
|
||||
});
|
||||
|
||||
const getProps = computed(() => {
|
||||
@ -40,7 +61,7 @@
|
||||
...unref(getMergeProps),
|
||||
title: undefined,
|
||||
};
|
||||
return opt as DescOptions;
|
||||
return opt as DescriptionProps;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -66,7 +87,7 @@
|
||||
/**
|
||||
* @description:设置desc
|
||||
*/
|
||||
function setDescProps(descProps: Partial<DescOptions>): void {
|
||||
function setDescProps(descProps: Partial<DescriptionProps>): void {
|
||||
// Keep the last setDrawerProps
|
||||
propsRef.value = { ...(unref(propsRef) as Recordable), ...descProps } as Recordable;
|
||||
}
|
||||
@ -79,7 +100,6 @@
|
||||
|
||||
const labelStyles: CSSProperties = {
|
||||
...labelStyle,
|
||||
|
||||
minWidth: `${labelMinWidth}px `,
|
||||
};
|
||||
return <div style={labelStyles}>{label}</div>;
|
||||
@ -97,7 +117,9 @@
|
||||
|
||||
const getContent = () => {
|
||||
const _data = unref(getProps)?.data;
|
||||
if (!_data) return null;
|
||||
if (!_data) {
|
||||
return null;
|
||||
}
|
||||
const getField = get(_data, field);
|
||||
return isFunction(render) ? render(getField, _data) : getField ?? '';
|
||||
};
|
||||
@ -131,7 +153,6 @@
|
||||
const renderContainer = () => {
|
||||
const content = props.useCollapse ? renderDesc() : <div>{renderDesc()}</div>;
|
||||
// Reduce the dom level
|
||||
|
||||
if (!props.useCollapse) {
|
||||
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 {
|
||||
labelMinWidth?: number;
|
||||
|
||||
contentMinWidth?: number;
|
||||
|
||||
labelStyle?: CSSProperties;
|
||||
|
||||
field: string;
|
||||
label: string | VNode | JSX.Element;
|
||||
// Merge column
|
||||
@ -21,7 +18,7 @@ export interface DescItem {
|
||||
) => VNode | undefined | JSX.Element | Element | string | number;
|
||||
}
|
||||
|
||||
export interface DescOptions extends DescriptionsProps {
|
||||
export interface DescriptionProps extends DescriptionsProps {
|
||||
// Whether to include the collapse component
|
||||
useCollapse?: boolean;
|
||||
/**
|
||||
@ -42,7 +39,7 @@ export interface DescOptions extends DescriptionsProps {
|
||||
}
|
||||
|
||||
export interface DescInstance {
|
||||
setDescProps(descProps: Partial<DescOptions>): void;
|
||||
setDescProps(descProps: Partial<DescriptionProps>): 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 { isProdMode } from '/@/utils/env';
|
||||
|
||||
import type { DescOptions, DescInstance, UseDescReturnType } from './types';
|
||||
|
||||
export function useDescription(props?: Partial<DescOptions>): UseDescReturnType {
|
||||
export function useDescription(props?: Partial<DescriptionProps>): UseDescReturnType {
|
||||
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 loadedRef = ref(false);
|
||||
const desc = ref<Nullable<DescInstance>>(null);
|
||||
const loaded = ref(false);
|
||||
|
||||
function register(instance: DescInstance) {
|
||||
if (unref(loadedRef) && isProdMode()) return;
|
||||
descRef.value = instance;
|
||||
if (unref(loaded) && isProdMode()) {
|
||||
return;
|
||||
}
|
||||
desc.value = instance;
|
||||
props && instance.setDescProps(props);
|
||||
loadedRef.value = true;
|
||||
loaded.value = true;
|
||||
}
|
||||
|
||||
const methods: DescInstance = {
|
||||
setDescProps: (descProps: Partial<DescOptions>): void => {
|
||||
unref(descRef)?.setDescProps(descProps);
|
||||
setDescProps: (descProps: Partial<DescriptionProps>): void => {
|
||||
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 * from './src/types';
|
||||
export const BasicDrawer = withInstall(basicDrawer);
|
||||
export * from './src/typing';
|
||||
export { useDrawer, useDrawerInner } from './src/useDrawer';
|
||||
|
@ -31,9 +31,8 @@
|
||||
</Drawer>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { DrawerInstance, DrawerProps } from './types';
|
||||
import type { DrawerInstance, DrawerProps } from './typing';
|
||||
import type { CSSProperties } from 'vue';
|
||||
|
||||
import {
|
||||
defineComponent,
|
||||
ref,
|
||||
@ -46,15 +45,12 @@
|
||||
getCurrentInstance,
|
||||
} from 'vue';
|
||||
import { Drawer } from 'ant-design-vue';
|
||||
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
|
||||
import { isFunction, isNumber } from '/@/utils/is';
|
||||
import { deepMerge } from '/@/utils';
|
||||
import DrawerFooter from './components/DrawerFooter.vue';
|
||||
import DrawerHeader from './components/DrawerHeader.vue';
|
||||
import { ScrollContainer } from '/@/components/Container';
|
||||
|
||||
import { basicProps } from './props';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { useAttrs } from '/@/hooks/core/useAttrs';
|
||||
@ -167,7 +163,7 @@
|
||||
|
||||
function setDrawerProps(props: Partial<DrawerProps>): void {
|
||||
// Keep the last setDrawerProps
|
||||
propsRef.value = deepMerge(unref(propsRef) || {}, props);
|
||||
propsRef.value = deepMerge(unref(propsRef) || ({} as any), props);
|
||||
|
||||
if (Reflect.has(props, 'visible')) {
|
||||
visibleRef.value = !!props.visible;
|
||||
|
@ -40,6 +40,7 @@
|
||||
function handleClose() {
|
||||
emit('close');
|
||||
}
|
||||
|
||||
return { prefixCls, handleClose };
|
||||
},
|
||||
});
|
||||
|
@ -1,50 +1,44 @@
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
const { t } = useI18n();
|
||||
|
||||
export const footerProps = {
|
||||
confirmLoading: propTypes.bool,
|
||||
confirmLoading: { type: Boolean },
|
||||
/**
|
||||
* @description: Show close button
|
||||
*/
|
||||
showCancelBtn: propTypes.bool.def(true),
|
||||
showCancelBtn: { type: Boolean, default: true },
|
||||
cancelButtonProps: Object as PropType<Recordable>,
|
||||
cancelText: propTypes.string.def(t('common.cancelText')),
|
||||
cancelText: { type: String, default: t('common.cancelText') },
|
||||
/**
|
||||
* @description: Show confirmation button
|
||||
*/
|
||||
showOkBtn: propTypes.bool.def(true),
|
||||
showOkBtn: { type: Boolean, default: true },
|
||||
okButtonProps: Object as PropType<Recordable>,
|
||||
okText: propTypes.string.def(t('common.okText')),
|
||||
okType: propTypes.string.def('primary'),
|
||||
showFooter: propTypes.bool,
|
||||
okText: { type: String, default: t('common.okText') },
|
||||
okType: { type: String, default: 'primary' },
|
||||
showFooter: { type: Boolean },
|
||||
footerHeight: {
|
||||
type: [String, Number] as PropType<string | number>,
|
||||
default: 60,
|
||||
},
|
||||
};
|
||||
export const basicProps = {
|
||||
isDetail: propTypes.bool,
|
||||
title: propTypes.string.def(''),
|
||||
loadingText: propTypes.string,
|
||||
showDetailBack: propTypes.bool.def(true),
|
||||
visible: propTypes.bool,
|
||||
loading: propTypes.bool,
|
||||
maskClosable: propTypes.bool.def(true),
|
||||
isDetail: { type: Boolean },
|
||||
title: { type: String, default: '' },
|
||||
loadingText: { type: String },
|
||||
showDetailBack: { type: Boolean, default: true },
|
||||
visible: { type: Boolean },
|
||||
loading: { type: Boolean },
|
||||
maskClosable: { type: Boolean, default: true },
|
||||
getContainer: {
|
||||
type: [Object, String] as PropType<any>,
|
||||
},
|
||||
scrollOptions: {
|
||||
type: Object as PropType<any>,
|
||||
default: null,
|
||||
},
|
||||
closeFunc: {
|
||||
type: [Function, Object] as PropType<any>,
|
||||
default: null,
|
||||
},
|
||||
triggerWindowResize: propTypes.bool,
|
||||
destroyOnClose: propTypes.bool,
|
||||
destroyOnClose: { type: Boolean },
|
||||
...footerProps,
|
||||
};
|
||||
|
@ -9,6 +9,7 @@ export interface DrawerInstance {
|
||||
|
||||
export interface ReturnMethods extends DrawerInstance {
|
||||
openDrawer: <T = any>(visible?: boolean, data?: T, openOnSet?: boolean) => void;
|
||||
closeDrawer: () => void;
|
||||
getVisible?: ComputedRef<boolean>;
|
||||
}
|
||||
|
||||
@ -181,7 +182,6 @@ export interface DrawerProps extends DrawerFooterProps {
|
||||
placement?: 'top' | 'right' | 'bottom' | 'left';
|
||||
afterVisibleChange?: (visible?: boolean) => void;
|
||||
keyboard?: boolean;
|
||||
|
||||
/**
|
||||
* Specify a callback that will be called when a user clicks mask, close button or Cancel button.
|
||||
*/
|
@ -4,8 +4,7 @@ import type {
|
||||
ReturnMethods,
|
||||
DrawerProps,
|
||||
UseDrawerInnerReturnType,
|
||||
} from './types';
|
||||
|
||||
} from './typing';
|
||||
import {
|
||||
ref,
|
||||
getCurrentInstance,
|
||||
@ -16,11 +15,9 @@ import {
|
||||
toRaw,
|
||||
computed,
|
||||
} from 'vue';
|
||||
|
||||
import { isProdMode } from '/@/utils/env';
|
||||
import { isFunction } from '/@/utils/is';
|
||||
import { tryOnUnmounted } from '@vueuse/core';
|
||||
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { error } from '/@/utils/log';
|
||||
|
||||
@ -32,24 +29,27 @@ const visibleData = reactive<{ [key: number]: boolean }>({});
|
||||
* @description: Applicable to separate drawer and call outside
|
||||
*/
|
||||
export function useDrawer(): UseDrawerReturnType {
|
||||
const drawerRef = ref<DrawerInstance | null>(null);
|
||||
const loadedRef = ref<Nullable<boolean>>(false);
|
||||
const uidRef = ref<string>('');
|
||||
if (!getCurrentInstance()) {
|
||||
throw new Error('useDrawer() can only be used inside setup() or functional components!');
|
||||
}
|
||||
const drawer = ref<DrawerInstance | null>(null);
|
||||
const loaded = ref<Nullable<boolean>>(false);
|
||||
const uid = ref<string>('');
|
||||
|
||||
function register(drawerInstance: DrawerInstance, uuid: string) {
|
||||
isProdMode() &&
|
||||
tryOnUnmounted(() => {
|
||||
drawerRef.value = null;
|
||||
loadedRef.value = null;
|
||||
dataTransferRef[unref(uidRef)] = null;
|
||||
drawer.value = null;
|
||||
loaded.value = null;
|
||||
dataTransferRef[unref(uid)] = null;
|
||||
});
|
||||
|
||||
if (unref(loadedRef) && isProdMode() && drawerInstance === unref(drawerRef)) {
|
||||
if (unref(loaded) && isProdMode() && drawerInstance === unref(drawer)) {
|
||||
return;
|
||||
}
|
||||
uidRef.value = uuid;
|
||||
drawerRef.value = drawerInstance;
|
||||
loadedRef.value = true;
|
||||
uid.value = uuid;
|
||||
drawer.value = drawerInstance;
|
||||
loaded.value = true;
|
||||
|
||||
drawerInstance.emitVisible = (visible: boolean, uid: number) => {
|
||||
visibleData[uid] = visible;
|
||||
@ -57,7 +57,7 @@ export function useDrawer(): UseDrawerReturnType {
|
||||
}
|
||||
|
||||
const getInstance = () => {
|
||||
const instance = unref(drawerRef);
|
||||
const instance = unref(drawer);
|
||||
if (!instance) {
|
||||
error('useDrawer instance is undefined!');
|
||||
}
|
||||
@ -70,7 +70,7 @@ export function useDrawer(): UseDrawerReturnType {
|
||||
},
|
||||
|
||||
getVisible: computed((): boolean => {
|
||||
return visibleData[~~unref(uidRef)];
|
||||
return visibleData[~~unref(uid)];
|
||||
}),
|
||||
|
||||
openDrawer: <T = any>(visible = true, data?: T, openOnSet = true): void => {
|
||||
@ -80,15 +80,18 @@ export function useDrawer(): UseDrawerReturnType {
|
||||
if (!data) return;
|
||||
|
||||
if (openOnSet) {
|
||||
dataTransferRef[unref(uidRef)] = null;
|
||||
dataTransferRef[unref(uidRef)] = toRaw(data);
|
||||
dataTransferRef[unref(uid)] = null;
|
||||
dataTransferRef[unref(uid)] = toRaw(data);
|
||||
return;
|
||||
}
|
||||
const equal = isEqual(toRaw(dataTransferRef[unref(uidRef)]), toRaw(data));
|
||||
const equal = isEqual(toRaw(dataTransferRef[unref(uid)]), toRaw(data));
|
||||
if (!equal) {
|
||||
dataTransferRef[unref(uidRef)] = toRaw(data);
|
||||
dataTransferRef[unref(uid)] = toRaw(data);
|
||||
}
|
||||
},
|
||||
closeDrawer: () => {
|
||||
getInstance()?.setDrawerProps({ visible: false });
|
||||
},
|
||||
};
|
||||
|
||||
return [register, methods];
|
||||
@ -99,8 +102,8 @@ export const useDrawerInner = (callbackFn?: Fn): UseDrawerInnerReturnType => {
|
||||
const currentInstance = getCurrentInstance();
|
||||
const uidRef = ref<string>('');
|
||||
|
||||
if (!currentInstance) {
|
||||
error('useDrawerInner instance is undefined!');
|
||||
if (!getCurrentInstance()) {
|
||||
throw new Error('useDrawerInner() can only be used inside setup() or functional components!');
|
||||
}
|
||||
|
||||
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 { Dropdown };
|
||||
export * from './src/typing';
|
||||
export const Dropdown = withInstall(dropdown);
|
||||
|
@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<a-dropdown :trigger="trigger" v-bind="$attrs">
|
||||
<Dropdown :trigger="trigger" v-bind="$attrs">
|
||||
<span>
|
||||
<slot></slot>
|
||||
</span>
|
||||
<template #overlay>
|
||||
<a-menu :selectedKeys="selectedKeys">
|
||||
<Menu :selectedKeys="selectedKeys">
|
||||
<template v-for="item in dropMenuList" :key="`${item.event}`">
|
||||
<a-menu-item
|
||||
<MenuItem
|
||||
v-bind="getAttr(item.event)"
|
||||
@click="handleClickMenu(item)"
|
||||
:disabled="item.disabled"
|
||||
@ -19,17 +19,17 @@
|
||||
<Icon :icon="item.icon" v-if="item.icon" />
|
||||
<span class="ml-1">{{ item.text }}</span>
|
||||
</template>
|
||||
</a-menu-item>
|
||||
<a-menu-divider v-if="item.divider" :key="`d-${item.event}`" />
|
||||
</MenuItem>
|
||||
<MenuDivider v-if="item.divider" :key="`d-${item.event}`" />
|
||||
</template>
|
||||
</a-menu>
|
||||
</Menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</Dropdown>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import type { DropMenu } from './types';
|
||||
import type { DropMenu } from './typing';
|
||||
|
||||
import { defineComponent } from 'vue';
|
||||
import { Dropdown, Menu, Popconfirm } from 'ant-design-vue';
|
||||
@ -38,10 +38,10 @@
|
||||
export default defineComponent({
|
||||
name: 'BasicDropdown',
|
||||
components: {
|
||||
[Dropdown.name]: Dropdown,
|
||||
[Menu.name]: Menu,
|
||||
[Menu.Item.name]: Menu.Item,
|
||||
[Menu.Divider.name]: Menu.Divider,
|
||||
Dropdown,
|
||||
Menu,
|
||||
MenuItem: Menu.Item,
|
||||
MenuDivider: Menu.Divider,
|
||||
Icon,
|
||||
Popconfirm,
|
||||
},
|
||||
@ -53,7 +53,7 @@
|
||||
* @type string[]
|
||||
*/
|
||||
trigger: {
|
||||
type: [Array] as PropType<string[]>,
|
||||
type: [Array] as PropType<('contextmenu' | 'click' | 'hover')[]>,
|
||||
default: () => {
|
||||
return ['contextmenu'];
|
||||
},
|
||||
@ -75,6 +75,7 @@
|
||||
emit('menuEvent', menu);
|
||||
item.onClick?.();
|
||||
}
|
||||
|
||||
return {
|
||||
handleClickMenu,
|
||||
getAttr: (key: string | number) => ({ key }),
|
||||
|
@ -7,5 +7,3 @@ export interface DropMenu {
|
||||
disabled?: boolean;
|
||||
divider?: boolean;
|
||||
}
|
||||
|
||||
// export type Trigger = 'click' | 'hover' | 'contextMenu';
|
@ -1,8 +1,8 @@
|
||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||
|
||||
export const ImpExcel = createAsyncComponent(() => import('./src/ImportExcel.vue'));
|
||||
export const ExpExcelModel = createAsyncComponent(() => import('./src/ExportExcelModel.vue'));
|
||||
|
||||
export * from './src/types';
|
||||
import { withInstall } from '/@/utils';
|
||||
import impExcel from './src/ImportExcel.vue';
|
||||
import expExcelModal from './src/ExportExcelModal.vue';
|
||||
|
||||
export const ImpExcel = withInstall(impExcel);
|
||||
export const ExpExcelModal = withInstall(expExcelModal);
|
||||
export * from './src/typing';
|
||||
export { jsonToSheetXlsx, aoaToSheetXlsx } from './src/Export2Excel';
|
||||
|
@ -1,10 +1,11 @@
|
||||
import xlsx from 'xlsx';
|
||||
import type { WorkBook } from 'xlsx';
|
||||
import type { JsonToSheet, AoAToSheet } from './types';
|
||||
import type { JsonToSheet, AoAToSheet } from './typing';
|
||||
|
||||
const { utils, writeFile } = xlsx;
|
||||
|
||||
const DEF_FILE_NAME = 'excel-list.xlsx';
|
||||
|
||||
export function jsonToSheetXlsx<T = any>({
|
||||
data,
|
||||
header,
|
||||
|
@ -14,7 +14,7 @@
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { ExportModalResult } from './types';
|
||||
import type { ExportModalResult } from './typing';
|
||||
import { defineComponent } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
|
@ -16,7 +16,7 @@
|
||||
import { defineComponent, ref, unref } from 'vue';
|
||||
import XLSX from 'xlsx';
|
||||
|
||||
import type { ExcelData } from './types';
|
||||
import type { ExcelData } from './typing';
|
||||
export default defineComponent({
|
||||
name: 'ImportExcel',
|
||||
emits: ['success', 'error'],
|
||||
|
@ -6,10 +6,6 @@ export interface ExcelData<T = any> {
|
||||
meta: { sheetName: string };
|
||||
}
|
||||
|
||||
// export interface ImportProps {
|
||||
// beforeUpload: (file: File) => boolean;
|
||||
// }
|
||||
|
||||
export interface JsonToSheet<T = any> {
|
||||
data: T[];
|
||||
header?: T;
|
@ -9,23 +9,20 @@
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { Definition } from '@logicflow/core';
|
||||
|
||||
import { defineComponent, ref, onMounted, unref, nextTick, computed, watch } from 'vue';
|
||||
|
||||
import FlowChartToolbar from './FlowChartToolbar.vue';
|
||||
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 { useAppStore } from '/@/store/modules/app';
|
||||
import { createFlowChartContext } from './useFlowContext';
|
||||
|
||||
import { toLogicFlowData } from './adpterForTurbo';
|
||||
import { useModal, BasicModal } from '/@/components/Modal';
|
||||
import { JsonPreview } from '/@/components/CodeEditor';
|
||||
|
||||
import { configDefaultDndPanel } from './config';
|
||||
import '@logicflow/core/dist/style/index.css';
|
||||
import '@logicflow/extension/lib/style/index.css';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FlowChart',
|
||||
components: { BasicModal, FlowChartToolbar, JsonPreview },
|
||||
@ -44,6 +41,9 @@
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
patternItems: {
|
||||
type: Array,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const lfElRef = ref<ElRef>(null);
|
||||
@ -104,6 +104,7 @@
|
||||
if (!lfEl) {
|
||||
return;
|
||||
}
|
||||
LogicFlow.use(DndPanel);
|
||||
|
||||
// Canvas configuration
|
||||
LogicFlow.use(Snapshot);
|
||||
@ -111,14 +112,16 @@
|
||||
LogicFlow.use(BpmnElement);
|
||||
// Start the right-click menu
|
||||
LogicFlow.use(Menu);
|
||||
LogicFlow.use(DndPanel);
|
||||
LogicFlow.use(SelectionSelect);
|
||||
|
||||
lfInstance.value = new LogicFlow({
|
||||
...unref(getFlowOptions),
|
||||
container: lfEl,
|
||||
});
|
||||
unref(lfInstance)?.setDefaultEdgeType('line');
|
||||
const lf = unref(lfInstance)!;
|
||||
lf?.setDefaultEdgeType('line');
|
||||
onRender();
|
||||
lf?.setPatternItems(props.patternItems || configDefaultDndPanel(lf));
|
||||
}
|
||||
|
||||
async function onRender() {
|
||||
@ -137,7 +140,6 @@
|
||||
return;
|
||||
}
|
||||
graphData.value = unref(lf).getGraphData();
|
||||
|
||||
openModal();
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<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 } : {}">
|
||||
<template #title>{{ item.tooltip }}</template>
|
||||
<span :class="`${prefixCls}-toolbar__icon`" v-if="item.icon" @click="onControl(item)">
|
||||
|
@ -53,3 +53,44 @@ export const BpmnNode = [
|
||||
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>
|
||||
<Form
|
||||
v-bind="{ ...$attrs, ...$props, ...getProps }"
|
||||
v-bind="getBindValue"
|
||||
:class="getFormClass"
|
||||
ref="formElRef"
|
||||
:model="formModel"
|
||||
@keypress.enter="handleEnterPress"
|
||||
>
|
||||
<Row v-bind="{ ...getRow }">
|
||||
<Row v-bind="getRow">
|
||||
<slot name="formHeader"></slot>
|
||||
<template v-for="schema in getSchema" :key="schema.field">
|
||||
<FormItem
|
||||
@ -39,7 +39,7 @@
|
||||
<script lang="ts">
|
||||
import type { FormActionType, FormProps, FormSchema } from './types/form';
|
||||
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 { Form, Row } from 'ant-design-vue';
|
||||
@ -69,7 +69,7 @@
|
||||
components: { FormItem, Form, Row, FormAction },
|
||||
props: basicProps,
|
||||
emits: ['advanced-change', 'reset', 'submit', 'register'],
|
||||
setup(props, { emit }) {
|
||||
setup(props, { emit, attrs }) {
|
||||
const formModel = reactive<Recordable>({});
|
||||
const modalFn = useModalContext();
|
||||
|
||||
@ -103,7 +103,7 @@
|
||||
});
|
||||
|
||||
// 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);
|
||||
return {
|
||||
style: baseRowStyle,
|
||||
@ -111,6 +111,10 @@
|
||||
};
|
||||
});
|
||||
|
||||
const getBindValue = computed(
|
||||
() => ({ ...attrs, ...props, ...unref(getProps) } as Recordable)
|
||||
);
|
||||
|
||||
const getSchema = computed((): FormSchema[] => {
|
||||
const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
|
||||
for (const schema of schemas) {
|
||||
@ -260,6 +264,7 @@
|
||||
});
|
||||
|
||||
return {
|
||||
getBindValue,
|
||||
handleToggleAdvanced,
|
||||
handleEnterPress,
|
||||
formModel,
|
||||
|
@ -26,7 +26,6 @@
|
||||
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
|
||||
import { useAttrs } from '/@/hooks/core/useAttrs';
|
||||
import { get, omit } from 'lodash-es';
|
||||
|
||||
import { LoadingOutlined } from '@ant-design/icons-vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
|
@ -40,13 +40,11 @@
|
||||
<script lang="ts">
|
||||
import type { ColEx } from '../types/index';
|
||||
import type { ButtonProps } from 'ant-design-vue/es/button/buttonTypes';
|
||||
|
||||
import { defineComponent, computed, PropType } from 'vue';
|
||||
import { Form, Col } from 'ant-design-vue';
|
||||
import { Button } from '/@/components/Button';
|
||||
import { BasicArrow } from '/@/components/Basic/index';
|
||||
import { useFormContext } from '../hooks/useFormContext';
|
||||
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
|
||||
|
@ -4,17 +4,14 @@
|
||||
import type { FormSchema } from '../types/form';
|
||||
import type { ValidationRule } from 'ant-design-vue/lib/form/Form';
|
||||
import type { TableActionType } from '/@/components/Table';
|
||||
|
||||
import { defineComponent, computed, unref, toRefs } from 'vue';
|
||||
import { Form, Col } from 'ant-design-vue';
|
||||
import { componentMap } from '../componentMap';
|
||||
import { BasicHelp } from '/@/components/Basic';
|
||||
|
||||
import { isBoolean, isFunction, isNull } from '/@/utils/is';
|
||||
import { getSlot } from '/@/utils/helper/tsxHelper';
|
||||
import { createPlaceholderMessage, setComponentRuleType } from '../helper';
|
||||
import { upperFirst, cloneDeep } from 'lodash-es';
|
||||
|
||||
import { useItemLabelWidth } from '../hooks/useLabelWidth';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
|
||||
@ -91,7 +88,6 @@
|
||||
if (isBoolean(dynamicDisabled)) {
|
||||
disabled = dynamicDisabled;
|
||||
}
|
||||
|
||||
if (isFunction(dynamicDisabled)) {
|
||||
disabled = dynamicDisabled(unref(getValues));
|
||||
}
|
||||
@ -229,9 +225,10 @@
|
||||
const eventKey = `on${upperFirst(changeEvent)}`;
|
||||
|
||||
const on = {
|
||||
[eventKey]: (e: Nullable<Recordable>) => {
|
||||
[eventKey]: (...args: Nullable<Recordable>[]) => {
|
||||
const [e] = args;
|
||||
if (propsData[eventKey]) {
|
||||
propsData[eventKey](e);
|
||||
propsData[eventKey](...args);
|
||||
}
|
||||
const target = e ? e.target : null;
|
||||
const value = target ? (isCheck ? target.checked : target.value) : e;
|
||||
@ -276,7 +273,6 @@
|
||||
: {
|
||||
default: () => renderComponentContent,
|
||||
};
|
||||
|
||||
return <Comp {...compAttr}>{compSlot}</Comp>;
|
||||
}
|
||||
|
||||
@ -317,7 +313,6 @@
|
||||
};
|
||||
|
||||
const showSuffix = !!suffix;
|
||||
|
||||
const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix;
|
||||
|
||||
return (
|
||||
@ -338,16 +333,18 @@
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
|
||||
return () => {
|
||||
const { colProps = {}, colSlot, renderColContent, component } = props.schema;
|
||||
if (!componentMap.has(component)) return null;
|
||||
if (!componentMap.has(component)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { baseColProps = {} } = props.formProps;
|
||||
|
||||
const realColProps = { ...baseColProps, ...colProps };
|
||||
const { isIfShow, isShow } = getShow();
|
||||
|
||||
const values = unref(getValues);
|
||||
|
||||
const getContent = () => {
|
||||
return colSlot
|
||||
? 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
|
||||
-->
|
||||
|
||||
<template>
|
||||
<RadioGroup v-bind="attrs" v-model:value="state" button-style="solid">
|
||||
<template v-for="item in getOptions" :key="`${item.value}`">
|
||||
@ -17,6 +16,7 @@
|
||||
import { isString } from '/@/utils/is';
|
||||
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
|
||||
import { useAttrs } from '/@/hooks/core/useAttrs';
|
||||
|
||||
type OptionsItem = { label: string; value: string | number | boolean; disabled?: boolean };
|
||||
type RadioItem = string | OptionsItem;
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
const attrs = useAttrs();
|
||||
// Embedded in the form, just use the hook binding to perform form verification
|
||||
const [state] = useRuleFormItem(props);
|
||||
|
||||
// Processing options value
|
||||
const getOptions = computed((): OptionsItem[] => {
|
||||
const { options } = props;
|
||||
|
@ -2,10 +2,8 @@ import type { ColEx } from '../types';
|
||||
import type { AdvanceState } from '../types/hooks';
|
||||
import type { ComputedRef, Ref } from 'vue';
|
||||
import type { FormProps, FormSchema } from '../types/form';
|
||||
|
||||
import { computed, unref, watch } from 'vue';
|
||||
import { isBoolean, isFunction, isNumber, isObject } from '/@/utils/is';
|
||||
|
||||
import { useBreakpoint } from '/@/hooks/event/useBreakpoint';
|
||||
import { useDebounceFn } from '@vueuse/core';
|
||||
|
||||
|
@ -16,16 +16,22 @@ export async function useAutoFocus({
|
||||
isInitedDefault,
|
||||
}: UseAutoFocusContext) {
|
||||
watchEffect(async () => {
|
||||
if (unref(isInitedDefault) || !unref(getProps).autoFocusFirstItem) return;
|
||||
if (unref(isInitedDefault) || !unref(getProps).autoFocusFirstItem) {
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
const schemas = unref(getSchema);
|
||||
const formEl = unref(formElRef);
|
||||
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];
|
||||
// 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>;
|
||||
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 { NamePath } from 'ant-design-vue/lib/form/interface';
|
||||
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>;
|
||||
|
||||
|
@ -1,9 +1,7 @@
|
||||
import type { ComputedRef, Ref } from 'vue';
|
||||
import type { FormProps, FormSchema, FormActionType } from '../types/form';
|
||||
import type { NamePath } from 'ant-design-vue/lib/form/interface';
|
||||
|
||||
import { unref, toRaw } from 'vue';
|
||||
|
||||
import { isArray, isFunction, isObject, isString } from '/@/utils/is';
|
||||
import { deepMerge } from '/@/utils';
|
||||
import { dateItemType, handleInputNumberValue } from '../helper';
|
||||
|
@ -1,10 +1,8 @@
|
||||
import { isArray, isFunction, isObject, isString, isNullOrUnDef } from '/@/utils/is';
|
||||
import { dateUtil } from '/@/utils/dateUtil';
|
||||
|
||||
import { unref } from 'vue';
|
||||
import type { Ref, ComputedRef } from 'vue';
|
||||
import type { FormProps, FormSchema } from '../types/form';
|
||||
|
||||
import { set } from 'lodash-es';
|
||||
|
||||
interface UseFormValuesContext {
|
||||
|
@ -1,7 +1,6 @@
|
||||
import type { NamePath, RuleObject } from 'ant-design-vue/lib/form/interface';
|
||||
import type { VNode } from 'vue';
|
||||
import type { ButtonProps as AntdButtonProps } from 'ant-design-vue/es/button/buttonTypes';
|
||||
|
||||
import type { FormItem } from './formItem';
|
||||
import type { ColEx, ComponentType } from './index';
|
||||
import type { TableActionType } from '/@/components/Table/src/types/table';
|
||||
|
@ -90,9 +90,7 @@ export type ComponentType =
|
||||
| 'InputCountDown'
|
||||
| 'Select'
|
||||
| 'ApiSelect'
|
||||
| 'SelectOptGroup'
|
||||
| 'TreeSelect'
|
||||
| 'Transfer'
|
||||
| 'RadioButtonGroup'
|
||||
| 'RadioGroup'
|
||||
| 'Checkbox'
|
||||
|
@ -5,10 +5,8 @@
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { PropType } from 'vue';
|
||||
|
||||
import { defineComponent } from 'vue';
|
||||
import { Spin } from 'ant-design-vue';
|
||||
|
||||
import { SizeEnum } from '/@/enums/sizeEnum';
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { VNode, defineComponent } from 'vue';
|
||||
import type { LoadingProps } from './types';
|
||||
import type { LoadingProps } from './typing';
|
||||
|
||||
import { createVNode, render, reactive, h } from 'vue';
|
||||
import Loading from './Loading.vue';
|
||||
@ -20,8 +20,8 @@ export function createLoading(props?: Partial<LoadingProps>, target?: HTMLElemen
|
||||
|
||||
vm = createVNode(LoadingWrap);
|
||||
|
||||
// TODO fix https://github.com/anncwb/vue-vben-admin/issues/438
|
||||
if (wait) {
|
||||
// TODO fix https://github.com/anncwb/vue-vben-admin/issues/438
|
||||
setTimeout(() => {
|
||||
render(vm, document.createElement('div'));
|
||||
}, 0);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { unref } from 'vue';
|
||||
import { createLoading } from './createLoading';
|
||||
import type { LoadingProps } from './types';
|
||||
import type { LoadingProps } from './typing';
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
export interface UseLoadingOptions {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||
export const MarkDown = createAsyncComponent(() => import('./src/Markdown.vue'));
|
||||
import { withInstall } from '/@/utils';
|
||||
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';
|
||||
import Vditor from 'vditor';
|
||||
import 'vditor/dist/index.css';
|
||||
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { useLocale } from '/@/locales/useLocale';
|
||||
import { useModalContext } from '../../Modal';
|
||||
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
||||
|
||||
type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined;
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
height: propTypes.number.def(360),
|
||||
value: propTypes.string.def(''),
|
||||
height: { type: Number, default: 360 },
|
||||
value: { type: String, default: '' },
|
||||
},
|
||||
emits: ['change', 'get'],
|
||||
setup(props, { attrs, emit }) {
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { withInstall } from '/@/utils';
|
||||
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 { useModal, useModalInner } from './src/hooks/useModal';
|
||||
export * from './src/types';
|
||||
export * from './src/typing';
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Modal @cancel="handleCancel" v-bind="getBindValue">
|
||||
<Modal v-bind="getBindValue">
|
||||
<template #closeIcon v-if="!$slots.closeIcon">
|
||||
<ModalClose
|
||||
:canFullscreen="getProps.canFullscreen"
|
||||
@ -49,7 +49,7 @@
|
||||
</Modal>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { ModalProps, ModalMethods } from './types';
|
||||
import type { ModalProps, ModalMethods } from './typing';
|
||||
|
||||
import {
|
||||
defineComponent,
|
||||
@ -62,26 +62,23 @@
|
||||
getCurrentInstance,
|
||||
nextTick,
|
||||
} from 'vue';
|
||||
|
||||
import Modal from './components/Modal';
|
||||
import ModalWrapper from './components/ModalWrapper.vue';
|
||||
import ModalClose from './components/ModalClose.vue';
|
||||
import ModalFooter from './components/ModalFooter.vue';
|
||||
import ModalHeader from './components/ModalHeader.vue';
|
||||
|
||||
import { isFunction } from '/@/utils/is';
|
||||
import { deepMerge } from '/@/utils';
|
||||
|
||||
import { basicProps } from './props';
|
||||
import { useFullScreen } from './hooks/useModalFullScreen';
|
||||
|
||||
import { omit } from 'lodash-es';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BasicModal',
|
||||
components: { Modal, ModalWrapper, ModalClose, ModalFooter, ModalHeader },
|
||||
inheritAttrs: false,
|
||||
props: basicProps,
|
||||
emits: ['visible-change', 'height-change', 'cancel', 'ok', 'register'],
|
||||
emits: ['visible-change', 'height-change', 'cancel', 'ok', 'register', 'update:visible'],
|
||||
setup(props, { emit, attrs }) {
|
||||
const visibleRef = ref(false);
|
||||
const propsRef = ref<Partial<ModalProps> | null>(null);
|
||||
@ -157,6 +154,7 @@
|
||||
() => unref(visibleRef),
|
||||
(v) => {
|
||||
emit('visible-change', v);
|
||||
emit('update:visible', v);
|
||||
instance && modalMethods.emitVisible?.(v, instance.uid);
|
||||
nextTick(() => {
|
||||
if (props.scrollTop && v && unref(modalWrapperRef)) {
|
||||
@ -180,7 +178,7 @@
|
||||
}
|
||||
|
||||
visibleRef.value = false;
|
||||
emit('cancel');
|
||||
emit('cancel', e);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -188,13 +186,13 @@
|
||||
*/
|
||||
function setModalProps(props: Partial<ModalProps>): void {
|
||||
// Keep the last setModalProps
|
||||
propsRef.value = deepMerge(unref(propsRef) || {}, props);
|
||||
propsRef.value = deepMerge(unref(propsRef) || ({} as any), props);
|
||||
if (!Reflect.has(props, 'visible')) return;
|
||||
visibleRef.value = !!props.visible;
|
||||
}
|
||||
|
||||
function handleOk() {
|
||||
emit('ok');
|
||||
function handleOk(e: Event) {
|
||||
emit('ok', e);
|
||||
}
|
||||
|
||||
function handleHeightChange(height: string) {
|
||||
|
@ -20,7 +20,6 @@ export default defineComponent({
|
||||
|
||||
return () => {
|
||||
const propsData = { ...unref(attrs), ...props } as Recordable;
|
||||
|
||||
return <Modal {...propsData}>{extendSlots(slots)}</Modal>;
|
||||
};
|
||||
},
|
||||
|
@ -2,7 +2,6 @@
|
||||
<div :class="getClass">
|
||||
<template v-if="canFullscreen">
|
||||
<FullscreenExitOutlined role="full" @click="handleFullScreen" v-if="fullScreen" />
|
||||
|
||||
<FullscreenOutlined role="close" @click="handleFullScreen" v-else />
|
||||
</template>
|
||||
<CloseOutlined @click="handleCancel" />
|
||||
@ -12,14 +11,13 @@
|
||||
import { defineComponent, computed } from 'vue';
|
||||
import { FullscreenExitOutlined, FullscreenOutlined, CloseOutlined } from '@ant-design/icons-vue';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ModalClose',
|
||||
components: { FullscreenExitOutlined, FullscreenOutlined, CloseOutlined },
|
||||
props: {
|
||||
canFullscreen: propTypes.bool.def(true),
|
||||
fullScreen: propTypes.bool,
|
||||
canFullscreen: { type: Boolean, default: true },
|
||||
fullScreen: { type: Boolean },
|
||||
},
|
||||
emits: ['cancel', 'fullscreen'],
|
||||
setup(props, { emit }) {
|
||||
@ -35,9 +33,10 @@
|
||||
];
|
||||
});
|
||||
|
||||
function handleCancel() {
|
||||
emit('cancel');
|
||||
function handleCancel(e: Event) {
|
||||
emit('cancel', e);
|
||||
}
|
||||
|
||||
function handleFullScreen(e: Event) {
|
||||
e?.stopPropagation();
|
||||
e?.preventDefault();
|
||||
|
@ -26,13 +26,14 @@
|
||||
props: basicProps,
|
||||
emits: ['ok', 'cancel'],
|
||||
setup(_, { emit }) {
|
||||
function handleOk() {
|
||||
emit('ok');
|
||||
function handleOk(e: Event) {
|
||||
emit('ok', e);
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
emit('cancel');
|
||||
function handleCancel(e: Event) {
|
||||
emit('cancel', e);
|
||||
}
|
||||
|
||||
return { handleOk, handleCancel };
|
||||
},
|
||||
});
|
||||
|
@ -8,7 +8,6 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import { BasicTitle } from '/@/components/Basic';
|
||||
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
export default defineComponent({
|
||||
name: 'BasicModalHeader',
|
||||
components: { BasicTitle },
|
||||
@ -16,7 +15,7 @@
|
||||
helpMessage: {
|
||||
type: [String, Array] as PropType<string | string[]>,
|
||||
},
|
||||
title: propTypes.string,
|
||||
title: { type: String },
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -6,9 +6,7 @@
|
||||
</ScrollContainer>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { ModalWrapperProps } from '../types';
|
||||
import type { CSSProperties } from 'vue';
|
||||
|
||||
import {
|
||||
defineComponent,
|
||||
computed,
|
||||
@ -20,31 +18,31 @@
|
||||
nextTick,
|
||||
onUnmounted,
|
||||
} from 'vue';
|
||||
|
||||
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
|
||||
import { ScrollContainer } from '/@/components/Container';
|
||||
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
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({
|
||||
name: 'ModalWrapper',
|
||||
components: { ScrollContainer },
|
||||
inheritAttrs: false,
|
||||
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,
|
||||
},
|
||||
props,
|
||||
emits: ['height-change', 'ext-height'],
|
||||
setup(props: ModalWrapperProps, { emit }) {
|
||||
setup(props, { emit }) {
|
||||
const wrapperRef = ref<ComponentRef>(null);
|
||||
const spinRef = ref<ElRef>(null);
|
||||
const realHeightRef = ref(0);
|
||||
@ -56,6 +54,17 @@
|
||||
|
||||
useWindowSizeFn(setModalHeight.bind(null, false));
|
||||
|
||||
useMutationObserver(
|
||||
spinRef,
|
||||
() => {
|
||||
setModalHeight();
|
||||
},
|
||||
{
|
||||
attributes: true,
|
||||
subtree: true,
|
||||
}
|
||||
);
|
||||
|
||||
createModalContext({
|
||||
redoModalHeight: setModalHeight,
|
||||
});
|
||||
@ -63,8 +72,7 @@
|
||||
const spinStyle = computed((): CSSProperties => {
|
||||
return {
|
||||
minHeight: `${props.minHeight}px`,
|
||||
// padding 28
|
||||
maxHeight: `${unref(realHeightRef)}px`,
|
||||
[props.fullScreen ? 'height' : 'maxHeight']: `${unref(realHeightRef)}px`,
|
||||
};
|
||||
});
|
||||
|
||||
@ -87,7 +95,6 @@
|
||||
onMounted(() => {
|
||||
const { modalHeaderHeight, modalFooterHeight } = props;
|
||||
emit('ext-height', modalHeaderHeight + modalFooterHeight);
|
||||
// listenElResize();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
|
@ -4,8 +4,7 @@ import type {
|
||||
ModalProps,
|
||||
ReturnMethods,
|
||||
UseModalInnerReturnType,
|
||||
} from '../types';
|
||||
|
||||
} from '../typing';
|
||||
import {
|
||||
ref,
|
||||
onUnmounted,
|
||||
@ -20,10 +19,10 @@ import { isProdMode } from '/@/utils/env';
|
||||
import { isFunction } from '/@/utils/is';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { tryOnUnmounted } from '@vueuse/core';
|
||||
|
||||
import { error } from '/@/utils/log';
|
||||
import { computed } from 'vue';
|
||||
const dataTransferRef = reactive<any>({});
|
||||
|
||||
const dataTransfer = reactive<any>({});
|
||||
|
||||
const visibleData = reactive<{ [key: number]: boolean }>({});
|
||||
|
||||
@ -31,29 +30,31 @@ const visibleData = reactive<{ [key: number]: boolean }>({});
|
||||
* @description: Applicable to independent modal and call outside
|
||||
*/
|
||||
export function useModal(): UseModalReturnType {
|
||||
const modalRef = ref<Nullable<ModalMethods>>(null);
|
||||
const loadedRef = ref<Nullable<boolean>>(false);
|
||||
const uidRef = ref<string>('');
|
||||
const modal = ref<Nullable<ModalMethods>>(null);
|
||||
const loaded = ref<Nullable<boolean>>(false);
|
||||
const uid = ref<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() &&
|
||||
onUnmounted(() => {
|
||||
modalRef.value = null;
|
||||
loadedRef.value = false;
|
||||
dataTransferRef[unref(uidRef)] = null;
|
||||
modal.value = null;
|
||||
loaded.value = false;
|
||||
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) => {
|
||||
visibleData[uid] = visible;
|
||||
};
|
||||
}
|
||||
|
||||
const getInstance = () => {
|
||||
const instance = unref(modalRef);
|
||||
const instance = unref(modal);
|
||||
if (!instance) {
|
||||
error('useModal instance is undefined!');
|
||||
}
|
||||
@ -66,7 +67,7 @@ export function useModal(): UseModalReturnType {
|
||||
},
|
||||
|
||||
getVisible: computed((): boolean => {
|
||||
return visibleData[~~unref(uidRef)];
|
||||
return visibleData[~~unref(uid)];
|
||||
}),
|
||||
|
||||
redoModalHeight: () => {
|
||||
@ -79,15 +80,15 @@ export function useModal(): UseModalReturnType {
|
||||
});
|
||||
|
||||
if (!data) return;
|
||||
|
||||
const id = unref(uid);
|
||||
if (openOnSet) {
|
||||
dataTransferRef[unref(uidRef)] = null;
|
||||
dataTransferRef[unref(uidRef)] = toRaw(data);
|
||||
dataTransfer[id] = null;
|
||||
dataTransfer[id] = toRaw(data);
|
||||
return;
|
||||
}
|
||||
const equal = isEqual(toRaw(dataTransferRef[unref(uidRef)]), toRaw(data));
|
||||
const equal = isEqual(toRaw(dataTransfer[id]), toRaw(data));
|
||||
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 uidRef = ref<string>('');
|
||||
|
||||
// currentInstall.type.emits = [...currentInstall.type.emits, 'register'];
|
||||
// Object.assign(currentInstall.type.emits, ['register']);
|
||||
|
||||
const getInstance = () => {
|
||||
const instance = unref(modalInstanceRef);
|
||||
if (!instance) {
|
||||
@ -125,7 +123,7 @@ export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => {
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
const data = dataTransferRef[unref(uidRef)];
|
||||
const data = dataTransfer[unref(uidRef)];
|
||||
if (!data) return;
|
||||
if (!callbackFn || !isFunction(callbackFn)) return;
|
||||
nextTick(() => {
|
||||
|
@ -12,7 +12,6 @@ export function useFullScreen(context: UseFullScreenContext) {
|
||||
|
||||
const getWrapClassName = computed(() => {
|
||||
const clsName = unref(context.wrapClassName) || '';
|
||||
|
||||
return unref(fullScreenRef) ? `fullscreen-modal ${clsName} ` : unref(clsName);
|
||||
});
|
||||
|
||||
|
@ -1,45 +1,44 @@
|
||||
import type { PropType, CSSProperties } from 'vue';
|
||||
import type { ModalWrapperProps } from './typing';
|
||||
import { ButtonProps } from 'ant-design-vue/es/button/buttonTypes';
|
||||
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { propTypes, VueNode } from '/@/utils/propTypes';
|
||||
import type { ModalWrapperProps } from './types';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
export const modalProps = {
|
||||
visible: propTypes.bool,
|
||||
scrollTop: propTypes.bool.def(true),
|
||||
height: propTypes.number,
|
||||
minHeight: propTypes.number,
|
||||
visible: { type: Boolean },
|
||||
scrollTop: { type: Boolean, default: true },
|
||||
height: { type: Number },
|
||||
minHeight: { type: Number },
|
||||
// open drag
|
||||
draggable: propTypes.bool.def(true),
|
||||
centered: propTypes.bool,
|
||||
cancelText: propTypes.string.def(t('common.cancelText')),
|
||||
okText: propTypes.string.def(t('common.okText')),
|
||||
draggable: { type: Boolean, default: true },
|
||||
centered: { type: Boolean },
|
||||
cancelText: { type: String, default: t('common.cancelText') },
|
||||
okText: { type: String, default: t('common.okText') },
|
||||
|
||||
closeFunc: Function as PropType<() => Promise<boolean>>,
|
||||
};
|
||||
|
||||
export const basicProps = Object.assign({}, modalProps, {
|
||||
defaultFullscreen: propTypes.bool,
|
||||
defaultFullscreen: { type: Boolean },
|
||||
// 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
|
||||
wrapperFooterOffset: propTypes.number.def(0),
|
||||
wrapperFooterOffset: { type: Number, default: 0 },
|
||||
// Warm reminder message
|
||||
helpMessage: [String, Array] as PropType<string | string[]>,
|
||||
// Whether to setting wrapper
|
||||
useWrapper: propTypes.bool.def(true),
|
||||
loading: propTypes.bool,
|
||||
loadingTip: propTypes.string,
|
||||
useWrapper: { type: Boolean, default: true },
|
||||
loading: { type: Boolean },
|
||||
loadingTip: { type: String },
|
||||
/**
|
||||
* @description: Show close button
|
||||
*/
|
||||
showCancelBtn: propTypes.bool.def(true),
|
||||
showCancelBtn: { type: Boolean, default: true },
|
||||
/**
|
||||
* @description: Show confirmation button
|
||||
*/
|
||||
showOkBtn: propTypes.bool.def(true),
|
||||
showOkBtn: { type: Boolean, default: true },
|
||||
|
||||
wrapperProps: Object as PropType<Partial<ModalWrapperProps>>,
|
||||
|
||||
@ -47,38 +46,38 @@ export const basicProps = Object.assign({}, modalProps, {
|
||||
|
||||
bodyStyle: Object as PropType<CSSProperties>,
|
||||
|
||||
closable: propTypes.bool.def(true),
|
||||
closable: { type: Boolean, default: true },
|
||||
|
||||
closeIcon: Object as PropType<VueNode>,
|
||||
|
||||
confirmLoading: propTypes.bool,
|
||||
confirmLoading: { type: Boolean },
|
||||
|
||||
destroyOnClose: propTypes.bool,
|
||||
destroyOnClose: { type: Boolean },
|
||||
|
||||
footer: Object as PropType<VueNode>,
|
||||
|
||||
getContainer: Function as PropType<() => any>,
|
||||
|
||||
mask: propTypes.bool.def(true),
|
||||
mask: { type: Boolean, default: true },
|
||||
|
||||
maskClosable: propTypes.bool.def(true),
|
||||
keyboard: propTypes.bool.def(true),
|
||||
maskClosable: { type: Boolean, default: true },
|
||||
keyboard: { type: Boolean, default: true },
|
||||
|
||||
maskStyle: Object as PropType<CSSProperties>,
|
||||
|
||||
okType: propTypes.string.def('primary'),
|
||||
okType: { type: String, default: 'primary' },
|
||||
|
||||
okButtonProps: 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>,
|
||||
|
||||
wrapClassName: propTypes.string,
|
||||
wrapClassName: { type: String },
|
||||
|
||||
zIndex: propTypes.number,
|
||||
zIndex: { type: Number },
|
||||
});
|
||||
|
@ -1,2 +1,7 @@
|
||||
export { default as PageFooter } from './src/PageFooter.vue';
|
||||
export { default as PageWrapper } from './src/PageWrapper.vue';
|
||||
import { withInstall } from '/@/utils';
|
||||
|
||||
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>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
|
@ -44,6 +44,8 @@
|
||||
import { omit } from 'lodash-es';
|
||||
import { PageHeader } from 'ant-design-vue';
|
||||
import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
|
||||
import { useLayoutHeight } from '/@/layouts/default/content/useContentViewHeight';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PageWrapper',
|
||||
components: { PageFooter, PageHeader },
|
||||
@ -67,6 +69,7 @@
|
||||
const footerHeight = ref(0);
|
||||
const { prefixCls, prefixVar } = useDesign('page-wrapper');
|
||||
const { contentHeight, setPageHeight, pageHeight } = usePageContext();
|
||||
const { footerHeightRef } = useLayoutHeight();
|
||||
|
||||
const getClass = computed(() => {
|
||||
return [
|
||||
@ -93,7 +96,6 @@
|
||||
...contentStyle,
|
||||
minHeight: height,
|
||||
...(fixedHeight ? { height } : {}),
|
||||
paddingBottom: `${unref(footerHeight)}px`,
|
||||
};
|
||||
});
|
||||
|
||||
@ -109,7 +111,7 @@
|
||||
});
|
||||
|
||||
watch(
|
||||
() => [contentHeight?.value, getShowFooter.value],
|
||||
() => [contentHeight?.value, getShowFooter.value, footerHeightRef.value],
|
||||
() => {
|
||||
calcContentHeight();
|
||||
},
|
||||
@ -163,7 +165,36 @@
|
||||
const contentMarginTop = Number(marginTop.replace(/[^\d]/g, ''));
|
||||
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 {
|
||||
|
@ -1 +1,2 @@
|
||||
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 { toDataURL } from 'qrcode';
|
||||
import { downloadByUrl } from '/@/utils/file/download';
|
||||
import { QrcodeDoneEventParams } from './types';
|
||||
import { QrcodeDoneEventParams } from './typing';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'QrCode',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { toCanvas } 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) => {
|
||||
// 容错率,默认对内容少的二维码采用高容错率,内容多的二维码采用低容错率
|
||||
options.errorCorrectionLevel = options.errorCorrectionLevel || getErrorCorrectionLevel(content);
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { isString } from '/@/utils/is';
|
||||
import { RenderQrCodeParams, LogoType } from './types';
|
||||
import { RenderQrCodeParams, LogoType } from './typing';
|
||||
export const drawLogo = ({ canvas, logo }: RenderQrCodeParams) => {
|
||||
if (!logo) {
|
||||
return new Promise((resolve) => {
|
||||
resolve((canvas as HTMLCanvasElement).toDataURL());
|
||||
});
|
||||
}
|
||||
|
||||
const canvasWidth = (canvas as HTMLCanvasElement).width;
|
||||
const {
|
||||
logoSize = 0.15,
|
||||
|
@ -1,5 +1,4 @@
|
||||
// 参考 qr-code-with-logo 进行ts版本修改
|
||||
import { toCanvas } from './toCanvas';
|
||||
export * from './types';
|
||||
|
||||
export * from './typing';
|
||||
export { toCanvas };
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { renderQrCode } from './drawCanvas';
|
||||
import { drawLogo } from './drawLogo';
|
||||
import { RenderQrCodeParams } from './types';
|
||||
import { RenderQrCodeParams } from './typing';
|
||||
export const toCanvas = (options: RenderQrCodeParams) => {
|
||||
return renderQrCode(options)
|
||||
.then(() => {
|
||||
|
@ -22,7 +22,7 @@
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { createSimpleRootMenuContext } from './useSimpleMenuContext';
|
||||
import Mitt from '/@/utils/mitt';
|
||||
import mitt from '/@/utils/mitt';
|
||||
export default defineComponent({
|
||||
name: 'Menu',
|
||||
props: {
|
||||
@ -44,7 +44,7 @@
|
||||
},
|
||||
emits: ['select', 'open-change'],
|
||||
setup(props, { emit }) {
|
||||
const rootMenuEmitter = new Mitt();
|
||||
const rootMenuEmitter = mitt();
|
||||
const instance = getCurrentInstance();
|
||||
|
||||
const currentActiveName = ref<string | number>('');
|
||||
|
@ -77,7 +77,7 @@
|
||||
import Icon from '/@/components/Icon';
|
||||
import { Popover } from 'ant-design-vue';
|
||||
import { isBoolean, isObject } from '/@/utils/is';
|
||||
import Mitt from '/@/utils/mitt';
|
||||
import mitt from '/@/utils/mitt';
|
||||
|
||||
const DELAY = 200;
|
||||
export default defineComponent({
|
||||
@ -114,7 +114,7 @@
|
||||
|
||||
const { prefixCls } = useDesign('menu');
|
||||
|
||||
const subMenuEmitter = new Mitt();
|
||||
const subMenuEmitter = mitt();
|
||||
|
||||
const { rootMenuEmitter } = useSimpleRootMenuContext();
|
||||
|
||||
@ -273,8 +273,7 @@
|
||||
state.opened = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isObject(data)) {
|
||||
if (isObject(data) && rootProps.accordion) {
|
||||
const { opend, parent, uidList } = data as Recordable;
|
||||
if (parent === instance?.parent) {
|
||||
state.opened = opend;
|
||||
|
@ -1,9 +1,9 @@
|
||||
import type { InjectionKey, Ref } from 'vue';
|
||||
import { createContext, useContext } from '/@/hooks/core/useContext';
|
||||
import Mitt from '/@/utils/mitt';
|
||||
import mitt from '/@/utils/mitt';
|
||||
|
||||
export interface SimpleRootMenuContextProps {
|
||||
rootMenuEmitter: Mitt;
|
||||
rootMenuEmitter: typeof mitt;
|
||||
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">
|
||||
import { defineComponent, computed, ref, watch, unref, watchEffect } from 'vue';
|
||||
|
||||
import { Input } from 'ant-design-vue';
|
||||
|
||||
// @ts-ignore
|
||||
import { zxcvbn } from '@zxcvbn-ts/core';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
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 EditTableHeaderIcon } from './src/components/EditTableHeaderIcon.vue';
|
||||
export { default as TableImg } from './src/components/TableImg.vue';
|
||||
|
||||
export * from './src/types/table';
|
||||
export * from './src/types/pagination';
|
||||
export * from './src/types/tableAction';
|
||||
|
||||
export { useTable } from './src/hooks/useTable';
|
||||
|
||||
export type { FormSchema, FormProps } from '/@/components/Form/src/types/form';
|
||||
|
||||
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