chore: merge main
This commit is contained in:
commit
99cabf048d
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ test/server/static
|
|||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
.eslintcache
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,37 @@
|
|||
## 2.4.2(2021-06-10)
|
||||
|
||||
### ✨ Refactor
|
||||
|
||||
- `CountTo` component refactoring
|
||||
|
||||
### ✨ Features
|
||||
|
||||
- `radioButtonGroup` supports `boolean` value
|
||||
- `useModalInner` added `redoModalHeight` to reset the height of `Modal` inside Modal
|
||||
- `useECharts` added `getInstance` to obtain instances of `echart`
|
||||
- `TableAction` added `stopButtonPropagation` to prevent the action button click event from bubbling
|
||||
- `BasicTable` in the row edit mode, you can get or set the value of other editing components in the column
|
||||
- The `ApiSelect` component will automatically re-fetch the data after the `params` is changed
|
||||
- `TableImg` component improvement
|
||||
- `BasicTable` added `columns-change` event to monitor the user to change the sorting, display, and fixed status of columns
|
||||
- `Tinymce` supports dynamic modification readonly
|
||||
- `BasicTable` added `updateTableDataRecord` method to update the specified row data
|
||||
- `useModal` added `closeModal` method to close `Modal`
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- Fix the problem that `redoModalHeight` cannot reduce the height
|
||||
- Fix the problem that the schema data of `BasicForm` does not take effect
|
||||
- Fix the problem that multiple tags may cause `KeepAlive` to fail
|
||||
- Fix the problem that the default `axios` interceptor cannot handle custom code
|
||||
- Fix the height issue of the lock screen pop-up window
|
||||
- Fixed the problem that the half-selected state of the `Column Display` checkbox of `BaiscTable` was incorrectly displayed
|
||||
- Fixed the problem that the preview list of the `BasicUpload` component could not be displayed in some cases
|
||||
- Fix the problem that the `options` setting of ` RadioButtonGroup``disabled ` does not take effect
|
||||
- Fix the problem that the button for uploading pictures in the read-only mode of the `Tinymce` component is still available
|
||||
- Fix the stuttering problem of `BasicForm` under certain circumstances
|
||||
- Fix the problem that "directory" routing does not work
|
||||
|
||||
## 2.4.1(2021-06-01)
|
||||
|
||||
### ✨ Features
|
||||
|
|
|
|||
184
CHANGELOG.md
184
CHANGELOG.md
|
|
@ -1,3 +1,187 @@
|
|||
# [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
|
||||
|
||||
- fix darkModeSwitch switch failure ([34a8054](https://github.com/anncwb/vue-vben-admin/commit/34a80542de670f0385dffaf5bf64bb9c3f6b90da))
|
||||
- **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)
|
||||
- **avatar:** mock data and Account center style ([2066f66](https://github.com/anncwb/vue-vben-admin/commit/2066f669715491f3e91ac6d0e905cd2b3e80b58d))
|
||||
- **axios:** transformRequestHook logic error ([b69dcd7](https://github.com/anncwb/vue-vben-admin/commit/b69dcd79d742fd171302ce0f48c7750d60da217f))
|
||||
- **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))
|
||||
- **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:** 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)
|
||||
- **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)
|
||||
- **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))
|
||||
- **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))
|
||||
- **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)
|
||||
- build error ([5212ea7](https://github.com/anncwb/vue-vben-admin/commit/5212ea79b43c832a5136354b549de8f89b6e2156))
|
||||
- **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))
|
||||
- **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))
|
||||
- **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))
|
||||
- **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))
|
||||
- **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)
|
||||
- **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))
|
||||
- **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))
|
||||
- **upload:** ensure preview items valid ([4376928](https://github.com/anncwb/vue-vben-admin/commit/437692869a232ee65c300c65ee473557ae0913c7))
|
||||
- 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))
|
||||
- **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))
|
||||
- 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)
|
||||
- **tree:** support defaultExpandAll prop ([3ed2339](https://github.com/anncwb/vue-vben-admin/commit/3ed2339a6d75abbd6ccf723b6eaa762f9921409e))
|
||||
- theme switching fails ([7e2ca79](https://github.com/anncwb/vue-vben-admin/commit/7e2ca79ece2f5209cb7ce4b0f5ee15012f9f51de))
|
||||
|
||||
### Features
|
||||
|
||||
- **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))
|
||||
- **demo:** `switch` use in table ([46899aa](https://github.com/anncwb/vue-vben-admin/commit/46899aa3cd6b1616c42ac263a28af75be839f6a0))
|
||||
- **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))
|
||||
- **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))
|
||||
- **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
|
||||
|
||||
- optimize components and add comments ([55e9d9f](https://github.com/anncwb/vue-vben-admin/commit/55e9d9fc2953643cec95c74b6ed34b0e68641fb6))
|
||||
- **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))
|
||||
|
||||
## [2.4.1](https://github.com/anncwb/vue-vben-admin/compare/v2.4.0...v2.4.1) (2021-06-01)
|
||||
|
||||
### Bug Fixes
|
||||
|
|
|
|||
|
|
@ -1,3 +1,78 @@
|
|||
## 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
|
||||
|
||||
- `CountTo`组件重构
|
||||
|
||||
### ✨ Features
|
||||
|
||||
- `radioButtonGroup` 支持`boolean`值
|
||||
- `useModalInner` 新增 `redoModalHeight`用于在 Modal 内部重设`Modal`高度
|
||||
- `useECharts` 新增`getInstance`用于获取`echart`实例
|
||||
- `TableAction` 新增 `stopButtonPropagation` 阻止操作按钮点击事件冒泡
|
||||
- `BasicTable` 在行编辑模式下,可以获取或设置其它处于列的编辑组件的值
|
||||
- `ApiSelect` 组件在`params`改变后会自动重新`fetch`数据
|
||||
- `TableImg` 组件改进
|
||||
- `BasicTable` 新增 `columns-change` 事件用于监听用户改变列排序、展示、固定状态
|
||||
- `Tinymce`支持动态修改 readonly
|
||||
- `BasicTable`新增`updateTableDataRecord`方法用于更新指定行数据
|
||||
- `useModal`新增`closeModal`方法用于关闭`Modal`
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- 修复`redoModalHeight`不能减小高度的问题
|
||||
- 修复 `BasicForm`设置 schemas 数据不生效的问题
|
||||
- 修复多标签可能导致`KeepAlive`失效的问题
|
||||
- 修复默认的`axios`拦截器不能处理自定义 code 的问题
|
||||
- 修复锁屏弹窗的高度问题
|
||||
- 修复`BaiscTable`的`列展示`复选框的半选状态显示不正确的问题
|
||||
- 修复`BasicUpload`组件的预览列表某些情况下不能显示的问题
|
||||
- 修复`RadioButtonGroup`的`options`设置`disabled`不生效的问题
|
||||
- 修复`Tinymce`组件在只读模式下上传图片的按钮仍然可用的问题
|
||||
- 修复`BasicForm`特定情况下的卡顿问题
|
||||
- 修复"目录"路由不起作用的问题
|
||||
|
||||
## 2.4.1(2021-06-01)
|
||||
|
||||
### ✨ Features
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
72
package.json
72
package.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "vben-admin",
|
||||
"version": "2.4.1",
|
||||
"version": "2.5.0",
|
||||
"author": {
|
||||
"name": "vben",
|
||||
"email": "anncwb@126.com",
|
||||
|
|
@ -62,11 +62,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",
|
||||
|
|
@ -75,23 +77,24 @@
|
|||
"postinstall": "npm run install:husky"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify/iconify": "^2.0.1",
|
||||
"@logicflow/core": "^0.4.11",
|
||||
"@logicflow/extension": "^0.4.12",
|
||||
"@vueuse/core": "^4.11.2",
|
||||
"@iconify/iconify": "^2.0.2",
|
||||
"@logicflow/core": "^0.5.0",
|
||||
"@logicflow/extension": "^0.5.0",
|
||||
"@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",
|
||||
"codemirror": "^5.61.1",
|
||||
"cropperjs": "^1.5.11",
|
||||
"cropperjs": "^1.5.12",
|
||||
"crypto-js": "^4.0.0",
|
||||
"echarts": "^5.1.1",
|
||||
"electron-is-dev": "^1.2.0",
|
||||
"echarts": "^5.1.2",
|
||||
"intro.js": "^4.1.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"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",
|
||||
"print-js": "^1.6.0",
|
||||
"qrcode": "^1.4.4",
|
||||
"sortablejs": "^1.13.0",
|
||||
|
|
@ -100,14 +103,14 @@
|
|||
"vue": "3.0.11",
|
||||
"vue-i18n": "9.1.6",
|
||||
"vue-json-pretty": "^2.0.2",
|
||||
"vue-router": "^4.0.8",
|
||||
"vue-router": "^4.0.9",
|
||||
"vue-types": "^3.0.2",
|
||||
"xlsx": "^0.17.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^12.1.4",
|
||||
"@commitlint/config-conventional": "^12.1.4",
|
||||
"@iconify/json": "^1.1.353",
|
||||
"@iconify/json": "^1.1.358",
|
||||
"@purge-icons/generated": "^0.7.0",
|
||||
"@rollup/plugin-alias": "^3.1.1",
|
||||
"@rollup/plugin-commonjs": "^15.0.0",
|
||||
|
|
@ -116,20 +119,23 @@
|
|||
"@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.1",
|
||||
"@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.0",
|
||||
"@typescript-eslint/parser": "^4.26.0",
|
||||
"@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",
|
||||
|
|
@ -140,22 +146,25 @@
|
|||
"electron-connect": "^0.6.3",
|
||||
"electron-contextmenu-middleware": "^1.0.3",
|
||||
"electron-input-menu": "^2.1.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.0",
|
||||
"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-esbuild": "^3.0.2",
|
||||
"rollup-plugin-visualizer": "5.5.0",
|
||||
|
|
@ -163,19 +172,20 @@
|
|||
"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.6",
|
||||
"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.0",
|
||||
"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.1",
|
||||
"vue-eslint-parser": "^7.6.0",
|
||||
"vue-tsc": "^0.1.7",
|
||||
"wait-on": "^5.2.1"
|
||||
|
|
@ -183,7 +193,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.0"
|
||||
"rollup": "^2.52.1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,7 +5,11 @@ export interface DemoOptionsItem {
|
|||
value: string;
|
||||
}
|
||||
|
||||
export interface selectParams {
|
||||
id: number | string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: Request list return value
|
||||
*/
|
||||
export type DemoOptionsGetResultModel = BasicFetchResult<DemoOptionsItem[]>;
|
||||
export type DemoOptionsGetResultModel = BasicFetchResult<DemoOptionsItem>;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { DemoOptionsGetResultModel } from './model/optionsModel';
|
||||
|
||||
import { DemoOptionsItem, selectParams } from './model/optionsModel';
|
||||
enum Api {
|
||||
OPTIONS_LIST = '/select/getDemoOptions',
|
||||
}
|
||||
|
|
@ -8,5 +7,5 @@ enum Api {
|
|||
/**
|
||||
* @description: Get sample options value
|
||||
*/
|
||||
export const optionsListApi = () =>
|
||||
defHttp.get<DemoOptionsGetResultModel>({ url: Api.OPTIONS_LIST });
|
||||
export const optionsListApi = (params?: selectParams) =>
|
||||
defHttp.post<DemoOptionsItem[]>({ url: Api.OPTIONS_LIST, params });
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@ export interface BasicPageParams {
|
|||
}
|
||||
|
||||
export interface BasicFetchResult<T extends any> {
|
||||
items: T;
|
||||
items: T[];
|
||||
total: number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -1,8 +1,15 @@
|
|||
import AppLogo from './src/AppLogo.vue';
|
||||
import AppProvider from './src/AppProvider.vue';
|
||||
import AppSearch from './src/search/AppSearch.vue';
|
||||
import AppLocalePicker from './src/AppLocalePicker.vue';
|
||||
import AppDarkModeToggle from './src/AppDarkModeToggle.vue';
|
||||
import { withInstall } from '/@/utils';
|
||||
|
||||
import appLogo from './src/AppLogo.vue';
|
||||
import appProvider from './src/AppProvider.vue';
|
||||
import appSearch from './src/search/AppSearch.vue';
|
||||
import appLocalePicker from './src/AppLocalePicker.vue';
|
||||
import appDarkModeToggle from './src/AppDarkModeToggle.vue';
|
||||
|
||||
export { useAppProviderContext } from './src/useAppContext';
|
||||
export { AppLogo, AppProvider, AppSearch, AppLocalePicker, AppDarkModeToggle };
|
||||
|
||||
export const AppLogo = withInstall(appLogo);
|
||||
export const AppProvider = withInstall(appProvider);
|
||||
export const AppSearch = withInstall(appSearch);
|
||||
export const AppLocalePicker = withInstall(appLocalePicker);
|
||||
export const AppDarkModeToggle = withInstall(appDarkModeToggle);
|
||||
|
|
|
|||
|
|
@ -1,40 +1,35 @@
|
|||
<template>
|
||||
<div
|
||||
v-if="getShowDarkModeToggle"
|
||||
:class="[
|
||||
prefixCls,
|
||||
{
|
||||
[`${prefixCls}--dark`]: isDark,
|
||||
},
|
||||
]"
|
||||
@click="toggleDarkMode"
|
||||
>
|
||||
<div v-if="getShowDarkModeToggle" :class="getClass" @click="toggleDarkMode">
|
||||
<div :class="`${prefixCls}-inner`"> </div>
|
||||
<SvgIcon size="14" name="sun" />
|
||||
<SvgIcon size="14" name="moon" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from 'vue';
|
||||
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
import { defineComponent, computed, unref } from 'vue';
|
||||
import { SvgIcon } from '/@/components/Icon';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
||||
import { updateHeaderBgColor, updateSidebarBgColor } from '/@/logics/theme/updateBackground';
|
||||
import { updateDarkTheme } from '/@/logics/theme/dark';
|
||||
|
||||
import { ThemeEnum } from '/@/enums/appEnum';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DarkModeToggle',
|
||||
components: { SvgIcon },
|
||||
setup() {
|
||||
const { prefixCls } = useDesign('dark-mode-toggle');
|
||||
const { prefixCls } = useDesign('dark-switch');
|
||||
const { getDarkMode, setDarkMode, getShowDarkModeToggle } = useRootSetting();
|
||||
|
||||
const isDark = computed(() => getDarkMode.value === ThemeEnum.DARK);
|
||||
|
||||
const getClass = computed(() => [
|
||||
prefixCls,
|
||||
{
|
||||
[`${prefixCls}--dark`]: unref(isDark),
|
||||
},
|
||||
]);
|
||||
|
||||
function toggleDarkMode() {
|
||||
const darkMode = getDarkMode.value === ThemeEnum.DARK ? ThemeEnum.LIGHT : ThemeEnum.DARK;
|
||||
setDarkMode(darkMode);
|
||||
|
|
@ -44,6 +39,7 @@
|
|||
}
|
||||
|
||||
return {
|
||||
getClass,
|
||||
isDark,
|
||||
prefixCls,
|
||||
toggleDarkMode,
|
||||
|
|
@ -53,7 +49,7 @@
|
|||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@prefix-cls: ~'@{namespace}-dark-mode-toggle';
|
||||
@prefix-cls: ~'@{namespace}-dark-switch';
|
||||
|
||||
html[data-theme='dark'] {
|
||||
.@{prefix-cls} {
|
||||
|
|
|
|||
|
|
@ -13,39 +13,44 @@
|
|||
>
|
||||
<span class="cursor-pointer flex items-center">
|
||||
<Icon icon="ion:language" />
|
||||
<span v-if="showText" class="ml-1">{{ getLangText }}</span>
|
||||
<span v-if="showText" class="ml-1">{{ getLocaleText }}</span>
|
||||
</span>
|
||||
</Dropdown>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { LocaleType } from '/#/config';
|
||||
import type { DropMenu } from '/@/components/Dropdown';
|
||||
|
||||
import { defineComponent, ref, watchEffect, unref, computed } from 'vue';
|
||||
import { Dropdown } from '/@/components/Dropdown';
|
||||
import Icon from '/@/components/Icon';
|
||||
|
||||
import { Icon } from '/@/components/Icon';
|
||||
import { useLocale } from '/@/locales/useLocale';
|
||||
import { localeList } from '/@/settings/localeSetting';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
|
||||
const props = {
|
||||
/**
|
||||
* Whether to display text
|
||||
*/
|
||||
showText: { type: Boolean, default: true },
|
||||
/**
|
||||
* Whether to refresh the interface when changing
|
||||
*/
|
||||
reload: { type: Boolean },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AppLocalPicker',
|
||||
components: { Dropdown, Icon },
|
||||
props: {
|
||||
// Whether to display text
|
||||
showText: propTypes.bool.def(true),
|
||||
// Whether to refresh the interface when changing
|
||||
reload: propTypes.bool,
|
||||
},
|
||||
props,
|
||||
setup(props) {
|
||||
const selectedKeys = ref<string[]>([]);
|
||||
|
||||
const { changeLocale, getLocale } = useLocale();
|
||||
|
||||
const getLangText = computed(() => {
|
||||
const getLocaleText = computed(() => {
|
||||
const key = selectedKeys.value[0];
|
||||
if (!key) return '';
|
||||
if (!key) {
|
||||
return '';
|
||||
}
|
||||
return localeList.find((item) => item.event === key)?.text;
|
||||
});
|
||||
|
||||
|
|
@ -60,11 +65,13 @@
|
|||
}
|
||||
|
||||
function handleMenuEvent(menu: DropMenu) {
|
||||
if (unref(getLocale) === menu.event) return;
|
||||
if (unref(getLocale) === menu.event) {
|
||||
return;
|
||||
}
|
||||
toggleLocale(menu.event as string);
|
||||
}
|
||||
|
||||
return { localeList, handleMenuEvent, selectedKeys, getLangText };
|
||||
return { localeList, handleMenuEvent, selectedKeys, getLocaleText };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -3,63 +3,69 @@
|
|||
* @Description: logo component
|
||||
-->
|
||||
<template>
|
||||
<div
|
||||
class="anticon"
|
||||
:class="[prefixCls, theme, { 'collapsed-show-title': getCollapsedShowTitle }]"
|
||||
@click="handleGoHome"
|
||||
>
|
||||
<div class="anticon" :class="getAppLogoClass" @click="goHome">
|
||||
<img src="../../../assets/images/logo.png" />
|
||||
<div
|
||||
class="ml-2 truncate md:opacity-100"
|
||||
:class="[
|
||||
`${prefixCls}__title`,
|
||||
{
|
||||
'xs:opacity-0': !alwaysShowTitle,
|
||||
},
|
||||
]"
|
||||
v-show="showTitle"
|
||||
>
|
||||
<div class="ml-2 truncate md:opacity-100" :class="getTitleClass" v-show="showTitle">
|
||||
{{ title }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
import { defineComponent, computed, unref } from 'vue';
|
||||
import { useGlobSetting } from '/@/hooks/setting';
|
||||
import { useGo } from '/@/hooks/web/usePage';
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
import { PageEnum } from '/@/enums/pageEnum';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
|
||||
const props = {
|
||||
/**
|
||||
* The theme of the current parent component
|
||||
*/
|
||||
theme: { type: String, validator: (v) => ['light', 'dark'].includes(v) },
|
||||
/**
|
||||
* Whether to show title
|
||||
*/
|
||||
showTitle: { type: Boolean, default: true },
|
||||
/**
|
||||
* The title is also displayed when the menu is collapsed
|
||||
*/
|
||||
alwaysShowTitle: { type: Boolean },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AppLogo',
|
||||
props: {
|
||||
/**
|
||||
* The theme of the current parent component
|
||||
*/
|
||||
theme: propTypes.oneOf(['light', 'dark']),
|
||||
// Whether to show title
|
||||
showTitle: propTypes.bool.def(true),
|
||||
alwaysShowTitle: propTypes.bool.def(false),
|
||||
},
|
||||
setup() {
|
||||
props: props,
|
||||
setup(props) {
|
||||
const { prefixCls } = useDesign('app-logo');
|
||||
const { getCollapsedShowTitle } = useMenuSetting();
|
||||
const { title } = useGlobSetting();
|
||||
const go = useGo();
|
||||
|
||||
function handleGoHome(): void {
|
||||
const getAppLogoClass = computed(() => [
|
||||
prefixCls,
|
||||
props.theme,
|
||||
{ 'collapsed-show-title': unref(getCollapsedShowTitle) },
|
||||
]);
|
||||
|
||||
const getTitleClass = computed(() => [
|
||||
`${prefixCls}__title`,
|
||||
{
|
||||
'xs:opacity-0': !props.alwaysShowTitle,
|
||||
},
|
||||
]);
|
||||
|
||||
function goHome() {
|
||||
go(PageEnum.BASE_HOME);
|
||||
}
|
||||
|
||||
return {
|
||||
handleGoHome,
|
||||
getAppLogoClass,
|
||||
getTitleClass,
|
||||
getCollapsedShowTitle,
|
||||
goHome,
|
||||
title,
|
||||
prefixCls,
|
||||
getCollapsedShowTitle,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,26 +1,29 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent, toRefs, ref, unref } from 'vue';
|
||||
|
||||
import { createAppProviderContext } from './useAppContext';
|
||||
|
||||
import { prefixCls } from '/@/settings/designSetting';
|
||||
import { createBreakpointListen } from '/@/hooks/event/useBreakpoint';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { prefixCls } from '/@/settings/designSetting';
|
||||
import { useAppStore } from '/@/store/modules/app';
|
||||
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
|
||||
|
||||
const props = {
|
||||
/**
|
||||
* class style prefix
|
||||
*/
|
||||
prefixCls: { type: String, default: prefixCls },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AppProvider',
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
prefixCls: propTypes.string.def(prefixCls),
|
||||
},
|
||||
props,
|
||||
setup(props, { slots }) {
|
||||
const isMobile = ref(false);
|
||||
const isSetState = ref(false);
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
// Monitor screen breakpoint information changes
|
||||
createBreakpointListen(({ screenMap, sizeEnum, width }) => {
|
||||
const lgWidth = screenMap.get(sizeEnum.LG);
|
||||
if (lgWidth) {
|
||||
|
|
@ -30,8 +33,13 @@
|
|||
});
|
||||
|
||||
const { prefixCls } = toRefs(props);
|
||||
|
||||
// Inject variables into the global
|
||||
createAppProviderContext({ prefixCls, isMobile });
|
||||
|
||||
/**
|
||||
* Used to maintain the state before the window changes
|
||||
*/
|
||||
function handleRestoreState() {
|
||||
if (unref(isMobile)) {
|
||||
if (!unref(isSetState)) {
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@
|
|||
import { SearchOutlined } from '@ant-design/icons-vue';
|
||||
import AppSearchModal from './AppSearchModal.vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AppSearch',
|
||||
setup() {
|
||||
const showModal = ref(false);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
function changeModal(show: boolean) {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<div :class="`${prefixCls}`">
|
||||
<AppSearchKeyItem :class="`${prefixCls}__item`" icon="ant-design:enter-outlined" />
|
||||
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="ant-design:enter-outlined" />
|
||||
<span>{{ t('component.app.toSearch') }}</span>
|
||||
<AppSearchKeyItem :class="`${prefixCls}__item`" icon="ion:arrow-up-outline" />
|
||||
<AppSearchKeyItem :class="`${prefixCls}__item`" icon="ion:arrow-down-outline" />
|
||||
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="ion:arrow-up-outline" />
|
||||
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="ion:arrow-down-outline" />
|
||||
<span>{{ t('component.app.toNavigate') }}</span>
|
||||
<AppSearchKeyItem :class="`${prefixCls}__item`" icon="mdi:keyboard-esc" />
|
||||
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="mdi:keyboard-esc" />
|
||||
<span>{{ t('common.closeText') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -21,10 +21,7 @@
|
|||
setup() {
|
||||
const { prefixCls } = useDesign('app-search-footer');
|
||||
const { t } = useI18n();
|
||||
return {
|
||||
prefixCls,
|
||||
t,
|
||||
};
|
||||
return { prefixCls, t };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
@ -44,7 +41,7 @@
|
|||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
&__item {
|
||||
&-item {
|
||||
display: flex;
|
||||
width: 20px;
|
||||
height: 18px;
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@
|
|||
import { Icon } from '/@/components/Icon';
|
||||
export default defineComponent({
|
||||
components: { Icon },
|
||||
props: {
|
||||
icon: String,
|
||||
},
|
||||
props: { icon: String },
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -12,10 +12,9 @@
|
|||
@change="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<!-- <Icon icon="ion:search"/> -->
|
||||
<SearchOutlined />
|
||||
</template>
|
||||
</Input>
|
||||
</a-input>
|
||||
<span :class="`${prefixCls}-cancel`" @click="handleClose">
|
||||
{{ t('common.cancelText') }}
|
||||
</span>
|
||||
|
|
@ -59,39 +58,36 @@
|
|||
</template>
|
||||
<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';
|
||||
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { useRefs } from '/@/hooks/core/useRefs';
|
||||
import { useMenuSearch } from './useMenuSearch';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useAppInject } from '/@/hooks/web/useAppInject';
|
||||
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
const props = {
|
||||
visible: { type: Boolean },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AppSearchModal',
|
||||
components: { Icon, SearchOutlined, AppSearchFooter, Input },
|
||||
components: { Icon, SearchOutlined, AppSearchFooter },
|
||||
directives: {
|
||||
clickOutside,
|
||||
},
|
||||
props: {
|
||||
visible: propTypes.bool,
|
||||
},
|
||||
props,
|
||||
emits: ['close'],
|
||||
setup(props, { emit }) {
|
||||
const scrollWrap = ref<ElRef>(null);
|
||||
const { prefixCls } = useDesign('app-search-modal');
|
||||
const inputRef = ref<Nullable<HTMLElement>>(null);
|
||||
|
||||
const { t } = useI18n();
|
||||
const { prefixCls } = useDesign('app-search-modal');
|
||||
const [refs, setRefs] = useRefs();
|
||||
const { getIsMobile } = useAppInject();
|
||||
const inputRef = ref<Nullable<HTMLElement>>(null);
|
||||
|
||||
const { handleSearch, searchResult, keyword, activeIndex, handleEnter, handleMouseenter } =
|
||||
useMenuSearch(refs, scrollWrap, emit);
|
||||
|
|
@ -109,8 +105,8 @@
|
|||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(v: boolean) => {
|
||||
v &&
|
||||
(visible: boolean) => {
|
||||
visible &&
|
||||
nextTick(() => {
|
||||
unref(inputRef)?.focus();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,12 +1,8 @@
|
|||
import type { Menu } from '/@/router/types';
|
||||
|
||||
import { ref, onBeforeMount, unref, Ref, nextTick } from 'vue';
|
||||
|
||||
import { getMenus } from '/@/router/menus';
|
||||
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { filter, forEach } from '/@/utils/helper/treeHelper';
|
||||
|
||||
import { useGo } from '/@/hooks/web/usePage';
|
||||
import { useScrollTo } from '/@/hooks/event/useScrollTo';
|
||||
import { onKeyStroke, useDebounceFn } from '@vueuse/core';
|
||||
|
|
@ -67,7 +63,6 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
|
|||
|
||||
function handlerSearchResult(filterMenu: Menu[], reg: RegExp, parent?: Menu) {
|
||||
const ret: SearchResult[] = [];
|
||||
|
||||
filterMenu.forEach((item) => {
|
||||
const { name, path, icon, children } = item;
|
||||
if (reg.test(name) && !children?.length) {
|
||||
|
|
@ -84,11 +79,13 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
|
|||
return ret;
|
||||
}
|
||||
|
||||
// Activate when the mouse moves to a certain line
|
||||
function handleMouseenter(e: any) {
|
||||
const index = e.target.dataset.index;
|
||||
activeIndex.value = Number(index);
|
||||
}
|
||||
|
||||
// Arrow key up
|
||||
function handleUp() {
|
||||
if (!searchResult.value.length) return;
|
||||
activeIndex.value--;
|
||||
|
|
@ -98,6 +95,7 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
|
|||
handleScroll();
|
||||
}
|
||||
|
||||
// Arrow key down
|
||||
function handleDown() {
|
||||
if (!searchResult.value.length) return;
|
||||
activeIndex.value++;
|
||||
|
|
@ -107,15 +105,23 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
|
|||
handleScroll();
|
||||
}
|
||||
|
||||
// When the keyboard up and down keys move to an invisible place
|
||||
// the scroll bar needs to scroll automatically
|
||||
function handleScroll() {
|
||||
const refList = unref(refs);
|
||||
if (!refList || !Array.isArray(refList) || refList.length === 0 || !unref(scrollWrap)) return;
|
||||
if (!refList || !Array.isArray(refList) || refList.length === 0 || !unref(scrollWrap)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const index = unref(activeIndex);
|
||||
const currentRef = refList[index];
|
||||
if (!currentRef) return;
|
||||
if (!currentRef) {
|
||||
return;
|
||||
}
|
||||
const wrapEl = unref(scrollWrap);
|
||||
if (!wrapEl) return;
|
||||
if (!wrapEl) {
|
||||
return;
|
||||
}
|
||||
const scrollHeight = currentRef.offsetTop + currentRef.offsetHeight;
|
||||
const wrapHeight = wrapEl.offsetHeight;
|
||||
const { start } = useScrollTo({
|
||||
|
|
@ -126,8 +132,11 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
|
|||
start();
|
||||
}
|
||||
|
||||
// enter keyboard event
|
||||
async function handleEnter() {
|
||||
if (!searchResult.value.length) return;
|
||||
if (!searchResult.value.length) {
|
||||
return;
|
||||
}
|
||||
const result = unref(searchResult);
|
||||
const index = unref(activeIndex);
|
||||
if (result.length === 0 || index < 0) {
|
||||
|
|
@ -139,14 +148,18 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref<ElRef>,
|
|||
go(to.path);
|
||||
}
|
||||
|
||||
// close search modal
|
||||
function handleClose() {
|
||||
searchResult.value = [];
|
||||
emit('close');
|
||||
}
|
||||
|
||||
// enter search
|
||||
onKeyStroke('Enter', handleEnter);
|
||||
// Monitor keyboard arrow keys
|
||||
onKeyStroke('ArrowUp', handleUp);
|
||||
onKeyStroke('ArrowDown', handleDown);
|
||||
// esc close
|
||||
onKeyStroke('Escape', handleClose);
|
||||
|
||||
return { handleSearch, searchResult, keyword, activeIndex, handleMouseenter, handleEnter };
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { createContext, useContext } from '/@/hooks/core/useContext';
|
|||
|
||||
export interface AppProviderContextProps {
|
||||
prefixCls: Ref<string>;
|
||||
|
||||
isMobile: Ref<boolean>;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import Authority from './src/Authority.vue';
|
||||
import { withInstall } from '/@/utils';
|
||||
import authority from './src/Authority.vue';
|
||||
|
||||
export { Authority };
|
||||
export const Authority = withInstall(authority);
|
||||
|
|
|
|||
|
|
@ -4,11 +4,8 @@
|
|||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
import { RoleEnum } from '/@/enums/roleEnum';
|
||||
|
||||
import { usePermission } from '/@/hooks/web/usePermission';
|
||||
|
||||
import { getSlot } from '/@/utils/helper/tsxHelper';
|
||||
|
||||
export default defineComponent({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import BasicArrow from './src/BasicArrow.vue';
|
||||
import BasicTitle from './src/BasicTitle.vue';
|
||||
import BasicHelp from './src/BasicHelp.vue';
|
||||
import { withInstall } from '/@/utils';
|
||||
import basicArrow from './src/BasicArrow.vue';
|
||||
import basicTitle from './src/BasicTitle.vue';
|
||||
import basicHelp from './src/BasicHelp.vue';
|
||||
|
||||
export { BasicArrow, BasicTitle, BasicHelp };
|
||||
export const BasicArrow = withInstall(basicArrow);
|
||||
export const BasicTitle = withInstall(basicTitle);
|
||||
export const BasicHelp = withInstall(basicHelp);
|
||||
|
|
|
|||
|
|
@ -9,41 +9,50 @@
|
|||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from 'vue';
|
||||
|
||||
import { Icon } from '/@/components/Icon';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
|
||||
import { Icon } from '/@/components/Icon';
|
||||
const props = {
|
||||
/**
|
||||
* Arrow expand state
|
||||
*/
|
||||
expand: { type: Boolean },
|
||||
/**
|
||||
* Arrow up by default
|
||||
*/
|
||||
up: { type: Boolean },
|
||||
/**
|
||||
* Arrow down by default
|
||||
*/
|
||||
down: { type: Boolean },
|
||||
/**
|
||||
* Cancel padding/margin for inline
|
||||
*/
|
||||
inset: { type: Boolean },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BasicArrow',
|
||||
components: { Icon },
|
||||
props: {
|
||||
expand: propTypes.bool,
|
||||
top: propTypes.bool,
|
||||
bottom: propTypes.bool,
|
||||
inset: propTypes.bool,
|
||||
},
|
||||
props,
|
||||
setup(props) {
|
||||
const { prefixCls } = useDesign('basic-arrow');
|
||||
|
||||
// get component class
|
||||
const getClass = computed(() => {
|
||||
const { expand, top, bottom, inset } = props;
|
||||
const { expand, up, down, inset } = props;
|
||||
return [
|
||||
prefixCls,
|
||||
{
|
||||
[`${prefixCls}--active`]: expand,
|
||||
top,
|
||||
up,
|
||||
inset,
|
||||
bottom,
|
||||
down,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
return {
|
||||
getClass,
|
||||
};
|
||||
return { getClass };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
@ -65,19 +74,19 @@
|
|||
line-height: 0px;
|
||||
}
|
||||
|
||||
&.top {
|
||||
&.up {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
&.bottom {
|
||||
&.down {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
&.top.@{prefix-cls}--active {
|
||||
&.up.@{prefix-cls}--active {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
&.bottom.@{prefix-cls}--active {
|
||||
&.down.@{prefix-cls}--active {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,104 +1,90 @@
|
|||
<script lang="tsx">
|
||||
import type { CSSProperties, PropType } from 'vue';
|
||||
import { defineComponent, computed, unref } from 'vue';
|
||||
|
||||
import { Tooltip } from 'ant-design-vue';
|
||||
import { InfoCircleOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
import { getPopupContainer } from '/@/utils';
|
||||
import { isString, isArray } from '/@/utils/is';
|
||||
import { getSlot } from '/@/utils/helper/tsxHelper';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
const props = {
|
||||
/**
|
||||
* Help text max-width
|
||||
* @default: 600px
|
||||
*/
|
||||
maxWidth: { type: String, default: '600px' },
|
||||
/**
|
||||
* Whether to display the serial number
|
||||
* @default: false
|
||||
*/
|
||||
showIndex: { type: Boolean },
|
||||
/**
|
||||
* Help text font color
|
||||
* @default: #ffffff
|
||||
*/
|
||||
color: { type: String, default: '#ffffff' },
|
||||
/**
|
||||
* Help text font size
|
||||
* @default: 14px
|
||||
*/
|
||||
fontSize: { type: String, default: '14px' },
|
||||
/**
|
||||
* Help text list
|
||||
*/
|
||||
placement: { type: String, default: 'right' },
|
||||
/**
|
||||
* Help text list
|
||||
*/
|
||||
text: { type: [Array, String] as PropType<string[] | string> },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BasicHelp',
|
||||
components: { Tooltip },
|
||||
props: {
|
||||
// max-width
|
||||
maxWidth: propTypes.string.def('600px'),
|
||||
// Whether to display the serial number
|
||||
showIndex: propTypes.bool,
|
||||
// color
|
||||
color: propTypes.string.def('#ffffff'),
|
||||
fontSize: propTypes.string.def('14px'),
|
||||
placement: propTypes.string.def('right'),
|
||||
absolute: propTypes.bool,
|
||||
// Text list
|
||||
text: {
|
||||
type: [Array, String] as PropType<string[] | string>,
|
||||
},
|
||||
// 定位
|
||||
position: {
|
||||
type: [Object] as PropType<any>,
|
||||
default: () => ({
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
}),
|
||||
},
|
||||
},
|
||||
props,
|
||||
setup(props, { slots }) {
|
||||
const { prefixCls } = useDesign('basic-help');
|
||||
|
||||
const getOverlayStyle = computed(
|
||||
(): CSSProperties => {
|
||||
return {
|
||||
maxWidth: props.maxWidth,
|
||||
};
|
||||
}
|
||||
const getTooltipStyle = computed(
|
||||
(): CSSProperties => ({ color: props.color, fontSize: props.fontSize })
|
||||
);
|
||||
|
||||
const getWrapStyle = computed(
|
||||
(): CSSProperties => {
|
||||
return {
|
||||
color: props.color,
|
||||
fontSize: props.fontSize,
|
||||
};
|
||||
}
|
||||
);
|
||||
const getOverlayStyle = computed((): CSSProperties => ({ maxWidth: props.maxWidth }));
|
||||
|
||||
const getMainStyleRef = computed(() => {
|
||||
return props.absolute ? props.position : {};
|
||||
});
|
||||
function renderTitle() {
|
||||
const textList = props.text;
|
||||
|
||||
const renderTitle = () => {
|
||||
const list = props.text;
|
||||
|
||||
if (isString(list)) {
|
||||
return <p>{list}</p>;
|
||||
if (isString(textList)) {
|
||||
return <p>{textList}</p>;
|
||||
}
|
||||
|
||||
if (isArray(list)) {
|
||||
return list.map((item, index) => {
|
||||
if (isArray(textList)) {
|
||||
return textList.map((text, index) => {
|
||||
return (
|
||||
<p key={item}>
|
||||
<p key={text}>
|
||||
<>
|
||||
{props.showIndex ? `${index + 1}. ` : ''}
|
||||
{item}
|
||||
{text}
|
||||
</>
|
||||
</p>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<Tooltip
|
||||
title={<div style={unref(getWrapStyle)}>{renderTitle()}</div>}
|
||||
overlayClassName={`${prefixCls}__wrap`}
|
||||
title={<div style={unref(getTooltipStyle)}>{renderTitle()}</div>}
|
||||
autoAdjustOverflow={true}
|
||||
overlayStyle={unref(getOverlayStyle)}
|
||||
placement={props.placement as 'left'}
|
||||
placement={props.placement as 'right'}
|
||||
getPopupContainer={() => getPopupContainer()}
|
||||
>
|
||||
<span class={prefixCls} style={unref(getMainStyleRef)}>
|
||||
{getSlot(slots) || <InfoCircleOutlined />}
|
||||
</span>
|
||||
<span class={prefixCls}>{getSlot(slots) || <InfoCircleOutlined />}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,30 +1,40 @@
|
|||
<template>
|
||||
<span :class="getClass">
|
||||
<slot></slot>
|
||||
<BasicHelp :class="`${prefixCls}__help`" v-if="helpMessage" :text="helpMessage" />
|
||||
<BasicHelp :class="`${prefixCls}-help`" v-if="helpMessage" :text="helpMessage" />
|
||||
</span>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
import { defineComponent, computed } from 'vue';
|
||||
import BasicHelp from './BasicHelp.vue';
|
||||
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
const props = {
|
||||
/**
|
||||
* Help text list or string
|
||||
* @default: ''
|
||||
*/
|
||||
helpMessage: {
|
||||
type: [String, Array] as PropType<string | string[]>,
|
||||
default: '',
|
||||
},
|
||||
/**
|
||||
* Whether the color block on the left side of the title
|
||||
* @default: false
|
||||
*/
|
||||
span: { type: Boolean },
|
||||
/**
|
||||
* Whether to default the text, that is, not bold
|
||||
* @default: false
|
||||
*/
|
||||
normal: { type: Boolean },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BasicTitle',
|
||||
components: { BasicHelp },
|
||||
props: {
|
||||
helpMessage: {
|
||||
type: [String, Array] as PropType<string | string[]>,
|
||||
default: '',
|
||||
},
|
||||
span: propTypes.bool,
|
||||
normal: propTypes.bool.def(false),
|
||||
},
|
||||
props,
|
||||
setup(props, { slots }) {
|
||||
const { prefixCls } = useDesign('basic-title');
|
||||
|
||||
|
|
@ -33,6 +43,7 @@
|
|||
{ [`${prefixCls}-show-span`]: props.span && slots.default },
|
||||
{ [`${prefixCls}-normal`]: props.normal },
|
||||
]);
|
||||
|
||||
return { prefixCls, getClass };
|
||||
},
|
||||
});
|
||||
|
|
@ -67,7 +78,7 @@
|
|||
content: '';
|
||||
}
|
||||
|
||||
&__help {
|
||||
&-help {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import Button from './src/BasicButton.vue';
|
||||
import PopConfirmButton from './src/PopConfirmButton.vue';
|
||||
import { withInstall } from '/@/utils';
|
||||
import button from './src/BasicButton.vue';
|
||||
import popConfirmButton from './src/PopConfirmButton.vue';
|
||||
|
||||
export { Button, PopConfirmButton };
|
||||
export const Button = withInstall(button);
|
||||
export const PopConfirmButton = withInstall(popConfirmButton);
|
||||
|
|
|
|||
|
|
@ -13,11 +13,21 @@
|
|||
import { Icon } from '/@/components/Icon';
|
||||
|
||||
const props = {
|
||||
color: { type: String, validate: (v) => ['error', 'warning', 'success', ''].includes(v) },
|
||||
color: { type: String, validator: (v) => ['error', 'warning', 'success', ''].includes(v) },
|
||||
loading: { type: Boolean },
|
||||
disabled: { type: Boolean },
|
||||
/**
|
||||
* Text before icon.
|
||||
*/
|
||||
preIcon: { type: String },
|
||||
/**
|
||||
* Text after icon.
|
||||
*/
|
||||
postIcon: { type: String },
|
||||
/**
|
||||
* preIcon and postIcon icon size.
|
||||
* @default: 14
|
||||
*/
|
||||
iconSize: { type: Number, default: 14 },
|
||||
onClick: { type: Function as PropType<(...args) => any>, default: null },
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent, h, unref, computed } from 'vue';
|
||||
import { Popconfirm } from 'ant-design-vue';
|
||||
import BasicButton from './BasicButton.vue';
|
||||
import { Popconfirm } from 'ant-design-vue';
|
||||
import { extendSlots } from '/@/utils/helper/tsxHelper';
|
||||
import { omit } from 'lodash-es';
|
||||
import { useAttrs } from '/@/hooks/core/useAttrs';
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import ClickOutSide from './src/ClickOutSide.vue';
|
||||
import { withInstall } from '/@/utils';
|
||||
import clickOutSide from './src/ClickOutSide.vue';
|
||||
|
||||
export { ClickOutSide };
|
||||
export const ClickOutSide = withInstall(clickOutSide);
|
||||
|
|
|
|||
|
|
@ -1,15 +1,6 @@
|
|||
import type { App } from 'vue';
|
||||
import { withInstall } from '/@/utils';
|
||||
import codeEditor from './src/CodeEditor.vue';
|
||||
import jsonPreview from './src/json-preview/JsonPreview.vue';
|
||||
|
||||
export const CodeEditor = Object.assign(codeEditor, {
|
||||
install(app: App) {
|
||||
app.component(codeEditor.name, codeEditor);
|
||||
},
|
||||
});
|
||||
|
||||
export const JsonPreview = Object.assign(jsonPreview, {
|
||||
install(app: App) {
|
||||
app.component(jsonPreview.name, jsonPreview);
|
||||
},
|
||||
});
|
||||
export const CodeEditor = withInstall(codeEditor);
|
||||
export const JsonPreview = withInstall(jsonPreview);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
<template>
|
||||
<div class="h-full">
|
||||
<CodeMirrorEditor :value="getValue" @change="handleValueChange" :mode="mode" :readonly="readonly" />
|
||||
<CodeMirrorEditor
|
||||
:value="getValue"
|
||||
@change="handleValueChange"
|
||||
:mode="mode"
|
||||
:readonly="readonly"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
|
|
@ -13,43 +18,34 @@
|
|||
html: 'htmlmixed',
|
||||
js: 'javascript',
|
||||
};
|
||||
|
||||
const props = {
|
||||
value: { type: [Object, String] as PropType<Record<string, any> | string> },
|
||||
mode: { type: String, default: MODE.JSON },
|
||||
readonly: { type: Boolean },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CodeEditor',
|
||||
components: { CodeMirrorEditor },
|
||||
props: {
|
||||
value: {
|
||||
type: [Object, String],
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: MODE.JSON,
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
props,
|
||||
emits: ['change'],
|
||||
setup(props, { emit }) {
|
||||
const getValue = computed(() => {
|
||||
const { value, mode } = props;
|
||||
|
||||
if (mode === MODE.JSON) {
|
||||
return isString(value)
|
||||
? JSON.stringify(JSON.parse(value), null, 2)
|
||||
: JSON.stringify(value, null, 2);
|
||||
if (mode !== MODE.JSON) {
|
||||
return value as string;
|
||||
}
|
||||
return value;
|
||||
return isString(value)
|
||||
? JSON.stringify(JSON.parse(value), null, 2)
|
||||
: JSON.stringify(value, null, 2);
|
||||
});
|
||||
|
||||
function handleValueChange(v) {
|
||||
emit('change', v);
|
||||
}
|
||||
|
||||
return {
|
||||
handleValueChange,
|
||||
getValue,
|
||||
};
|
||||
return { handleValueChange, getValue };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -15,31 +15,25 @@
|
|||
} from 'vue';
|
||||
import { useDebounceFn } from '@vueuse/core';
|
||||
import { useAppStore } from '/@/store/modules/app';
|
||||
|
||||
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
|
||||
import CodeMirror from 'codemirror';
|
||||
// css
|
||||
import './codemirror.css';
|
||||
import 'codemirror/theme/idea.css';
|
||||
import 'codemirror/theme/material-palenight.css';
|
||||
|
||||
// modes
|
||||
import 'codemirror/mode/javascript/javascript';
|
||||
import 'codemirror/mode/css/css';
|
||||
import 'codemirror/mode/htmlmixed/htmlmixed';
|
||||
|
||||
const props = {
|
||||
mode: { type: String, default: 'application/json' },
|
||||
value: { type: String, default: '' },
|
||||
readonly: { type: Boolean, default: false },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'application/json',
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
props,
|
||||
emits: ['change'],
|
||||
setup(props, { emit }) {
|
||||
const el = ref();
|
||||
|
|
@ -50,11 +44,11 @@
|
|||
|
||||
watch(
|
||||
() => props.value,
|
||||
async (v) => {
|
||||
async (value) => {
|
||||
await nextTick();
|
||||
const oldValue = editor?.getValue();
|
||||
if (v !== oldValue) {
|
||||
editor?.setValue(v ? v : '');
|
||||
if (value !== oldValue) {
|
||||
editor?.setValue(value ? value : '');
|
||||
}
|
||||
},
|
||||
{ flush: 'post' }
|
||||
|
|
@ -113,13 +107,13 @@
|
|||
onMounted(async () => {
|
||||
await nextTick();
|
||||
init();
|
||||
window.addEventListener('resize', debounceRefresh);
|
||||
useWindowSizeFn(debounceRefresh);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', debounceRefresh);
|
||||
editor = null;
|
||||
});
|
||||
|
||||
return { el };
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,20 +12,21 @@
|
|||
--qualifier: #ff6032;
|
||||
--important: var(--string);
|
||||
|
||||
position: relative;
|
||||
height: auto;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
font-family: var(--font-code);
|
||||
background: white;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
/* PADDING */
|
||||
|
||||
.CodeMirror-lines {
|
||||
min-height: 1px; /* prevents collapsing before first draw */
|
||||
padding: 4px 0; /* Vertical padding around content */
|
||||
}
|
||||
|
||||
.CodeMirror pre {
|
||||
padding: 0 4px; /* Horizontal padding of content */
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.CodeMirror-scrollbar-filler,
|
||||
|
|
@ -36,6 +37,11 @@
|
|||
/* GUTTER */
|
||||
|
||||
.CodeMirror-gutters {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 3;
|
||||
min-height: 100%;
|
||||
white-space: nowrap;
|
||||
background-color: transparent;
|
||||
border-right: 1px solid #ddd;
|
||||
|
|
@ -96,7 +102,9 @@
|
|||
/* CURSOR */
|
||||
|
||||
.CodeMirror-cursor {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
pointer-events: none;
|
||||
border-right: none;
|
||||
border-left: 1px solid black;
|
||||
}
|
||||
|
|
@ -132,37 +140,19 @@
|
|||
animation: blink 1.06s steps(1) infinite;
|
||||
}
|
||||
@-moz-keyframes blink {
|
||||
0% {
|
||||
}
|
||||
|
||||
50% {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
100% {
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes blink {
|
||||
0% {
|
||||
}
|
||||
|
||||
50% {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
100% {
|
||||
}
|
||||
}
|
||||
@keyframes blink {
|
||||
0% {
|
||||
}
|
||||
|
||||
50% {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
100% {
|
||||
}
|
||||
}
|
||||
|
||||
.cm-tab {
|
||||
|
|
@ -316,12 +306,6 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {
|
|||
/* The rest of this file contains styles related to the mechanics of
|
||||
the editor. You probably shouldn't touch them. */
|
||||
|
||||
.CodeMirror {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.CodeMirror-scroll {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
|
@ -378,14 +362,6 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {
|
|||
left: 0;
|
||||
}
|
||||
|
||||
.CodeMirror-gutters {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 3;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.CodeMirror-gutter {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
|
|
@ -422,14 +398,10 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {
|
|||
background-color: transparent;
|
||||
}
|
||||
|
||||
.CodeMirror-lines {
|
||||
min-height: 1px; /* prevents collapsing before first draw */
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.CodeMirror pre {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 0 4px; /* Horizontal padding of content */
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
font-family: inherit;
|
||||
|
|
@ -497,11 +469,6 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {
|
|||
visibility: hidden;
|
||||
}
|
||||
|
||||
.CodeMirror-cursor {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.CodeMirror-measure pre {
|
||||
position: static;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,11 +8,7 @@
|
|||
import { defineComponent } from 'vue';
|
||||
export default defineComponent({
|
||||
name: 'JsonPreview',
|
||||
components: {
|
||||
VueJsonPretty,
|
||||
},
|
||||
props: {
|
||||
data: Object,
|
||||
},
|
||||
components: { VueJsonPretty },
|
||||
props: { data: Object },
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import CollapseContainer from './src/collapse/CollapseContainer.vue';
|
||||
import ScrollContainer from './src/ScrollContainer.vue';
|
||||
import LazyContainer from './src/LazyContainer.vue';
|
||||
import { withInstall } from '/@/utils';
|
||||
import collapseContainer from './src/collapse/CollapseContainer.vue';
|
||||
import scrollContainer from './src/ScrollContainer.vue';
|
||||
import lazyContainer from './src/LazyContainer.vue';
|
||||
|
||||
export { CollapseContainer, ScrollContainer, LazyContainer };
|
||||
export * from './src/types';
|
||||
export const CollapseContainer = withInstall(collapseContainer);
|
||||
export const ScrollContainer = withInstall(scrollContainer);
|
||||
export const LazyContainer = withInstall(lazyContainer);
|
||||
|
||||
export * from './src/typing';
|
||||
|
|
|
|||
|
|
@ -18,13 +18,10 @@
|
|||
</template>
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
import { defineComponent, reactive, onMounted, ref, toRef, toRefs } from 'vue';
|
||||
|
||||
import { Skeleton } from 'ant-design-vue';
|
||||
import { useTimeoutFn } from '/@/hooks/core/useTimeout';
|
||||
import { useIntersectionObserver } from '/@/hooks/event/useIntersectionObserver';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
|
||||
interface State {
|
||||
isInit: boolean;
|
||||
|
|
@ -32,36 +29,47 @@
|
|||
intersectionObserverInstance: IntersectionObserver | null;
|
||||
}
|
||||
|
||||
const props = {
|
||||
/**
|
||||
* Waiting time, if the time is specified, whether visible or not, it will be automatically loaded after the specified time
|
||||
*/
|
||||
timeout: { type: Number },
|
||||
/**
|
||||
* The viewport where the component is located.
|
||||
* If the component is scrolling in the page container, the viewport is the container
|
||||
*/
|
||||
viewport: {
|
||||
type: (typeof window !== 'undefined' ? window.HTMLElement : Object) as PropType<HTMLElement>,
|
||||
default: () => null,
|
||||
},
|
||||
/**
|
||||
* Preload threshold, css unit
|
||||
*/
|
||||
threshold: { type: String, default: '0px' },
|
||||
/**
|
||||
* The scroll direction of the viewport, vertical represents the vertical direction, horizontal represents the horizontal direction
|
||||
*/
|
||||
direction: {
|
||||
type: String,
|
||||
default: 'vertical',
|
||||
validator: (v) => ['vertical', 'horizontal'].includes(v),
|
||||
},
|
||||
/**
|
||||
* The label name of the outer container that wraps the component
|
||||
*/
|
||||
tag: { type: String, default: 'div' },
|
||||
maxWaitingTime: { type: Number, default: 80 },
|
||||
/**
|
||||
* transition name
|
||||
*/
|
||||
transitionName: { type: String, default: 'lazy-container' },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LazyContainer',
|
||||
components: { Skeleton },
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
// Waiting time, if the time is specified, whether visible or not, it will be automatically loaded after the specified time
|
||||
timeout: propTypes.number,
|
||||
|
||||
// The viewport where the component is located. If the component is scrolling in the page container, the viewport is the container
|
||||
viewport: {
|
||||
type: (typeof window !== 'undefined'
|
||||
? window.HTMLElement
|
||||
: Object) as PropType<HTMLElement>,
|
||||
default: () => null,
|
||||
},
|
||||
|
||||
// Preload threshold, css unit
|
||||
threshold: propTypes.string.def('0px'),
|
||||
|
||||
// The scroll direction of the viewport, vertical represents the vertical direction, horizontal represents the horizontal direction
|
||||
direction: propTypes.oneOf(['vertical', 'horizontal']).def('vertical'),
|
||||
|
||||
// The label name of the outer container that wraps the component
|
||||
tag: propTypes.string.def('div'),
|
||||
|
||||
maxWaitingTime: propTypes.number.def(80),
|
||||
|
||||
// transition name
|
||||
transitionName: propTypes.string.def('lazy-container'),
|
||||
},
|
||||
props,
|
||||
emits: ['init'],
|
||||
setup(props, { emit }) {
|
||||
const elRef = ref();
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent, ref, unref, nextTick } from 'vue';
|
||||
import { Scrollbar, ScrollbarType } from '/@/components/Scrollbar';
|
||||
|
||||
import { useScrollTo } from '/@/hooks/event/useScrollTo';
|
||||
|
||||
export default defineComponent({
|
||||
|
|
@ -16,12 +15,14 @@
|
|||
setup() {
|
||||
const scrollbarRef = ref<Nullable<ScrollbarType>>(null);
|
||||
|
||||
/**
|
||||
* Scroll to the specified position
|
||||
*/
|
||||
function scrollTo(to: number, duration = 500) {
|
||||
const scrollbar = unref(scrollbarRef);
|
||||
if (!scrollbar) {
|
||||
return;
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
const wrap = unref(scrollbar.wrap);
|
||||
if (!wrap) {
|
||||
|
|
@ -44,12 +45,14 @@
|
|||
return scrollbar.wrap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll to the bottom
|
||||
*/
|
||||
function scrollBottom() {
|
||||
const scrollbar = unref(scrollbarRef);
|
||||
if (!scrollbar) {
|
||||
return;
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
const wrap = unref(scrollbar.wrap);
|
||||
if (!wrap) {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
<template>
|
||||
<div :class="prefixCls">
|
||||
<CollapseHeader
|
||||
v-bind="getBindValues"
|
||||
:prefixCls="prefixCls"
|
||||
:show="show"
|
||||
@expand="handleExpand"
|
||||
>
|
||||
<CollapseHeader v-bind="$props" :prefixCls="prefixCls" :show="show" @expand="handleExpand">
|
||||
<template #title>
|
||||
<slot name="title"></slot>
|
||||
</template>
|
||||
|
|
@ -16,13 +11,12 @@
|
|||
|
||||
<div class="p-2">
|
||||
<CollapseTransition :enable="canExpan">
|
||||
<Skeleton v-if="loading" :active="active" />
|
||||
<Skeleton v-if="loading" :active="loading" />
|
||||
<div :class="`${prefixCls}__body`" v-else v-show="show">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</CollapseTransition>
|
||||
</div>
|
||||
|
||||
<div :class="`${prefixCls}__footer`" v-if="$slots.footer">
|
||||
<slot name="footer"></slot>
|
||||
</div>
|
||||
|
|
@ -30,20 +24,41 @@
|
|||
</template>
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
import { defineComponent, ref, computed } from 'vue';
|
||||
|
||||
import { defineComponent, ref } from 'vue';
|
||||
// component
|
||||
import { Skeleton } from 'ant-design-vue';
|
||||
import { CollapseTransition } from '/@/components/Transition';
|
||||
import CollapseHeader from './CollapseHeader.vue';
|
||||
|
||||
import { triggerWindowResize } from '/@/utils/event';
|
||||
// hook
|
||||
import { useTimeoutFn } from '/@/hooks/core/useTimeout';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
const props = {
|
||||
title: { type: String, default: '' },
|
||||
loading: { type: Boolean },
|
||||
/**
|
||||
* Can it be expanded
|
||||
*/
|
||||
canExpan: { type: Boolean, default: true },
|
||||
/**
|
||||
* Warm reminder on the right side of the title
|
||||
*/
|
||||
helpMessage: {
|
||||
type: [Array, String] as PropType<string[] | string>,
|
||||
default: '',
|
||||
},
|
||||
/**
|
||||
* Whether to trigger window.resize when expanding and contracting,
|
||||
* Can adapt to tables and forms, when the form shrinks, the form triggers resize to adapt to the height
|
||||
*/
|
||||
triggerWindowResize: { type: Boolean },
|
||||
/**
|
||||
* Delayed loading time
|
||||
*/
|
||||
lazyTime: { type: Number, default: 0 },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CollapseContainer',
|
||||
components: {
|
||||
|
|
@ -51,23 +66,7 @@
|
|||
CollapseHeader,
|
||||
CollapseTransition,
|
||||
},
|
||||
props: {
|
||||
title: propTypes.string.def(''),
|
||||
// Can it be expanded
|
||||
canExpan: propTypes.bool.def(true),
|
||||
// Warm reminder on the right side of the title
|
||||
helpMessage: {
|
||||
type: [Array, String] as PropType<string[] | string>,
|
||||
default: '',
|
||||
},
|
||||
// Whether to trigger window.resize when expanding and contracting,
|
||||
// Can adapt to tables and forms, when the form shrinks, the form triggers resize to adapt to the height
|
||||
triggerWindowResize: propTypes.bool,
|
||||
loading: propTypes.bool.def(false),
|
||||
active: propTypes.bool.def(true),
|
||||
// Delayed loading time
|
||||
lazyTime: propTypes.number.def(0),
|
||||
},
|
||||
props,
|
||||
setup(props) {
|
||||
const show = ref(true);
|
||||
|
||||
|
|
@ -84,15 +83,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
const getBindValues = computed((): any => {
|
||||
return props;
|
||||
});
|
||||
|
||||
return {
|
||||
show,
|
||||
handleExpand,
|
||||
prefixCls,
|
||||
getBindValues,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,28 +8,31 @@
|
|||
<slot name="title"></slot>
|
||||
</template>
|
||||
</BasicTitle>
|
||||
|
||||
<div :class="`${prefixCls}__action`">
|
||||
<slot name="action"></slot>
|
||||
<BasicArrow v-if="canExpan" top :expand="show" @click="$emit('expand')" />
|
||||
<BasicArrow v-if="canExpan" up :expand="show" @click="$emit('expand')" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { BasicArrow, BasicTitle } from '/@/components/Basic';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
|
||||
const props = {
|
||||
prefixCls: { type: String },
|
||||
helpMessage: {
|
||||
type: [Array, String] as PropType<string[] | string>,
|
||||
default: '',
|
||||
},
|
||||
title: { type: String },
|
||||
show: { type: Boolean },
|
||||
canExpan: { type: Boolean },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: { BasicArrow, BasicTitle },
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
prefixCls: propTypes.string,
|
||||
helpMessage: propTypes.string,
|
||||
title: propTypes.string,
|
||||
show: propTypes.bool,
|
||||
canExpan: propTypes.bool,
|
||||
},
|
||||
props,
|
||||
emits: ['expand'],
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
export { createContextMenu, destroyContextMenu } from './src/createContextMenu';
|
||||
|
||||
export * from './src/types';
|
||||
export * from './src/typing';
|
||||
|
|
|
|||
|
|
@ -1,117 +0,0 @@
|
|||
import './index.less';
|
||||
|
||||
import type { ContextMenuItem, ItemContentProps } from './types';
|
||||
import type { FunctionalComponent, CSSProperties } from 'vue';
|
||||
|
||||
import { defineComponent, nextTick, onMounted, computed, ref, unref, onUnmounted } from 'vue';
|
||||
|
||||
import Icon from '/@/components/Icon';
|
||||
import { Menu, Divider } from 'ant-design-vue';
|
||||
|
||||
import { contextMenuProps } from './props';
|
||||
|
||||
const prefixCls = 'context-menu';
|
||||
|
||||
const ItemContent: FunctionalComponent<ItemContentProps> = (props) => {
|
||||
const { item } = props;
|
||||
return (
|
||||
<span
|
||||
style="display: inline-block; width: 100%; "
|
||||
class="px-4"
|
||||
onClick={props.handler.bind(null, item)}
|
||||
>
|
||||
{props.showIcon && item.icon && <Icon class="mr-2" icon={item.icon} />}
|
||||
<span>{item.label}</span>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ContextMenu',
|
||||
props: contextMenuProps,
|
||||
setup(props) {
|
||||
const wrapRef = ref<ElRef>(null);
|
||||
const showRef = ref(false);
|
||||
|
||||
const getStyle = computed((): CSSProperties => {
|
||||
const { axis, items, styles, width } = props;
|
||||
const { x, y } = axis || { x: 0, y: 0 };
|
||||
const menuHeight = (items || []).length * 40;
|
||||
const menuWidth = width;
|
||||
const body = document.body;
|
||||
|
||||
const left = body.clientWidth < x + menuWidth ? x - menuWidth : x;
|
||||
const top = body.clientHeight < y + menuHeight ? y - menuHeight : y;
|
||||
return {
|
||||
...styles,
|
||||
width: `${width}px`,
|
||||
left: `${left + 1}px`,
|
||||
top: `${top + 1}px`,
|
||||
};
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => (showRef.value = true));
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
const el = unref(wrapRef);
|
||||
el && document.body.removeChild(el);
|
||||
});
|
||||
|
||||
function handleAction(item: ContextMenuItem, e: MouseEvent) {
|
||||
const { handler, disabled } = item;
|
||||
if (disabled) return;
|
||||
showRef.value = false;
|
||||
|
||||
e?.stopPropagation();
|
||||
e?.preventDefault();
|
||||
handler?.();
|
||||
}
|
||||
|
||||
function renderMenuItem(items: ContextMenuItem[]) {
|
||||
return items.map((item) => {
|
||||
const { disabled, label, children, divider = false } = item;
|
||||
|
||||
const DividerComp = divider ? <Divider key={`d-${label}`} /> : null;
|
||||
if (!children || children.length === 0) {
|
||||
return (
|
||||
<>
|
||||
<Menu.Item disabled={disabled} class={`${prefixCls}__item`} key={label}>
|
||||
<ItemContent showIcon={props.showIcon} item={item} handler={handleAction} />
|
||||
</Menu.Item>
|
||||
{DividerComp}
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (!unref(showRef)) return null;
|
||||
|
||||
return (
|
||||
<Menu.SubMenu key={label} disabled={disabled} popupClassName={`${prefixCls}__popup`}>
|
||||
{{
|
||||
title: () => (
|
||||
<ItemContent showIcon={props.showIcon} item={item} handler={handleAction} />
|
||||
),
|
||||
default: () => renderMenuItem(children),
|
||||
}}
|
||||
</Menu.SubMenu>
|
||||
);
|
||||
});
|
||||
}
|
||||
return () => {
|
||||
const { items } = props;
|
||||
if (!unref(showRef)) return null;
|
||||
return (
|
||||
<Menu
|
||||
inlineIndent={12}
|
||||
mode="vertical"
|
||||
class={prefixCls}
|
||||
ref={wrapRef}
|
||||
style={unref(getStyle)}
|
||||
>
|
||||
{renderMenuItem(items)}
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
<script lang="tsx">
|
||||
import type { ContextMenuItem, ItemContentProps, Axis } from './typing';
|
||||
import type { FunctionalComponent, CSSProperties } from 'vue';
|
||||
import { defineComponent, nextTick, onMounted, computed, ref, unref, onUnmounted } from 'vue';
|
||||
import Icon from '/@/components/Icon';
|
||||
import { Menu, Divider } from 'ant-design-vue';
|
||||
|
||||
const prefixCls = 'context-menu';
|
||||
|
||||
const props = {
|
||||
width: { type: Number, default: 156 },
|
||||
customEvent: { type: Object as PropType<Event>, default: null },
|
||||
styles: { type: Object as PropType<CSSProperties> },
|
||||
showIcon: { type: Boolean, default: true },
|
||||
axis: {
|
||||
// The position of the right mouse button click
|
||||
type: Object as PropType<Axis>,
|
||||
default() {
|
||||
return { x: 0, y: 0 };
|
||||
},
|
||||
},
|
||||
items: {
|
||||
// The most important list, if not, will not be displayed
|
||||
type: Array as PropType<ContextMenuItem[]>,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const ItemContent: FunctionalComponent<ItemContentProps> = (props) => {
|
||||
const { item } = props;
|
||||
return (
|
||||
<span
|
||||
style="display: inline-block; width: 100%; "
|
||||
class="px-4"
|
||||
onClick={props.handler.bind(null, item)}
|
||||
>
|
||||
{props.showIcon && item.icon && <Icon class="mr-2" icon={item.icon} />}
|
||||
<span>{item.label}</span>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ContextMenu',
|
||||
props,
|
||||
setup(props) {
|
||||
const wrapRef = ref<ElRef>(null);
|
||||
const showRef = ref(false);
|
||||
|
||||
const getStyle = computed((): CSSProperties => {
|
||||
const { axis, items, styles, width } = props;
|
||||
const { x, y } = axis || { x: 0, y: 0 };
|
||||
const menuHeight = (items || []).length * 40;
|
||||
const menuWidth = width;
|
||||
const body = document.body;
|
||||
|
||||
const left = body.clientWidth < x + menuWidth ? x - menuWidth : x;
|
||||
const top = body.clientHeight < y + menuHeight ? y - menuHeight : y;
|
||||
return {
|
||||
...styles,
|
||||
width: `${width}px`,
|
||||
left: `${left + 1}px`,
|
||||
top: `${top + 1}px`,
|
||||
};
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => (showRef.value = true));
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
const el = unref(wrapRef);
|
||||
el && document.body.removeChild(el);
|
||||
});
|
||||
|
||||
function handleAction(item: ContextMenuItem, e: MouseEvent) {
|
||||
const { handler, disabled } = item;
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
showRef.value = false;
|
||||
e?.stopPropagation();
|
||||
e?.preventDefault();
|
||||
handler?.();
|
||||
}
|
||||
|
||||
function renderMenuItem(items: ContextMenuItem[]) {
|
||||
return items.map((item) => {
|
||||
const { disabled, label, children, divider = false } = item;
|
||||
|
||||
const contentProps = {
|
||||
item,
|
||||
handler: handleAction,
|
||||
showIcon: props.showIcon,
|
||||
};
|
||||
|
||||
if (!children || children.length === 0) {
|
||||
return (
|
||||
<>
|
||||
<Menu.Item disabled={disabled} class={`${prefixCls}__item`} key={label}>
|
||||
<ItemContent {...contentProps} />
|
||||
</Menu.Item>
|
||||
{divider ? <Divider key={`d-${label}`} /> : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (!unref(showRef)) return null;
|
||||
|
||||
return (
|
||||
<Menu.SubMenu key={label} disabled={disabled} popupClassName={`${prefixCls}__popup`}>
|
||||
{{
|
||||
title: () => <ItemContent {...contentProps} />,
|
||||
default: () => renderMenuItem(children),
|
||||
}}
|
||||
</Menu.SubMenu>
|
||||
);
|
||||
});
|
||||
}
|
||||
return () => {
|
||||
if (!unref(showRef)) {
|
||||
return null;
|
||||
}
|
||||
const { items } = props;
|
||||
return (
|
||||
<Menu
|
||||
inlineIndent={12}
|
||||
mode="vertical"
|
||||
class={prefixCls}
|
||||
ref={wrapRef}
|
||||
style={unref(getStyle)}
|
||||
>
|
||||
{renderMenuItem(items)}
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
@default-height: 42px !important;
|
||||
|
||||
@small-height: 36px !important;
|
||||
|
||||
@large-height: 36px !important;
|
||||
|
||||
.item-style() {
|
||||
li {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: @default-height;
|
||||
margin: 0 !important;
|
||||
line-height: @default-height;
|
||||
|
||||
span {
|
||||
line-height: @default-height;
|
||||
}
|
||||
|
||||
> div {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
&:not(.ant-menu-item-disabled):hover {
|
||||
color: @text-color-base;
|
||||
background-color: @item-hover-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.context-menu {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 200;
|
||||
display: block;
|
||||
width: 156px;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
background-color: @component-background;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
border-radius: 0.25rem;
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.1),
|
||||
0 1px 5px 0 rgba(0, 0, 0, 0.06);
|
||||
background-clip: padding-box;
|
||||
user-select: none;
|
||||
|
||||
.item-style();
|
||||
|
||||
.ant-divider {
|
||||
margin: 0 0;
|
||||
}
|
||||
|
||||
&__popup {
|
||||
.ant-divider {
|
||||
margin: 0 0;
|
||||
}
|
||||
|
||||
.item-style();
|
||||
}
|
||||
|
||||
.ant-menu-submenu-title,
|
||||
.ant-menu-item {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import contextMenuVue from './ContextMenu';
|
||||
import contextMenuVue from './ContextMenu.vue';
|
||||
import { isClient } from '/@/utils/is';
|
||||
import { CreateContextOptions, ContextMenuProps } from './types';
|
||||
import { CreateContextOptions, ContextMenuProps } from './typing';
|
||||
import { createVNode, render } from 'vue';
|
||||
|
||||
const menuManager: {
|
||||
|
|
@ -16,7 +16,9 @@ export const createContextMenu = function (options: CreateContextOptions) {
|
|||
|
||||
event && event?.preventDefault();
|
||||
|
||||
if (!isClient) return;
|
||||
if (!isClient) {
|
||||
return;
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
const body = document.body;
|
||||
|
||||
|
|
@ -54,9 +56,9 @@ export const createContextMenu = function (options: CreateContextOptions) {
|
|||
body.removeEventListener('scroll', handleClick);
|
||||
};
|
||||
|
||||
menuManager.resolve = function (...arg: any) {
|
||||
menuManager.resolve = function (arg) {
|
||||
remove();
|
||||
resolve(arg[0]);
|
||||
resolve(arg);
|
||||
};
|
||||
remove();
|
||||
body.appendChild(container);
|
||||
|
|
|
|||
|
|
@ -1,65 +0,0 @@
|
|||
@default-height: 42px !important;
|
||||
|
||||
@small-height: 36px !important;
|
||||
|
||||
@large-height: 36px !important;
|
||||
|
||||
.item-style() {
|
||||
li {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: @default-height;
|
||||
margin: 0 !important;
|
||||
line-height: @default-height;
|
||||
|
||||
span {
|
||||
line-height: @default-height;
|
||||
}
|
||||
|
||||
> div {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
&:not(.ant-menu-item-disabled):hover {
|
||||
color: @text-color-base;
|
||||
background-color: @item-hover-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.context-menu {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 200;
|
||||
display: block;
|
||||
width: 156px;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
background-color: @component-background;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
border-radius: 0.25rem;
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.1),
|
||||
0 1px 5px 0 rgba(0, 0, 0, 0.06);
|
||||
background-clip: padding-box;
|
||||
user-select: none;
|
||||
|
||||
.item-style();
|
||||
|
||||
.ant-divider {
|
||||
margin: 0 0;
|
||||
}
|
||||
|
||||
&__popup {
|
||||
.ant-divider {
|
||||
margin: 0 0;
|
||||
}
|
||||
|
||||
.item-style();
|
||||
}
|
||||
|
||||
.ant-menu-submenu-title,
|
||||
.ant-menu-item {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
import type { PropType } from 'vue';
|
||||
import type { Axis, ContextMenuItem } from './types';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
export const contextMenuProps = {
|
||||
width: propTypes.number.def(156),
|
||||
customEvent: {
|
||||
type: Object as PropType<Event>,
|
||||
default: null,
|
||||
},
|
||||
styles: propTypes.style,
|
||||
showIcon: propTypes.bool.def(true),
|
||||
axis: {
|
||||
// The position of the right mouse button click
|
||||
type: Object as PropType<Axis>,
|
||||
default() {
|
||||
return { x: 0, y: 0 };
|
||||
},
|
||||
},
|
||||
items: {
|
||||
// The most important list, if not, will not be displayed
|
||||
type: Array as PropType<ContextMenuItem[]>,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
import CountButton from './src/CountButton.vue';
|
||||
import CountdownInput from './src/CountdownInput.vue';
|
||||
import { withInstall } from '/@/utils';
|
||||
import countButton from './src/CountButton.vue';
|
||||
import countdownInput from './src/CountdownInput.vue';
|
||||
|
||||
export { CountdownInput, CountButton };
|
||||
export const CountdownInput = withInstall(countdownInput);
|
||||
export const CountButton = withInstall(countButton);
|
||||
|
|
|
|||
|
|
@ -1,42 +1,44 @@
|
|||
<template>
|
||||
<Button v-bind="$attrs" :disabled="isStart" @click="handleStart" :loading="loading">
|
||||
{{
|
||||
!isStart
|
||||
? t('component.countdown.normalText')
|
||||
: t('component.countdown.sendText', [currentCount])
|
||||
}}
|
||||
{{ getButtonText }}
|
||||
</Button>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, PropType, watchEffect } from 'vue';
|
||||
|
||||
import { defineComponent, ref, watchEffect, computed, unref } from 'vue';
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { useCountdown } from './useCountdown';
|
||||
import { isFunction } from '/@/utils/is';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
|
||||
const props = {
|
||||
value: { type: [Object, Number, String, Array] },
|
||||
count: { type: Number, default: 60 },
|
||||
beforeStartFunc: {
|
||||
type: Function as PropType<() => Promise<boolean>>,
|
||||
default: null,
|
||||
},
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CountButton',
|
||||
components: { Button },
|
||||
props: {
|
||||
value: propTypes.any,
|
||||
count: propTypes.number.def(60),
|
||||
beforeStartFunc: {
|
||||
type: Function as PropType<() => boolean>,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
props,
|
||||
setup(props) {
|
||||
const loading = ref(false);
|
||||
|
||||
const { currentCount, isStart, start, reset } = useCountdown(props.count);
|
||||
const { t } = useI18n();
|
||||
|
||||
const getButtonText = computed(() => {
|
||||
return !unref(isStart)
|
||||
? t('component.countdown.normalText')
|
||||
: t('component.countdown.sendText', [unref(currentCount)]);
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
props.value === undefined && reset();
|
||||
});
|
||||
|
||||
/**
|
||||
* @description: Judge whether there is an external function before execution, and decide whether to start after execution
|
||||
*/
|
||||
|
|
@ -54,7 +56,7 @@
|
|||
start();
|
||||
}
|
||||
}
|
||||
return { handleStart, isStart, currentCount, loading, t };
|
||||
return { handleStart, currentCount, loading, getButtonText, isStart };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,37 +1,35 @@
|
|||
<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 { propTypes } from '/@/utils/propTypes';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
|
||||
|
||||
const props = {
|
||||
value: { type: String },
|
||||
size: { type: String, validator: (v) => ['default', 'large', 'small'].includes(v) },
|
||||
count: { type: Number, default: 60 },
|
||||
sendCodeApi: {
|
||||
type: Function as PropType<() => Promise<boolean>>,
|
||||
default: null,
|
||||
},
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CountDownInput',
|
||||
components: { [Input.name]: Input, CountButton },
|
||||
components: { CountButton },
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
value: propTypes.string,
|
||||
size: propTypes.oneOf(['default', 'large', 'small']),
|
||||
count: propTypes.number.def(60),
|
||||
sendCodeApi: {
|
||||
type: Function as PropType<() => boolean>,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
props,
|
||||
setup(props) {
|
||||
const { prefixCls } = useDesign('countdown-input');
|
||||
const [state] = useRuleFormItem(props);
|
||||
|
||||
return { prefixCls, state };
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Transform vue-count-to to support vue3 version
|
||||
import { withInstall } from '/@/utils';
|
||||
import countTo from './src/CountTo.vue';
|
||||
|
||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||
export const CountTo = createAsyncComponent(() => import('./src/CountTo.vue'));
|
||||
export const CountTo = withInstall(countTo);
|
||||
|
|
|
|||
|
|
@ -1,50 +1,56 @@
|
|||
<template>
|
||||
<span :style="{ color: color }">
|
||||
{{ displayValue }}
|
||||
<span :style="{ color }">
|
||||
{{ value }}
|
||||
</span>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, computed, watch, onMounted, unref, toRef } from 'vue';
|
||||
import { countToProps } from './props';
|
||||
import { defineComponent, ref, computed, watchEffect, unref, onMounted, watch } from 'vue';
|
||||
import { useTransition, TransitionPresets } from '@vueuse/core';
|
||||
import { isNumber } from '/@/utils/is';
|
||||
|
||||
const props = {
|
||||
startVal: { type: Number, default: 0 },
|
||||
endVal: { type: Number, default: 2021 },
|
||||
duration: { type: Number, default: 1500 },
|
||||
autoplay: { type: Boolean, default: true },
|
||||
decimals: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
validator(value: number) {
|
||||
return value >= 0;
|
||||
},
|
||||
},
|
||||
prefix: { type: String, default: '' },
|
||||
suffix: { type: String, default: '' },
|
||||
separator: { type: String, default: ',' },
|
||||
decimal: { type: String, default: '.' },
|
||||
/**
|
||||
* font color
|
||||
*/
|
||||
color: { type: String },
|
||||
/**
|
||||
* Turn on digital animation
|
||||
*/
|
||||
useEasing: { type: Boolean, default: true },
|
||||
/**
|
||||
* Digital animation
|
||||
*/
|
||||
transition: { type: String, default: 'linear' },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CountTo',
|
||||
props: countToProps,
|
||||
emits: ['mounted', 'callback'],
|
||||
props,
|
||||
emits: ['onStarted', 'onFinished'],
|
||||
setup(props, { emit }) {
|
||||
const state = reactive<{
|
||||
localStartVal: number;
|
||||
printVal: number | null;
|
||||
displayValue: string;
|
||||
paused: boolean;
|
||||
localDuration: number | null;
|
||||
startTime: number | null;
|
||||
timestamp: number | null;
|
||||
rAF: any;
|
||||
remaining: number | null;
|
||||
color: any;
|
||||
}>({
|
||||
localStartVal: props.startVal,
|
||||
displayValue: formatNumber(props.startVal),
|
||||
printVal: null,
|
||||
paused: false,
|
||||
localDuration: props.duration,
|
||||
startTime: null,
|
||||
timestamp: null,
|
||||
remaining: null,
|
||||
rAF: null,
|
||||
color: null,
|
||||
});
|
||||
const source = ref(props.startVal);
|
||||
const disabled = ref(false);
|
||||
let outputValue = useTransition(source);
|
||||
|
||||
onMounted(() => {
|
||||
if (props.autoplay) {
|
||||
start();
|
||||
}
|
||||
emit('mounted');
|
||||
});
|
||||
const value = computed(() => formatNumber(unref(outputValue)));
|
||||
|
||||
const getCountDown = computed(() => {
|
||||
return props.startVal > props.endVal;
|
||||
watchEffect(() => {
|
||||
source.value = props.startVal;
|
||||
});
|
||||
|
||||
watch([() => props.startVal, () => props.endVal], () => {
|
||||
|
|
@ -53,93 +59,42 @@
|
|||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
props.autoplay && start();
|
||||
});
|
||||
|
||||
function start() {
|
||||
const { startVal, duration, color } = props;
|
||||
state.localStartVal = startVal;
|
||||
state.startTime = null;
|
||||
state.localDuration = duration;
|
||||
state.color = color;
|
||||
state.paused = false;
|
||||
state.rAF = requestAnimationFrame(count);
|
||||
}
|
||||
|
||||
function pauseResume() {
|
||||
if (state.paused) {
|
||||
resume();
|
||||
state.paused = false;
|
||||
} else {
|
||||
pause();
|
||||
state.paused = true;
|
||||
}
|
||||
}
|
||||
|
||||
function pause() {
|
||||
cancelAnimationFrame(state.rAF);
|
||||
}
|
||||
|
||||
function resume() {
|
||||
state.startTime = null;
|
||||
state.localDuration = +(state.remaining as number);
|
||||
state.localStartVal = +(state.printVal as number);
|
||||
requestAnimationFrame(count);
|
||||
run();
|
||||
source.value = props.endVal;
|
||||
}
|
||||
|
||||
function reset() {
|
||||
state.startTime = null;
|
||||
cancelAnimationFrame(state.rAF);
|
||||
state.displayValue = formatNumber(props.startVal);
|
||||
source.value = props.startVal;
|
||||
run();
|
||||
}
|
||||
|
||||
function count(timestamp: number) {
|
||||
const { useEasing, easingFn, endVal } = props;
|
||||
if (!state.startTime) state.startTime = timestamp;
|
||||
state.timestamp = timestamp;
|
||||
const progress = timestamp - state.startTime;
|
||||
state.remaining = (state.localDuration as number) - progress;
|
||||
if (useEasing) {
|
||||
if (unref(getCountDown)) {
|
||||
state.printVal =
|
||||
state.localStartVal -
|
||||
easingFn(progress, 0, state.localStartVal - endVal, state.localDuration as number);
|
||||
} else {
|
||||
state.printVal = easingFn(
|
||||
progress,
|
||||
state.localStartVal,
|
||||
endVal - state.localStartVal,
|
||||
state.localDuration as number
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (unref(getCountDown)) {
|
||||
state.printVal =
|
||||
state.localStartVal -
|
||||
(state.localStartVal - endVal) * (progress / (state.localDuration as number));
|
||||
} else {
|
||||
state.printVal =
|
||||
state.localStartVal +
|
||||
(endVal - state.localStartVal) * (progress / (state.localDuration as number));
|
||||
}
|
||||
}
|
||||
if (unref(getCountDown)) {
|
||||
state.printVal = state.printVal < endVal ? endVal : state.printVal;
|
||||
} else {
|
||||
state.printVal = state.printVal > endVal ? endVal : state.printVal;
|
||||
}
|
||||
state.displayValue = formatNumber(state.printVal);
|
||||
if (progress < (state.localDuration as number)) {
|
||||
state.rAF = requestAnimationFrame(count);
|
||||
} else {
|
||||
emit('callback');
|
||||
}
|
||||
function run() {
|
||||
outputValue = useTransition(source, {
|
||||
disabled,
|
||||
duration: props.duration,
|
||||
onFinished: () => emit('onFinished'),
|
||||
onStarted: () => emit('onStarted'),
|
||||
...(props.useEasing ? { transition: TransitionPresets[props.transition] } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
function formatNumber(num: number | string) {
|
||||
if (!num) {
|
||||
return '';
|
||||
}
|
||||
const { decimals, decimal, separator, suffix, prefix } = props;
|
||||
num = Number(num).toFixed(decimals);
|
||||
num += '';
|
||||
|
||||
const x = num.split('.');
|
||||
let x1 = x[0];
|
||||
const x2 = x.length > 1 ? decimal + x[1] : '';
|
||||
|
||||
const rgx = /(\d+)(\d{3})/;
|
||||
if (separator && !isNumber(separator)) {
|
||||
while (rgx.test(x1)) {
|
||||
|
|
@ -149,14 +104,7 @@
|
|||
return prefix + x1 + x2 + suffix;
|
||||
}
|
||||
|
||||
return {
|
||||
count,
|
||||
reset,
|
||||
resume,
|
||||
start,
|
||||
pauseResume,
|
||||
displayValue: toRef(state, 'displayValue'),
|
||||
};
|
||||
return { value, start, reset };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
import { PropType } from 'vue';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
export const countToProps = {
|
||||
startVal: propTypes.number.def(0),
|
||||
endVal: propTypes.number.def(2020),
|
||||
duration: propTypes.number.def(1300),
|
||||
autoplay: propTypes.bool.def(true),
|
||||
decimals: {
|
||||
type: Number as PropType<number>,
|
||||
required: false,
|
||||
default: 0,
|
||||
validator(value: number) {
|
||||
return value >= 0;
|
||||
},
|
||||
},
|
||||
color: {
|
||||
type: String as PropType<string>,
|
||||
require: false,
|
||||
},
|
||||
decimal: propTypes.string.def('.'),
|
||||
separator: propTypes.string.def(','),
|
||||
prefix: propTypes.string.def(''),
|
||||
suffix: propTypes.string.def(''),
|
||||
useEasing: propTypes.bool.def(true),
|
||||
easingFn: {
|
||||
type: Function as PropType<(t: number, b: number, c: number, d: number) => number>,
|
||||
default(t: number, b: number, c: number, d: number) {
|
||||
return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -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>
|
||||
|
|
@ -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: Cropper.Options = {
|
||||
aspectRatio: 16 / 9,
|
||||
const defaultOptions: Options = {
|
||||
aspectRatio: 1,
|
||||
zoomable: true,
|
||||
zoomOnTouch: true,
|
||||
zoomOnWheel: true,
|
||||
|
|
@ -42,40 +42,33 @@
|
|||
movable: true,
|
||||
rotatable: true,
|
||||
};
|
||||
|
||||
const props = {
|
||||
src: { type: String, required: true },
|
||||
alt: { type: String },
|
||||
circled: { type: Boolean, default: false },
|
||||
realTimePreview: { type: Boolean, default: true },
|
||||
height: { type: [String, Number], default: '360px' },
|
||||
crossorigin: {
|
||||
type: String as PropType<'' | 'anonymous' | 'use-credentials' | undefined>,
|
||||
default: undefined,
|
||||
},
|
||||
imageStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
|
||||
options: { type: Object as PropType<Options>, default: () => ({}) },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
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,
|
||||
|
|
@ -84,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) {
|
||||
|
|
@ -98,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.onloadend = (e: any) => {
|
||||
ctx.emit('cropperedInfo', {
|
||||
imgBase64: e.target.result,
|
||||
fileReader.readAsDataURL(blob);
|
||||
fileReader.onloadend = (e) => {
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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';
|
||||
|
|
@ -81,46 +77,40 @@
|
|||
|
||||
instance && emit('register', drawerInstance, instance.uid);
|
||||
|
||||
const getMergeProps = computed(
|
||||
(): DrawerProps => {
|
||||
return deepMerge(toRaw(props), unref(propsRef));
|
||||
}
|
||||
);
|
||||
const getMergeProps = computed((): DrawerProps => {
|
||||
return deepMerge(toRaw(props), unref(propsRef));
|
||||
});
|
||||
|
||||
const getProps = computed(
|
||||
(): DrawerProps => {
|
||||
const opt = {
|
||||
placement: 'right',
|
||||
...unref(attrs),
|
||||
...unref(getMergeProps),
|
||||
visible: unref(visibleRef),
|
||||
};
|
||||
opt.title = undefined;
|
||||
const { isDetail, width, wrapClassName, getContainer } = opt;
|
||||
if (isDetail) {
|
||||
if (!width) {
|
||||
opt.width = '100%';
|
||||
}
|
||||
const detailCls = `${prefixCls}__detail`;
|
||||
opt.wrapClassName = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls;
|
||||
|
||||
if (!getContainer) {
|
||||
// TODO type error?
|
||||
opt.getContainer = `.${prefixVar}-layout-content` as any;
|
||||
}
|
||||
const getProps = computed((): DrawerProps => {
|
||||
const opt = {
|
||||
placement: 'right',
|
||||
...unref(attrs),
|
||||
...unref(getMergeProps),
|
||||
visible: unref(visibleRef),
|
||||
};
|
||||
opt.title = undefined;
|
||||
const { isDetail, width, wrapClassName, getContainer } = opt;
|
||||
if (isDetail) {
|
||||
if (!width) {
|
||||
opt.width = '100%';
|
||||
}
|
||||
return opt as DrawerProps;
|
||||
}
|
||||
);
|
||||
const detailCls = `${prefixCls}__detail`;
|
||||
opt.wrapClassName = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls;
|
||||
|
||||
const getBindValues = computed(
|
||||
(): DrawerProps => {
|
||||
return {
|
||||
...attrs,
|
||||
...unref(getProps),
|
||||
};
|
||||
if (!getContainer) {
|
||||
// TODO type error?
|
||||
opt.getContainer = `.${prefixVar}-layout-content` as any;
|
||||
}
|
||||
}
|
||||
);
|
||||
return opt as DrawerProps;
|
||||
});
|
||||
|
||||
const getBindValues = computed((): DrawerProps => {
|
||||
return {
|
||||
...attrs,
|
||||
...unref(getProps),
|
||||
};
|
||||
});
|
||||
|
||||
// Custom implementation of the bottom button,
|
||||
const getFooterHeight = computed(() => {
|
||||
|
|
@ -133,15 +123,13 @@
|
|||
return `0px`;
|
||||
});
|
||||
|
||||
const getScrollContentStyle = computed(
|
||||
(): CSSProperties => {
|
||||
const footerHeight = unref(getFooterHeight);
|
||||
return {
|
||||
position: 'relative',
|
||||
height: `calc(100% - ${footerHeight})`,
|
||||
};
|
||||
}
|
||||
);
|
||||
const getScrollContentStyle = computed((): CSSProperties => {
|
||||
const footerHeight = unref(getFooterHeight);
|
||||
return {
|
||||
position: 'relative',
|
||||
height: `calc(100% - ${footerHeight})`,
|
||||
};
|
||||
});
|
||||
|
||||
const getLoading = computed(() => {
|
||||
return !!unref(getProps)?.loading;
|
||||
|
|
@ -175,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;
|
||||
|
|
|
|||
|
|
@ -43,15 +43,13 @@
|
|||
setup(props, { emit }) {
|
||||
const { prefixCls } = useDesign('basic-drawer-footer');
|
||||
|
||||
const getStyle = computed(
|
||||
(): CSSProperties => {
|
||||
const heightStr = `${props.height}`;
|
||||
return {
|
||||
height: heightStr,
|
||||
lineHeight: heightStr,
|
||||
};
|
||||
}
|
||||
);
|
||||
const getStyle = computed((): CSSProperties => {
|
||||
const heightStr = `${props.height}`;
|
||||
return {
|
||||
height: heightStr,
|
||||
lineHeight: heightStr,
|
||||
};
|
||||
});
|
||||
|
||||
function handleOk() {
|
||||
emit('ok');
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -1,8 +1,4 @@
|
|||
import type { App } from 'vue';
|
||||
import { withInstall } from '/@/utils';
|
||||
import flowChart from './src/FlowChart.vue';
|
||||
|
||||
export const FlowChart = Object.assign(flowChart, {
|
||||
install(app: App) {
|
||||
app.component(flowChart.name, flowChart);
|
||||
},
|
||||
});
|
||||
export const FlowChart = withInstall(flowChart);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -25,8 +25,7 @@
|
|||
import { isFunction } from '/@/utils/is';
|
||||
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
|
||||
import { useAttrs } from '/@/hooks/core/useAttrs';
|
||||
import { get } from 'lodash-es';
|
||||
|
||||
import { get, omit } from 'lodash-es';
|
||||
import { LoadingOutlined } from '@ant-design/icons-vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
|
|
@ -83,6 +82,7 @@
|
|||
prev.push({
|
||||
label: next[labelField],
|
||||
value: numberToString ? `${value}` : value,
|
||||
...omit(next, [labelField, valueField]),
|
||||
});
|
||||
}
|
||||
return prev;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue