chore: merge branch 'main' of github.com:anncwb/vue-vben-admin into main

This commit is contained in:
shisan 2021-08-16 23:51:56 +08:00
commit 2a790d24bf
150 changed files with 3399 additions and 3000 deletions

View File

@ -25,6 +25,7 @@ module.exports = defineConfig({
'plugin:jest/recommended', 'plugin:jest/recommended',
], ],
rules: { rules: {
'vue/script-setup-uses-vars': 'error',
'@typescript-eslint/ban-ts-ignore': 'off', '@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-explicit-any': 'off',
@ -61,7 +62,6 @@ module.exports = defineConfig({
'vue/singleline-html-element-content-newline': 'off', 'vue/singleline-html-element-content-newline': 'off',
'vue/attribute-hyphenation': 'off', 'vue/attribute-hyphenation': 'off',
'vue/require-default-prop': 'off', 'vue/require-default-prop': 'off',
'vue/script-setup-uses-vars': 'off',
'vue/html-self-closing': [ 'vue/html-self-closing': [
'error', 'error',
{ {

28
.github/ISSUE_TEMPLATE/3-bug-cn.md vendored Normal file
View File

@ -0,0 +1,28 @@
---
name: 🐛 Bug 报告
about: 向我们报告一个Bug以帮助我们改进
title: ''
labels: 'bug: pending triage'
assignees: ''
---
**⚠️ 重要 ⚠️ 在进一步操作之前,请检查下列选项。如果您忽视此模板或者没有提供关键信息,您的 Issue 将直接被关闭**
- [ ] 已阅读 [文档](https://anncwb.github.io/vue-vben-admin-doc/).
- [ ] 确保您的代码已是最新或者所报告的 Bug 在最新版本中可以重现. (部分 Bug 可能已经在最近的代码中修复)
- [ ] 已在 Issues 中搜索了相关的关键词
- [ ] 不是 ant design vue 组件库的 Bug
### 描述 Bug
请清晰地描述此 Bug 的具体表现。
### 复现 Bug
请描述在演示页面中复现 Bug 的详细步骤,以确保我们可以理解并定位问题。部分 Bug 如果未在 Demo 中涉及,请务必提供关键代码
## 系统信息
- 操作系统:
- Node 版本:
- 包管理器 (npm/yarn/pnpm) 及其版本:

View File

@ -68,10 +68,10 @@ jobs:
sed -i "s#VITE_BUILD_COMPRESS\s*=.*#VITE_BUILD_COMPRESS = 'gzip'#g" ./.env.production sed -i "s#VITE_BUILD_COMPRESS\s*=.*#VITE_BUILD_COMPRESS = 'gzip'#g" ./.env.production
cat ./.env.production cat ./.env.production
- name: use Node.js 14 - name: use Node.js 16
uses: actions/setup-node@v2.1.2 uses: actions/setup-node@v2.1.2
with: with:
node-version: '14.x' node-version: '16.x'
- name: Get yarn cache - name: Get yarn cache
id: yarn-cache id: yarn-cache

View File

@ -21,10 +21,10 @@ jobs:
sed -i "s#VITE_DROP_CONSOLE\s*=.*#VITE_DROP_CONSOLE = true#g" ./.env.production sed -i "s#VITE_DROP_CONSOLE\s*=.*#VITE_DROP_CONSOLE = true#g" ./.env.production
cat ./.env.production cat ./.env.production
- name: use Node.js 15 - name: use Node.js 16
uses: actions/setup-node@v2.1.2 uses: actions/setup-node@v2.1.2
with: with:
node-version: '15.x' node-version: '16.x'
- name: Get yarn cache - name: Get yarn cache
id: yarn-cache id: yarn-cache

View File

@ -117,6 +117,7 @@
"i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}", "i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}",
"i18n-ally.enabledParsers": ["ts"], "i18n-ally.enabledParsers": ["ts"],
"i18n-ally.sourceLanguage": "en", "i18n-ally.sourceLanguage": "en",
"i18n-ally.displayLanguage": "zh-CN",
"i18n-ally.enabledFrameworks": ["vue", "react"], "i18n-ally.enabledFrameworks": ["vue", "react"],
"cSpell.words": [ "cSpell.words": [
"vben", "vben",
@ -147,6 +148,8 @@
"vueuse", "vueuse",
"zxcvbn", "zxcvbn",
"lintstagedrc", "lintstagedrc",
"brotli" "brotli",
"tailwindcss",
"sider"
] ]
} }

View File

@ -1,3 +1,60 @@
## 2.7.1(2021-08-16)
- Upgrade vue 3.2, if the operation fails, delete node_modules and reinstall it
### ✨ Features
- **BasicTree** Add search function related properties and methods
- **BasicForm** added `alwaysShowLines` to set the number of lines kept displayed when folding
### 🐛 Bug Fixes
- **Cropper** Fix the problem of failure to destroy in time
- **BasicTable**
- Fix the problem that `CellFormat` cannot use `Map` type data
- Fixed an issue where the editable cell failed to display the `0` value correctly
- Fixed the issue that selection-change event failed to trigger correctly when unchecked
- Fix the problem that the background color of the full screen state under the light theme is incorrect
- Fix the problem of obtaining complete data when `getSelectRows` does not support remote data cross-page selection
- Fix the issue that the `size` property provided for editing components in `editComponentProps` is invalid
- **Qrcode** Fixed the problem that the QR code component could not be drawn in time when it was created
- **BasicModal** Fix the problem that the `helpMessage` property does not work
- **BasicButton** Fix the problem that the button style performance is inconsistent with the official antd
- **Others** Fix the problem that `useRedo` (reload the current route) will lose route `params` data
## 2.7.0(2021-08-03)
## (Breaking changes) Breaking changes
- Restore the project `tailwindcss` back to `windicss`, tried `tailwindcss`, there may be a lot of problems, first switch back to `windicss` to improve development efficiency and lower switching costs.
- There are currently incompatible areas of the project
- The wording of `xl:!m-4` needs to be changed to `!xl:m-4`, note that only `!` is incompatible. If you dont use it, you dont need to change it.
- The memory overflow problem may still exist (low frequency, just restart, restart vite faster)
### ✨ Features
- **Preview** Add new properties and events
- **Dark Theme** added support for tailwindcss night mode
- **Others** add setTip method for useLoading
### 🐛 Bug Fixes
- **ApiTreeSelect** Fixed the problem of failing to monitor `params` changes correctly
- **ImgRotateDragVerify** Fix the problem that the component `resume` method cannot be called
- **TableAction** Fix the problem that the stopButtonPropagation property does not work in some cases
- **PageWrapper** Fix the problem of invalid `class` attribute
- **BasicTree** Fix the problem that the `checkAll` method will affect the `disabled` state node
- **BasicTable**
- Fix the issue that editable cells do not support `ellipsis` configuration
- Fixed the problem that the pop-up layer of sub-components (popconfirm and edit components such as select and treeSelect) cannot be seen in full-screen mode
- Fixed an issue where when `expandRowByClick` is enabled, clicking non-expandable rows may cause style errors
- Fix the problem that the dynamic change of `pagination` property does not take effect
- Fix the problem that `getSelectRows` does not support the child data of the tree table -**Dark Theme** Fix the color matching problem under the dark theme
- Fix the background color of the selected node of the `Tree` component
- Fix the color configuration of the `Alert` component
- Fix the problem of the button color of `link` type in the disabled state
- Fix the style problem of checked checkboxes in `Tree` -**Others** Fix the problem that useScript failed to automatically remove the script node
## 2.6.1(2021-07-19) ## 2.6.1(2021-07-19)
### ✨ Features ### ✨ Features

View File

@ -1,3 +1,268 @@
## [2.7.1](https://github.com/anncwb/vue-vben-admin/compare/v2.6.1...v2.7.1) (2021-08-16)
### Bug Fixes
- `slots` working in components ([b1f3176](https://github.com/anncwb/vue-vben-admin/commit/b1f31762e3c86a432a8d559ab957444eaf5525ad))
- add axios error info from response ([#1083](https://github.com/anncwb/vue-vben-admin/issues/1083)) ([72634ff](https://github.com/anncwb/vue-vben-admin/commit/72634ffe6e6649d36ee41f7633c8ee2ab80cf25e))
- auto remove script dom in `useScript` ([a544dd3](https://github.com/anncwb/vue-vben-admin/commit/a544dd3e589329339177dad3d5c1f75dd6e6f0ca))
- fixed `useRedo` may loss route params ([2dd3d85](https://github.com/anncwb/vue-vben-admin/commit/2dd3d8544866231895d23dba63785b683ae0062e)), closes [#1079](https://github.com/anncwb/vue-vben-admin/issues/1079)
- fixed basicButton ghost style ([3ba8a67](https://github.com/anncwb/vue-vben-admin/commit/3ba8a67647d35fb9639a5af66f33d43eff493d15))
- fixed basicButton primary style ([1b57792](https://github.com/anncwb/vue-vben-admin/commit/1b577922e752c02fe7c033c53be37ef81e4e9b8e))
- fixed basicButton style ([beb4ae9](https://github.com/anncwb/vue-vben-admin/commit/beb4ae92c190780bbd3bc6bc7547d52e2ccf8cf1))
- **cropper:** cropper not destroy in time ([3819430](https://github.com/anncwb/vue-vben-admin/commit/381943078fd55123fde3d5555e04f279d7f1c407)), closes [#1027](https://github.com/anncwb/vue-vben-admin/issues/1027)
- **demo:** fix form style in modal ([30c5fc6](https://github.com/anncwb/vue-vben-admin/commit/30c5fc63c8600cfb03f917d79e56c0a7e7ff64e0)), closes [#1076](https://github.com/anncwb/vue-vben-admin/issues/1076)
- **i18n:** add i18n translate data ([1f55c41](https://github.com/anncwb/vue-vben-admin/commit/1f55c4180f9c0cf48e3796a77d6f0bfd46107272))
- **locales:** fix that vscode extension i18n-Ally detect zh-CN as zh ([#1044](https://github.com/anncwb/vue-vben-admin/issues/1044)) ([b2d49cb](https://github.com/anncwb/vue-vben-admin/commit/b2d49cbbf81cb15e75905deb95bdf7ac4af4e599))
- **modal:** `helpMessage` doesn't work ([953bfc6](https://github.com/anncwb/vue-vben-admin/commit/953bfc6f1a559309ea2b1114b8ede911a3751cc7))
- **page-wrapper:** fix `class` not working ([8879ae8](https://github.com/anncwb/vue-vben-admin/commit/8879ae8d773e8dc4c252c4234eefeab9bc135a30))
- **qrcode:** qrcode not displayed properly ([26f251e](https://github.com/anncwb/vue-vben-admin/commit/26f251e1ed5bfd79c8615fb552ca302f917cc588)), closes [#1026](https://github.com/anncwb/vue-vben-admin/issues/1026)
- **route:** the whitelist should include basicRoutes ([#1048](https://github.com/anncwb/vue-vben-admin/issues/1048)) ([1bb5156](https://github.com/anncwb/vue-vben-admin/commit/1bb51569236fd9bcc55dd9f237f51f218956b258))
- **table:** `0` is not shown in editable cell ([33a335a](https://github.com/anncwb/vue-vben-admin/commit/33a335a3f52aead522b3fbee0d558e2e797580ff)), closes [#1039](https://github.com/anncwb/vue-vben-admin/issues/1039)
- **table:** `cellFormat` support `Map` ([1214b7c](https://github.com/anncwb/vue-vben-admin/commit/1214b7c32c425750a4d0202a9b235eb9e45a6f47)), closes [#1031](https://github.com/anncwb/vue-vben-admin/issues/1031)
- **table:** `getSelectRows` support multi-page ([4b6025c](https://github.com/anncwb/vue-vben-admin/commit/4b6025cb9a3ef067680201ec3052bc651e0a0c1b)), closes [#914](https://github.com/anncwb/vue-vben-admin/issues/914)
- **table:** `selection-change` not triggered in unchecking ([019555b](https://github.com/anncwb/vue-vben-admin/commit/019555be0c88edc673cae382023d647e78959b30)), closes [#1053](https://github.com/anncwb/vue-vben-admin/issues/1053)
- **table:** `size` not worked in `editComponentProps` ([7971896](https://github.com/anncwb/vue-vben-admin/commit/7971896383296c155b7ab16b5beb3544f34ee525)), closes [#1074](https://github.com/anncwb/vue-vben-admin/issues/1074)
- **table:** fix `getSelectRows` for treeTable ([f2b8bb4](https://github.com/anncwb/vue-vben-admin/commit/f2b8bb43a0b9172b9ef9ced8e83bf91143a091d9)), closes [#1003](https://github.com/anncwb/vue-vben-admin/issues/1003)
- **table:** fix `injection not found` warning ([53e79a2](https://github.com/anncwb/vue-vben-admin/commit/53e79a2d94df19c0e1aa7399d5ce4c27834e0350))
- **types:** fix some type errors ([9035fd1](https://github.com/anncwb/vue-vben-admin/commit/9035fd191e4e8d954f42b3a4cd1e80ec70b7cbb6))
- The Style of tableTitle slot ([#1023](https://github.com/anncwb/vue-vben-admin/issues/1023)) ([02e7756](https://github.com/anncwb/vue-vben-admin/commit/02e77560624cc4a95a5a20ffd5e0601f1f88c6f4))
- fix build handler & misc ([#1060](https://github.com/anncwb/vue-vben-admin/issues/1060)) ([66feb77](https://github.com/anncwb/vue-vben-admin/commit/66feb779a8645a93760c784c510512118c4c6efa))
- **table:** recursive updateTableDataRecord ([#1024](https://github.com/anncwb/vue-vben-admin/issues/1024)) ([72f953c](https://github.com/anncwb/vue-vben-admin/commit/72f953c8d3413a7f5482793258503017a81cc759))
- **table:** wrong bg-color in fullscreen mode ([2052eb5](https://github.com/anncwb/vue-vben-admin/commit/2052eb5a65be38c44165efecdb15266de4638667))
- **type:** fix ant-design-vue -> ([#1043](https://github.com/anncwb/vue-vben-admin/issues/1043)) ([6d5388a](https://github.com/anncwb/vue-vben-admin/commit/6d5388aaf143ac76bac0b68d56a3ab6b5993e807))
- fix iframe heigth error ([#1012](https://github.com/anncwb/vue-vben-admin/issues/1012)) ([d76cfd7](https://github.com/anncwb/vue-vben-admin/commit/d76cfd7f809ba48880c950a64cb43a5c9c44176c))
- Fix the invalid hot update of BasicButton when changing style outside ([#1016](https://github.com/anncwb/vue-vben-admin/issues/1016)) ([be2d11d](https://github.com/anncwb/vue-vben-admin/commit/be2d11d5d344a508e94abe3534726c80e1f1f271))
- the position of tinymce upload image is wrong ([#1015](https://github.com/anncwb/vue-vben-admin/issues/1015)) ([2fd0fd2](https://github.com/anncwb/vue-vben-admin/commit/2fd0fd281e65d6d2551478c5f19250347dc14062))
- **tree:** fix `checkAll` effects `disabled` node ([ddd1893](https://github.com/anncwb/vue-vben-admin/commit/ddd1893b113e13786037522341abb2e75f8f9d5b))
- style property of actionColOpt is invalid ([#997](https://github.com/anncwb/vue-vben-admin/issues/997)) ([225bd4c](https://github.com/anncwb/vue-vben-admin/commit/225bd4c39de377d93c605f33bfdf3d8fd565f12b))
- typo ([#980](https://github.com/anncwb/vue-vben-admin/issues/980)) ([7e6a89f](https://github.com/anncwb/vue-vben-admin/commit/7e6a89ffeb8c63467908d5807d3d7c4761620ee3))
- **api-tree-select:** auto reload while `params` changed ([c734f68](https://github.com/anncwb/vue-vben-admin/commit/c734f6858daea6d11cd517463b06fcce58744947))
- **dark-theme:** alert color in dark-theme ([9b7ede0](https://github.com/anncwb/vue-vben-admin/commit/9b7ede09b9efe4d5a15ab0cdeadac480a29c0f62))
- **dark-theme:** bgcolor of `selected tree node` in dark theme ([8cf004a](https://github.com/anncwb/vue-vben-admin/commit/8cf004a5f59895e2487c3a350c83000e585b897e)), closes [#949](https://github.com/anncwb/vue-vben-admin/issues/949)
- **dark-theme:** disabled link `button` color ([4281216](https://github.com/anncwb/vue-vben-admin/commit/42812162c46832ce4d3e332bd579c042309115bc))
- **dark-theme:** fixed `TreeSelect` & `DatePicker` theme ([d1e0e8b](https://github.com/anncwb/vue-vben-admin/commit/d1e0e8bcea1c168631222989969e14f7d0d1b6a4)), closes [#955](https://github.com/anncwb/vue-vben-admin/issues/955)
- **dark-theme:** style for checked tree nodes ([662b576](https://github.com/anncwb/vue-vben-admin/commit/662b576ac2088247cb58e295378f228462508a37))
- **demo:** account page form validation ([8702965](https://github.com/anncwb/vue-vben-admin/commit/87029650570e470431fb94d35a273c5d07a73135))
- **demo:** fix roles mock data ([c375e32](https://github.com/anncwb/vue-vben-admin/commit/c375e32305eae5128e09ad1bda39ce0cc6afd790))
- **form:** remove console error for `setFieldsValue` ([8d185bb](https://github.com/anncwb/vue-vben-admin/commit/8d185bb5841c83eb49c78e8342e65067aa6f9b80)), closes [#952](https://github.com/anncwb/vue-vben-admin/issues/952)
- **table:** fix `pagination` props working ([e327893](https://github.com/anncwb/vue-vben-admin/commit/e32789373eb5b1b531572b59692bf552dac365dc))
- expandIcon slot of BasicTable component is invalid ([#975](https://github.com/anncwb/vue-vben-admin/issues/975)) ([98c206d](https://github.com/anncwb/vue-vben-admin/commit/98c206d9c9661a18dde4ec7782cbe1eb6e62ebeb))
- **demo:** menu `error-log` link to 404 page ([341bd63](https://github.com/anncwb/vue-vben-admin/commit/341bd633d8ed38a5a357db8f97166c2eba2895d3))
- **demo:** multi-modal used with dynamic component ([e1c4723](https://github.com/anncwb/vue-vben-admin/commit/e1c47233edf7675aede6d5f023726945a510ddf7))
- **echarts:** fix graphic config cannot be used in echarts options ([#959](https://github.com/anncwb/vue-vben-admin/issues/959)) ([525484e](https://github.com/anncwb/vue-vben-admin/commit/525484e7a409b032d22231f90a92e700ef4290ae))
- **form:** fix `validate` promise catch ([571f281](https://github.com/anncwb/vue-vben-admin/commit/571f28138f782553eb39cda2d632e5ac1aa1e145))
- **img-rotate-drag-verify:** fix `resume` method support ([32d64db](https://github.com/anncwb/vue-vben-admin/commit/32d64dbe816a0afda6ee9e91863199afb3e7b48e)), closes [#946](https://github.com/anncwb/vue-vben-admin/issues/946)
- **login:** fix `auto fill` style in dark-theme ([cebc6a5](https://github.com/anncwb/vue-vben-admin/commit/cebc6a590e1a19af7380a55aed43b23af274df0a))
- **perm-guard:** Fix the problem that the routing query is lost after refreshing the page ([#941](https://github.com/anncwb/vue-vben-admin/issues/941)) ([9c4889f](https://github.com/anncwb/vue-vben-admin/commit/9c4889f0859bc60decf0ef40c383c1946de1d68a))
- **qrcode:** Fix the problem that the QR code cannot be dynamically generated ([#974](https://github.com/anncwb/vue-vben-admin/issues/974)) ([fe4eae3](https://github.com/anncwb/vue-vben-admin/commit/fe4eae37146068f01ba08f033e0c2e8bd03e48b5))
- **style:** fix checkbox-checked css in dark mode ([d3f08e3](https://github.com/anncwb/vue-vben-admin/commit/d3f08e37c5b6e46f33d525e9ef4b4c235d77a192))
- **table:** component shown in `fullscreen` mode ([a07ab6d](https://github.com/anncwb/vue-vben-admin/commit/a07ab6d7aa1060f856649a9bdbec9dfa50b14f26))
- **table:** editable cell display with validation ([202aa42](https://github.com/anncwb/vue-vben-admin/commit/202aa42b8d5a94e84ad386bcf7feab96926c70dd)), closes [#953](https://github.com/anncwb/vue-vben-admin/issues/953)
- **table:** fix `dataPicker` show in `fullscreen` mode ([a5a9b3f](https://github.com/anncwb/vue-vben-admin/commit/a5a9b3fb34c64b6ea9c9ab3d58045f6e5963952b))
- **table:** fix expand style ([14fb21d](https://github.com/anncwb/vue-vben-admin/commit/14fb21d0b7b9ac69c7b3c463de6d709bd5713d14)), closes [#969](https://github.com/anncwb/vue-vben-admin/issues/969)
- **table:** fix tableSettings popup in fullscreen mode ([dce3fb0](https://github.com/anncwb/vue-vben-admin/commit/dce3fb0f20516aaf4817281f5d08109e53a73ecb))
- **table-action:** stopButtonPropagation not working ([9b8f165](https://github.com/anncwb/vue-vben-admin/commit/9b8f165a365758d001e6d86ae7afe4ae3316d485))
- Fix vite profile hot update error reporting ([#968](https://github.com/anncwb/vue-vben-admin/issues/968)) ([956ed2e](https://github.com/anncwb/vue-vben-admin/commit/956ed2e3f770cc9cf822ce80f71b1e7f179792fb))
- **utils:** The date function gets a non-date when the parameter is null ([#954](https://github.com/anncwb/vue-vben-admin/issues/954)) ([350c85a](https://github.com/anncwb/vue-vben-admin/commit/350c85accf5033cc5a21b71bc36d5b7a74eb2573))
- fixed moment locale config ([27207a7](https://github.com/anncwb/vue-vben-admin/commit/27207a78caccb04372e0275c5cee526ec460de0e))
- typo for utils/env ([#1004](https://github.com/anncwb/vue-vben-admin/issues/1004)) ([e8eefd1](https://github.com/anncwb/vue-vben-admin/commit/e8eefd1bca41c181ec6395bf1d087d2018e2b1f1))
- **table:** fix editable cell not support `ellipsis` ([4bb506f](https://github.com/anncwb/vue-vben-admin/commit/4bb506fb1f6ac7d246f8792d29f337ec003ff426)), closes [#944](https://github.com/anncwb/vue-vben-admin/issues/944)
### Features
- add `updatePath` for `useTabs` ([bcfa338](https://github.com/anncwb/vue-vben-admin/commit/bcfa33822736b761757a2673d977f752cb5c4f7c)), closes [#1068](https://github.com/anncwb/vue-vben-admin/issues/1068)
- always refresh userinfo when page reload ([cc46935](https://github.com/anncwb/vue-vben-admin/commit/cc46935a8296dae62ecfc753a956338ba433927e))
- **demo:** add `async-validator` demo ([8b4b767](https://github.com/anncwb/vue-vben-admin/commit/8b4b767f4ca78f7c6f7586d8ba662552c2b7bb51))
- **form:** add `alwaysShowLines` prop ([93f9a19](https://github.com/anncwb/vue-vben-admin/commit/93f9a19aa16a3e9cb95338417c52d9a398e3f70b)), closes [#1051](https://github.com/anncwb/vue-vben-admin/issues/1051)
- **preview:** add more features ([e23bd26](https://github.com/anncwb/vue-vben-admin/commit/e23bd2696da945291a9b652f1af39ad1936f376b))
- **table:** add getRawDataSource() function ([#1029](https://github.com/anncwb/vue-vben-admin/issues/1029)) ([f3cf162](https://github.com/anncwb/vue-vben-admin/commit/f3cf162af1fa5634d4e562fa5239939af6f26093))
- **tree:** add searchable function ([60577d6](https://github.com/anncwb/vue-vben-admin/commit/60577d6720fd3f8d4d1a88b20ab902d6161a0eec)), closes [#1057](https://github.com/anncwb/vue-vben-admin/issues/1057)
- **use-loading:** add `setTip` method ([26d9476](https://github.com/anncwb/vue-vben-admin/commit/26d9476caff41cc355190604af42e0bd2ef0a353))
- Added support for tailwindcss night mode mechanism ([#998](https://github.com/anncwb/vue-vben-admin/issues/998)) ([189bc6f](https://github.com/anncwb/vue-vben-admin/commit/189bc6feb3f2860be8c531dd1ca996f3a2cff018))
### Performance Improvements
- **table:** fixed code style ([da12da9](https://github.com/anncwb/vue-vben-admin/commit/da12da9d8caeba0e7732551cfbad9b0da3baaac4)), closes [#1070](https://github.com/anncwb/vue-vben-admin/issues/1070)
- improve legacy compatibility ([e2664f6](https://github.com/anncwb/vue-vben-admin/commit/e2664f60029f03642f8b1a6afa9b1998705fce37))
# [2.7.0](https://github.com/anncwb/vue-vben-admin/compare/v2.5.9...v2.7.0) (2021-08-03)
### Bug Fixes
- The Style of tableTitle slot ([#1023](https://github.com/anncwb/vue-vben-admin/issues/1023)) ([02e7756](https://github.com/anncwb/vue-vben-admin/commit/02e77560624cc4a95a5a20ffd5e0601f1f88c6f4))
- **api-select:** ensure that the onchange function parameters are correct ([fa64fc8](https://github.com/anncwb/vue-vben-admin/commit/fa64fc8a622832b87fdf672965d55d543b5929a2))
- **app-search:** exclude hidden items ([faf5c9f](https://github.com/anncwb/vue-vben-admin/commit/faf5c9fd7ea40c407419a5a5c473f9b0c32c2a53))
- **app-search:** exclude items by `hideChildrenInMenu` ([02d3dca](https://github.com/anncwb/vue-vben-admin/commit/02d3dca57efedc1322ae38e3f432cf1f6c2cf839))
- **axios:** option `withToken` not work ([d509e89](https://github.com/anncwb/vue-vben-admin/commit/d509e897be5753c852e912112e70dac6247ba467))
- **breadcrumb:** `redirect` not worked ([f5e31fe](https://github.com/anncwb/vue-vben-admin/commit/f5e31febbd18372a34166cac390b1d9b914fe80e))
- **comp-tree:** support comp-tree-foreach stop,add insertNodesByKey ([#818](https://github.com/anncwb/vue-vben-admin/issues/818)) ([d97aa92](https://github.com/anncwb/vue-vben-admin/commit/d97aa927417bf45a7c127ecfa9b8e835b6b68855))
- **CountTo:** Fix displaying empty string when the value is 0 ([#864](https://github.com/anncwb/vue-vben-admin/issues/864)) ([82eb72b](https://github.com/anncwb/vue-vben-admin/commit/82eb72bbced931ba7f50069211f9511035ad09f4))
- **demo:** `setup` page route config ([d5d5c4b](https://github.com/anncwb/vue-vben-admin/commit/d5d5c4b4bfb3e3a5e54f9993966adc46a09a8b90))
- **demo:** account list fetch loss param ([424b171](https://github.com/anncwb/vue-vben-admin/commit/424b171e0db727f5e0157cbcfd5460f15f8ea609)), closes [#830](https://github.com/anncwb/vue-vben-admin/issues/830)
- **demo:** add mock data `account detail` route ([993e19d](https://github.com/anncwb/vue-vben-admin/commit/993e19dcc319e2b4c68df2ab76174b7b4d7b0428)), closes [#858](https://github.com/anncwb/vue-vben-admin/issues/858)
- **demo:** fix async tree demo, fixed: [#823](https://github.com/anncwb/vue-vben-admin/issues/823) ([5637588](https://github.com/anncwb/vue-vben-admin/commit/5637588fce880b01137191cc82c73e0fce621e8c))
- **demo:** form pages support `keepAlive` ([9228282](https://github.com/anncwb/vue-vben-admin/commit/9228282ae27daaa246f42e441e27b1b05eb30464))
- **demo:** resolve `key not exist` warnings ([45a94e4](https://github.com/anncwb/vue-vben-admin/commit/45a94e41c1397b84d08373f84f766204d2488714))
- **demo:** style error,fix [#806](https://github.com/anncwb/vue-vben-admin/issues/806) ([a2d8be3](https://github.com/anncwb/vue-vben-admin/commit/a2d8be3ab29da88126f3ba971f6893cb12327759))
- **demo-form:** add fieldMapToTime example,fix [#807](https://github.com/anncwb/vue-vben-admin/issues/807) ([a2a75a0](https://github.com/anncwb/vue-vben-admin/commit/a2a75a097ff6c9df12471eff0d62d44d2b88cfff))
- **design:** correct tailwind configuration,fix [#800](https://github.com/anncwb/vue-vben-admin/issues/800) ([aec230c](https://github.com/anncwb/vue-vben-admin/commit/aec230ca19d541079b64c54ba00596ef9cd92ca0))
- **drawer:** openDrawer is not normal in some cases ([941ad59](https://github.com/anncwb/vue-vben-admin/commit/941ad59759cbd5a5e39bcdf29783d8eea85caf72))
- **dropdown:** icon and trigger work unexpected ([60b80c9](https://github.com/anncwb/vue-vben-admin/commit/60b80c96e82da9101d56b2e195e9e7571de11f0a)), closes [#796](https://github.com/anncwb/vue-vben-admin/issues/796) [#787](https://github.com/anncwb/vue-vben-admin/issues/787)
- **form:** fix `suffix` slot style ([a9bbed1](https://github.com/anncwb/vue-vben-admin/commit/a9bbed19739376ab2bf67a14b04e872f14ca84cc))
- **form:** fix some prop declaration ([b5046f0](https://github.com/anncwb/vue-vben-admin/commit/b5046f07a27e8ca7fc8b961b74fa5e1b0d715149))
- **lock-screen:** ensure lock info is saved ([d38ff66](https://github.com/anncwb/vue-vben-admin/commit/d38ff6670a37478b31447f8058e786c4b044e218))
- **lock-screen:** fix lock-screen can skip on new window ([d7b84c7](https://github.com/anncwb/vue-vben-admin/commit/d7b84c78744f7d0077a779b232e1358040b50383))
- **markdown:** resolving markdown exceptions ([d95815b](https://github.com/anncwb/vue-vben-admin/commit/d95815b5031984e224140eb1b1d46e2dbf80abc1))
- **markdown:** set `value` error ([35e1347](https://github.com/anncwb/vue-vben-admin/commit/35e1347029e29a83a9648b6b398e6863cc40fca9))
- **menu:** display error when contains hidden items ([5ceeefd](https://github.com/anncwb/vue-vben-admin/commit/5ceeefd17d9ddc0e8844b900069b100f24d9c00e))
- **menu:** fix mix-menu incorrect jumping in `hover` mode ([cad021c](https://github.com/anncwb/vue-vben-admin/commit/cad021c34b71fa109640af75a0c2b72179e9e257))
- **menu:** make sure the menu is activated correctly ([cdb10cc](https://github.com/anncwb/vue-vben-admin/commit/cdb10cc4ac5e5e8f9cce3ff18d8fbd29ef10c86f))
- **mix-sider:** fix mix-sider hover logic ([0595a72](https://github.com/anncwb/vue-vben-admin/commit/0595a72da9c666af81a0916663e8e6a014e6fa69))
- **modal:** `setModalProps` support `defaultFullscreen` ([c7de65e](https://github.com/anncwb/vue-vben-admin/commit/c7de65ebba53941771153f18b184d3d4d31c0dbf))
- **modal:** maskClosable not work ([f750ff4](https://github.com/anncwb/vue-vben-admin/commit/f750ff435fee06acee78d6b9633e6e18d91685f8))
- **modal:** remove console log ([3dbbde2](https://github.com/anncwb/vue-vben-admin/commit/3dbbde2662352780377a9b216598d9348522f6ba))
- **pop-confirm:** fix event working unexpected ([a6ef771](https://github.com/anncwb/vue-vben-admin/commit/a6ef771fcce14c3644c965afaa69b3a17d0a7087))
- **popconfirm-button:** remove button excess `title` ([73654b7](https://github.com/anncwb/vue-vben-admin/commit/73654b7862c59d623d6d5dc7dcf6ff2704564d9a))
- **sider:** bottom trigger not work ([1bde404](https://github.com/anncwb/vue-vben-admin/commit/1bde4041211229d5d9d01ce0ca806fa99356b6de)), closes [#820](https://github.com/anncwb/vue-vben-admin/issues/820)
- **sider:** custom trigger does not take effect ([5005e6e](https://github.com/anncwb/vue-vben-admin/commit/5005e6e56b1cc7763a1cc23e1162dfb49452013b))
- **svg-icon:** fix SvgIcon style ([99829c7](https://github.com/anncwb/vue-vben-admin/commit/99829c79ab41a2319f40c5595a7d82d9e406ba18))
- **table:** fix index column style ([c7c0a7e](https://github.com/anncwb/vue-vben-admin/commit/c7c0a7e4c88a895000b1621981e4d4b2020c64b1))
- **table:** auto hide unnecessary scrollbar ([735028c](https://github.com/anncwb/vue-vben-admin/commit/735028c43055e8e80ebc7344af0cd0f51c744f98))
- **table:** editComponentProps support onChange ([829b366](https://github.com/anncwb/vue-vben-admin/commit/829b366cb2abf27e69d9665e5be022b3d3f15655))
- **table:** event editCancel loss params ([8d22231](https://github.com/anncwb/vue-vben-admin/commit/8d22231a5fa4afed19201a4a4e5c29d674498516))
- **table:** fix rowSelection.onChange not work ([df0f000](https://github.com/anncwb/vue-vben-admin/commit/df0f00085c1113eddd7a15954818ccece3538068)), closes [#825](https://github.com/anncwb/vue-vben-admin/issues/825)
- **table:** fix table jitter problem ([8eba7fb](https://github.com/anncwb/vue-vben-admin/commit/8eba7fb52786d1977e4cb7b67673d74c91c5c827))
- **table:** fix tree node align ([1e61da6](https://github.com/anncwb/vue-vben-admin/commit/1e61da644f65a79ce10fde98ee017aba7d36be10)), closes [#829](https://github.com/anncwb/vue-vben-admin/issues/829)
- **table:** getDataSource not worked on empty data ([e78af6f](https://github.com/anncwb/vue-vben-admin/commit/e78af6f228e25f052dc4c5a1859a6db50e0b112e)), closes [#752](https://github.com/anncwb/vue-vben-admin/issues/752)
- **table:** global configuration accidentally modified ([b4a3f93](https://github.com/anncwb/vue-vben-admin/commit/b4a3f936cd19bf1fff3a331bacad60e79d2d6c22))
- **table:** param of `handleSearchInfoFn` ([791b323](https://github.com/anncwb/vue-vben-admin/commit/791b323dbd30acd7fabfe9c3fb6e528916311ffd))
- **table:** recursive updateTableDataRecord ([#1024](https://github.com/anncwb/vue-vben-admin/issues/1024)) ([72f953c](https://github.com/anncwb/vue-vben-admin/commit/72f953c8d3413a7f5482793258503017a81cc759))
- auto remove script dom in `useScript` ([a544dd3](https://github.com/anncwb/vue-vben-admin/commit/a544dd3e589329339177dad3d5c1f75dd6e6f0ca))
- fix iframe heigth error ([#1012](https://github.com/anncwb/vue-vben-admin/issues/1012)) ([d76cfd7](https://github.com/anncwb/vue-vben-admin/commit/d76cfd7f809ba48880c950a64cb43a5c9c44176c))
- Fix the invalid hot update of BasicButton when changing style outside ([#1016](https://github.com/anncwb/vue-vben-admin/issues/1016)) ([be2d11d](https://github.com/anncwb/vue-vben-admin/commit/be2d11d5d344a508e94abe3534726c80e1f1f271))
- style property of actionColOpt is invalid ([#997](https://github.com/anncwb/vue-vben-admin/issues/997)) ([225bd4c](https://github.com/anncwb/vue-vben-admin/commit/225bd4c39de377d93c605f33bfdf3d8fd565f12b))
- the position of tinymce upload image is wrong ([#1015](https://github.com/anncwb/vue-vben-admin/issues/1015)) ([2fd0fd2](https://github.com/anncwb/vue-vben-admin/commit/2fd0fd281e65d6d2551478c5f19250347dc14062))
- **api-select:** fix `options-change` event data ([897bed9](https://github.com/anncwb/vue-vben-admin/commit/897bed97295a0b9101d33102340749689a4368de))
- **api-tree-select:** auto load data if necessary ([1b3058f](https://github.com/anncwb/vue-vben-admin/commit/1b3058f8253effe974feaf08a12250a111ab58c0))
- **api-tree-select:** auto reload while `params` changed ([c734f68](https://github.com/anncwb/vue-vben-admin/commit/c734f6858daea6d11cd517463b06fcce58744947))
- **api-tree-select:** fix `event` checked in form ([d9d0071](https://github.com/anncwb/vue-vben-admin/commit/d9d00714011fa7914c61f990ce1159351ee21a1a))
- **basic-tree:** `checkedKeys` not worked with `search` ([b06a7ab](https://github.com/anncwb/vue-vben-admin/commit/b06a7ab77b99abee63dd55770ffd55b594ee42f9)), closes [#915](https://github.com/anncwb/vue-vben-admin/issues/915)
- **code-editor:** `value` not support use as `v-model` ([8832a07](https://github.com/anncwb/vue-vben-admin/commit/8832a074dceb44f057c87289d3a99feef58c08fd)), closes [#933](https://github.com/anncwb/vue-vben-admin/issues/933)
- **countdown-input:** add `slots` support ([a764a95](https://github.com/anncwb/vue-vben-admin/commit/a764a95ae9a6cff831f75aa97b00724cadc48e92))
- **dark-theme:** alert color in dark-theme ([9b7ede0](https://github.com/anncwb/vue-vben-admin/commit/9b7ede09b9efe4d5a15ab0cdeadac480a29c0f62))
- **dark-theme:** bgcolor of `selected tree node` in dark theme ([8cf004a](https://github.com/anncwb/vue-vben-admin/commit/8cf004a5f59895e2487c3a350c83000e585b897e)), closes [#949](https://github.com/anncwb/vue-vben-admin/issues/949)
- **dark-theme:** disabled link `button` color ([4281216](https://github.com/anncwb/vue-vben-admin/commit/42812162c46832ce4d3e332bd579c042309115bc))
- **dark-theme:** fixed `TreeSelect` & `DatePicker` theme ([d1e0e8b](https://github.com/anncwb/vue-vben-admin/commit/d1e0e8bcea1c168631222989969e14f7d0d1b6a4)), closes [#955](https://github.com/anncwb/vue-vben-admin/issues/955)
- **dark-theme:** style for checked tree nodes ([662b576](https://github.com/anncwb/vue-vben-admin/commit/662b576ac2088247cb58e295378f228462508a37))
- **demo:** account page form validation ([8702965](https://github.com/anncwb/vue-vben-admin/commit/87029650570e470431fb94d35a273c5d07a73135))
- **demo:** fix display problem of editable table with `apiSelect` ([535bddd](https://github.com/anncwb/vue-vben-admin/commit/535bdddf91785e20295c18cf80c8a22cc2172681))
- **demo:** fix roles mock data ([c375e32](https://github.com/anncwb/vue-vben-admin/commit/c375e32305eae5128e09ad1bda39ce0cc6afd790))
- **demo:** menu `error-log` link to 404 page ([341bd63](https://github.com/anncwb/vue-vben-admin/commit/341bd633d8ed38a5a357db8f97166c2eba2895d3))
- **demo:** multi-modal used with dynamic component ([e1c4723](https://github.com/anncwb/vue-vben-admin/commit/e1c47233edf7675aede6d5f023726945a510ddf7))
- **echarts:** fix graphic config cannot be used in echarts options ([#959](https://github.com/anncwb/vue-vben-admin/issues/959)) ([525484e](https://github.com/anncwb/vue-vben-admin/commit/525484e7a409b032d22231f90a92e700ef4290ae))
- **form:** fix `validate` promise catch ([571f281](https://github.com/anncwb/vue-vben-admin/commit/571f28138f782553eb39cda2d632e5ac1aa1e145))
- **form:** remove console error for `setFieldsValue` ([8d185bb](https://github.com/anncwb/vue-vben-admin/commit/8d185bb5841c83eb49c78e8342e65067aa6f9b80)), closes [#952](https://github.com/anncwb/vue-vben-admin/issues/952)
- **formItem:** Fix labelcol type mismatch ([#903](https://github.com/anncwb/vue-vben-admin/issues/903)) ([03b17a8](https://github.com/anncwb/vue-vben-admin/commit/03b17a8f8bdb50322aa10e3b614bcc40b9e9dcc8))
- **img-rotate-drag-verify:** fix `resume` method support ([32d64db](https://github.com/anncwb/vue-vben-admin/commit/32d64dbe816a0afda6ee9e91863199afb3e7b48e)), closes [#946](https://github.com/anncwb/vue-vben-admin/issues/946)
- **login:** fix `auto fill` style in dark-theme ([cebc6a5](https://github.com/anncwb/vue-vben-admin/commit/cebc6a590e1a19af7380a55aed43b23af274df0a))
- **modal:** ensure that props are passed correctly,fix [#897](https://github.com/anncwb/vue-vben-admin/issues/897) ([ae7821e](https://github.com/anncwb/vue-vben-admin/commit/ae7821e29690bea8934ea724bfd0ae4e2cf30c77))
- **modal:** fixed `fullscreen` not worked ([5baaa58](https://github.com/anncwb/vue-vben-admin/commit/5baaa58581f22a915cda9fa39e4cb9f094254d3b)), closes [#918](https://github.com/anncwb/vue-vben-admin/issues/918)
- **model:** auto validate on value change ([f844017](https://github.com/anncwb/vue-vben-admin/commit/f8440175f35076073c9f53483cf6c0164d427ff4)), closes [#920](https://github.com/anncwb/vue-vben-admin/issues/920)
- **multiple-tab:** ignore login page ([1e63379](https://github.com/anncwb/vue-vben-admin/commit/1e63379088e1d7c823f29f607ab49d62ca22cb25))
- **page-wrapper:** fix `class` not working ([8879ae8](https://github.com/anncwb/vue-vben-admin/commit/8879ae8d773e8dc4c252c4234eefeab9bc135a30))
- **perm-guard:** Fix the problem that the routing query is lost after refreshing the page ([#941](https://github.com/anncwb/vue-vben-admin/issues/941)) ([9c4889f](https://github.com/anncwb/vue-vben-admin/commit/9c4889f0859bc60decf0ef40c383c1946de1d68a))
- **qrcode:** Fix the problem that the QR code cannot be dynamically generated ([#974](https://github.com/anncwb/vue-vben-admin/issues/974)) ([fe4eae3](https://github.com/anncwb/vue-vben-admin/commit/fe4eae37146068f01ba08f033e0c2e8bd03e48b5))
- **style:** fix checkbox-checked css in dark mode ([d3f08e3](https://github.com/anncwb/vue-vben-admin/commit/d3f08e37c5b6e46f33d525e9ef4b4c235d77a192))
- **table:** `value` show problem in editable cell ([61ce25b](https://github.com/anncwb/vue-vben-admin/commit/61ce25be1b40d7a0e26205ca6a6757c6c43fc21e)), closes [#922](https://github.com/anncwb/vue-vben-admin/issues/922)
- **table:** component shown in `fullscreen` mode ([a07ab6d](https://github.com/anncwb/vue-vben-admin/commit/a07ab6d7aa1060f856649a9bdbec9dfa50b14f26))
- **table:** editable cell display with validation ([202aa42](https://github.com/anncwb/vue-vben-admin/commit/202aa42b8d5a94e84ad386bcf7feab96926c70dd)), closes [#953](https://github.com/anncwb/vue-vben-admin/issues/953)
- **table:** fix `dataPicker` show in `fullscreen` mode ([a5a9b3f](https://github.com/anncwb/vue-vben-admin/commit/a5a9b3fb34c64b6ea9c9ab3d58045f6e5963952b))
- **table:** fix `getSelectRows` for treeTable ([f2b8bb4](https://github.com/anncwb/vue-vben-admin/commit/f2b8bb43a0b9172b9ef9ced8e83bf91143a091d9)), closes [#1003](https://github.com/anncwb/vue-vben-admin/issues/1003)
- **table:** fix `pagination` props working ([e327893](https://github.com/anncwb/vue-vben-admin/commit/e32789373eb5b1b531572b59692bf552dac365dc))
- **table:** fix editable cell not support `ellipsis` ([4bb506f](https://github.com/anncwb/vue-vben-admin/commit/4bb506fb1f6ac7d246f8792d29f337ec003ff426)), closes [#944](https://github.com/anncwb/vue-vben-admin/issues/944)
- **table:** fix expand style ([14fb21d](https://github.com/anncwb/vue-vben-admin/commit/14fb21d0b7b9ac69c7b3c463de6d709bd5713d14)), closes [#969](https://github.com/anncwb/vue-vben-admin/issues/969)
- **table:** fix tableSettings popup in fullscreen mode ([dce3fb0](https://github.com/anncwb/vue-vben-admin/commit/dce3fb0f20516aaf4817281f5d08109e53a73ecb))
- **tree:** fix `checkAll` effects `disabled` node ([ddd1893](https://github.com/anncwb/vue-vben-admin/commit/ddd1893b113e13786037522341abb2e75f8f9d5b))
- ensure PAGE_NOT_FOUND_ROUTE exist ([87583c8](https://github.com/anncwb/vue-vben-admin/commit/87583c8b54d335ddf1c416859ef62bbde189c809))
- expandIcon slot of BasicTable component is invalid ([#975](https://github.com/anncwb/vue-vben-admin/issues/975)) ([98c206d](https://github.com/anncwb/vue-vben-admin/commit/98c206d9c9661a18dde4ec7782cbe1eb6e62ebeb))
- fix homePage affix error ([c117802](https://github.com/anncwb/vue-vben-admin/commit/c1178027f0fab2791d02efcd7c52beff5fc7dc25))
- Fix vite profile hot update error reporting ([#968](https://github.com/anncwb/vue-vben-admin/issues/968)) ([956ed2e](https://github.com/anncwb/vue-vben-admin/commit/956ed2e3f770cc9cf822ce80f71b1e7f179792fb))
- fixed moment locale config ([27207a7](https://github.com/anncwb/vue-vben-admin/commit/27207a78caccb04372e0275c5cee526ec460de0e))
- infinite redirect in `BACK` mode ([4b46a84](https://github.com/anncwb/vue-vben-admin/commit/4b46a84c2b85e8da799426c54b3381ae93183db4))
- resolving `Vue Router warn` ([237e65e](https://github.com/anncwb/vue-vben-admin/commit/237e65eac909368c4b4857da6c8deb1dc18e7684))
- typo ([#980](https://github.com/anncwb/vue-vben-admin/issues/980)) ([7e6a89f](https://github.com/anncwb/vue-vben-admin/commit/7e6a89ffeb8c63467908d5807d3d7c4761620ee3))
- typo for utils/env ([#1004](https://github.com/anncwb/vue-vben-admin/issues/1004)) ([e8eefd1](https://github.com/anncwb/vue-vben-admin/commit/e8eefd1bca41c181ec6395bf1d087d2018e2b1f1))
- **table:** selection-change not triggered on row click ([6f845b5](https://github.com/anncwb/vue-vben-admin/commit/6f845b53bdc4c33fbca3e65f10f64c63166bed0e))
- **table:** treeTable editable error ([4ae39c5](https://github.com/anncwb/vue-vben-admin/commit/4ae39c53b49532fc6c31086a31e30429d2e236ed)), closes [#811](https://github.com/anncwb/vue-vben-admin/issues/811)
- **table-action:** fix `circle` button style ([db7254a](https://github.com/anncwb/vue-vben-admin/commit/db7254a5e0ac6d10a7ea37334ad523b150facb19))
- **table-action:** fixed icon `margin` without label ([dc51e6a](https://github.com/anncwb/vue-vben-admin/commit/dc51e6a8d4e4f2c97b387b37959944c9bb49d779))
- **table-action:** incorrect button color of `disabled` state ([0f28e80](https://github.com/anncwb/vue-vben-admin/commit/0f28e803d0b65537216cd9f40ad5cad63c20db9b)), closes [#891](https://github.com/anncwb/vue-vben-admin/issues/891)
- **table-action:** stopButtonPropagation not working ([9b8f165](https://github.com/anncwb/vue-vben-admin/commit/9b8f165a365758d001e6d86ae7afe4ae3316d485))
- **tree:** fixed `checkedKeys` with `search` mode ([f707541](https://github.com/anncwb/vue-vben-admin/commit/f707541dda78146bda89814ddccbb259d9f5d8a2))
- **upload:** ensure the value type is correct ([05329ce](https://github.com/anncwb/vue-vben-admin/commit/05329ce9501eb899a0bbb45320e5807c83372317))
- **useWatermark:** fix `func` call `createWatermark` call `clear` to resizeEvent removed ([#901](https://github.com/anncwb/vue-vben-admin/issues/901)) ([a1d956d](https://github.com/anncwb/vue-vben-admin/commit/a1d956d3697cd07e0ba8910768f2a73e55f18491))
- `menuSetting` can not set collapsed to false as default ([808291b](https://github.com/anncwb/vue-vben-admin/commit/808291b503d59e3026f5f0b5e7a38b9c69bcc451))
- ensure that safari is running properly, fix [#875](https://github.com/anncwb/vue-vben-admin/issues/875) ([dafcdd8](https://github.com/anncwb/vue-vben-admin/commit/dafcdd898caae57104f1155b0ec660ea333e7b19))
- **table:** scrollbar style ([d8c3820](https://github.com/anncwb/vue-vben-admin/commit/d8c38207c08510d805a8dc66ffbba210e0cf4215))
- **tailwindcss:** remove console warnings ([acacb32](https://github.com/anncwb/vue-vben-admin/commit/acacb32bb592345cd0a90b4bbeb60a9b6ab1ac3c))
- `hasPermission` not work in `ROLE` Mode ([76a5f87](https://github.com/anncwb/vue-vben-admin/commit/76a5f87c0ce871cca48b9e4c32331353a796e7d2))
- fix antdv console warning ([480cfb9](https://github.com/anncwb/vue-vben-admin/commit/480cfb914e78c06eb7784e33465ed91b7d4c3eee))
- fix defHttp baseUrl work ([d5f9919](https://github.com/anncwb/vue-vben-admin/commit/d5f9919b60fdd7d5c435129e8db519c0bbd37529))
- multi windows token sharing ([e5f3788](https://github.com/anncwb/vue-vben-admin/commit/e5f37885ffb32d04d244f0ef39ac660dda6b71e1)), closes [#761](https://github.com/anncwb/vue-vben-admin/issues/761)
- routes filter can't effective when permission mode set to ROUTE_MAPPING ([#836](https://github.com/anncwb/vue-vben-admin/issues/836)) ([3871204](https://github.com/anncwb/vue-vben-admin/commit/3871204d08d481b8984440cd60bbf2bacb58d063))
- support various vite modes of build, not just production ([#832](https://github.com/anncwb/vue-vben-admin/issues/832)) ([95c16a5](https://github.com/anncwb/vue-vben-admin/commit/95c16a5d26f9fd9a1d11894afe1146ca495eee93))
- user drop-down event key loss ([20d7a25](https://github.com/anncwb/vue-vben-admin/commit/20d7a25eb898a5c28351ff269b93bf104b8ac10e))
- user dropdown event response failure ([c73694a](https://github.com/anncwb/vue-vben-admin/commit/c73694ab8b0b6242c4d5e0f30bc7ebe3d69b4e33))
- **upload:** make sure to carry custom parameters, fix [#802](https://github.com/anncwb/vue-vben-admin/issues/802) ([c4b22a2](https://github.com/anncwb/vue-vben-admin/commit/c4b22a225d0088d87be0c0068f543366312521db))
- **utils:** The date function gets a non-date when the parameter is null ([#954](https://github.com/anncwb/vue-vben-admin/issues/954)) ([350c85a](https://github.com/anncwb/vue-vben-admin/commit/350c85accf5033cc5a21b71bc36d5b7a74eb2573))
### Features
- **use-loading:** add `setTip` method ([26d9476](https://github.com/anncwb/vue-vben-admin/commit/26d9476caff41cc355190604af42e0bd2ef0a353))
- Added support for tailwindcss night mode mechanism ([#998](https://github.com/anncwb/vue-vben-admin/issues/998)) ([189bc6f](https://github.com/anncwb/vue-vben-admin/commit/189bc6feb3f2860be8c531dd1ca996f3a2cff018))
- **api-select:** clear options before fetch ([9cf070d](https://github.com/anncwb/vue-vben-admin/commit/9cf070dd6305bb69a67ab6be85ef00bddc86fda0))
- **api-tree-select:** add `api` options to tree-select ([d81db89](https://github.com/anncwb/vue-vben-admin/commit/d81db890dfeb533d60f378ddb86f8ac50a31252b))
- **avatar-cropper:** add action tooltip ([6cbac4b](https://github.com/anncwb/vue-vben-admin/commit/6cbac4b7ece60a1a7c1fda931cfffce42dfe3e51))
- **avatar-cropper:** more props added ([b96ea07](https://github.com/anncwb/vue-vben-admin/commit/b96ea0753bfd769693a368cf1e3d8316688c0dcb))
- **axios:** add `withToken` option ([c99cf5e](https://github.com/anncwb/vue-vben-admin/commit/c99cf5e53f057cdc332ab6c0635adf9c2d27de29))
- **axios:** use `defHttp` like `axios` ([49f39de](https://github.com/anncwb/vue-vben-admin/commit/49f39de7b40e3ec8343bdeaf3eb00fd79d395746)), closes [#850](https://github.com/anncwb/vue-vben-admin/issues/850)
- **basic-table:** add `ApiTreeSelect` edit component ([52af1dd](https://github.com/anncwb/vue-vben-admin/commit/52af1dd0d494e66c0af20f886dcc2b983cbb096f))
- **basic-upload:** `value` support v-model ([16c5d32](https://github.com/anncwb/vue-vben-admin/commit/16c5d327f1209f7c7437acde2ab0fa031da6a641))
- **basic-upload:** add preview-delete event ([49e72a8](https://github.com/anncwb/vue-vben-admin/commit/49e72a8e76b78fe54e19de9e23d7c72a19427f01)), closes [#835](https://github.com/anncwb/vue-vben-admin/issues/835)
- **demo:** add `async-validator` demo ([8b4b767](https://github.com/anncwb/vue-vben-admin/commit/8b4b767f4ca78f7c6f7586d8ba662552c2b7bb51))
- **demo:** add basicTree with async data expand all ([5421211](https://github.com/anncwb/vue-vben-admin/commit/542121129eb5bf65f61e7b484835591756c80f04))
- **demo:** add route multi tabs show ([0e414ba](https://github.com/anncwb/vue-vben-admin/commit/0e414ba3c10b4e47a85feb1a38cae66c815719d8)), closes [#817](https://github.com/anncwb/vue-vben-admin/issues/817)
- **demo:** add search demo for apiSelect ([41e6d94](https://github.com/anncwb/vue-vben-admin/commit/41e6d94b3b64dc0d40b7ec57ecfaa4d966f202ae))
- **demo:** demo default expanded tree table ([5f1a6cd](https://github.com/anncwb/vue-vben-admin/commit/5f1a6cdc599d5840df2dfebdaad029aac093cd81))
- **demo:** multi-modal in one page usage ([7a7dab0](https://github.com/anncwb/vue-vben-admin/commit/7a7dab0c4b3602b7bd3e9381408e4168d7494c52))
- **menu:** the route is automatically mapped to the menu ([913c22c](https://github.com/anncwb/vue-vben-admin/commit/913c22c84fc9a4221fdfff6bae0e79a68fd09b17))
- **modal:** add `tooltip` for action buttons ([c3b9076](https://github.com/anncwb/vue-vben-admin/commit/c3b907656a5fad7a9b241562179f7a0f6fe0e6f0))
- **notice-list:** add `pagination` support ([c16be2c](https://github.com/anncwb/vue-vben-admin/commit/c16be2c499d90126dfa35d699da9294c21a4ab48)), closes [#894](https://github.com/anncwb/vue-vben-admin/issues/894)
- **preview:** add more features ([e23bd26](https://github.com/anncwb/vue-vben-admin/commit/e23bd2696da945291a9b652f1af39ad1936f376b))
- customized user home page ([0a3683a](https://github.com/anncwb/vue-vben-admin/commit/0a3683a186ab55d34a12a5a3c6d794dfa1094ad4))
- **param-menu:** feature: menu with params ([#845](https://github.com/anncwb/vue-vben-admin/issues/845)) ([48fcd76](https://github.com/anncwb/vue-vben-admin/commit/48fcd7684cabff66e8648b71527c6cb4ce7d03be))
- **route:** add `hidePathForChildren` in `meta` ([d52b0de](https://github.com/anncwb/vue-vben-admin/commit/d52b0de83e69f7505c28e6f59ec84bbe526ecd0d))
- **table:** add `headerTop` slot ([540423e](https://github.com/anncwb/vue-vben-admin/commit/540423ecf741a815d28d7a6baa1541ac884efe95)), closes [#881](https://github.com/anncwb/vue-vben-admin/issues/881)
- **table:** support asynchrony in beforeFetch and afterFetch ([#827](https://github.com/anncwb/vue-vben-admin/issues/827)) ([749ba5c](https://github.com/anncwb/vue-vben-admin/commit/749ba5c1daf459625518937c239787b756c0a780))
- **table-action:** support `tooltip` option ([5fab267](https://github.com/anncwb/vue-vben-admin/commit/5fab267a69600fdf5d7a7f9e4d9fff859d09dede)), closes [#848](https://github.com/anncwb/vue-vben-admin/issues/848)
- **tree:** add `insertNodesByKey` method ([5a20df4](https://github.com/anncwb/vue-vben-admin/commit/5a20df45ad36b523d48bf7fe11bdb10a6d03df64))
- add Tree LoadData demo ([9298b3c](https://github.com/anncwb/vue-vben-admin/commit/9298b3c988c10b81d83430ca31b9ce1d98a3fad9))
- routers support `ignoreRoute` option ([72ac240](https://github.com/anncwb/vue-vben-admin/commit/72ac240f2858cd74cb62b7647ca89d63bb71d247))
### Performance Improvements
- improve legacy compatibility ([e2664f6](https://github.com/anncwb/vue-vben-admin/commit/e2664f60029f03642f8b1a6afa9b1998705fce37))
- **menu:** Optimize the style of the bottom collapse button in the Mix menu layout ([#896](https://github.com/anncwb/vue-vben-admin/issues/896)) ([6f83070](https://github.com/anncwb/vue-vben-admin/commit/6f830703a2607c33e5d25d6d17d0e453fc2fac2e))
- image compression configuration optimization ([cf840e3](https://github.com/anncwb/vue-vben-admin/commit/cf840e3e73b9572de0ba7bf7b32d83f6a353a8ad))
- **icon:** remove Icon component global registration ([59d3e8c](https://github.com/anncwb/vue-vben-admin/commit/59d3e8c80f72f029f2b90432b31901ad54ed1ee4))
- **pagewrapper:** 优化 PageWrapper 的高度自适应表现使用 getViewportOffset 替代 useContentViewHeight ([#792](https://github.com/anncwb/vue-vben-admin/issues/792)) ([4d8e398](https://github.com/anncwb/vue-vben-admin/commit/4d8e39857ea59fff99e69832b4a8cabf3a424c24))
- **router:** reduce the number of guard files ([327d71b](https://github.com/anncwb/vue-vben-admin/commit/327d71b8fb4907ae971d040f6b84bbecb0a6d897))
- **scrollbar:** scrollbar update when slot changed ([e9e51b2](https://github.com/anncwb/vue-vben-admin/commit/e9e51b2fdc879a66d8df08504a0955c9c21e3e27))
### Reverts
- **axios:** remove baseUrl config ([61d4efd](https://github.com/anncwb/vue-vben-admin/commit/61d4efd55a8b4f09990b5f1888e23ead43958164))
## [2.6.1](https://github.com/anncwb/vue-vben-admin/compare/v2.6.0...v2.6.1) (2021-07-19) ## [2.6.1](https://github.com/anncwb/vue-vben-admin/compare/v2.6.0...v2.6.1) (2021-07-19)
### Bug Fixes ### Bug Fixes

View File

@ -1,3 +1,62 @@
## 2.7.1(2021-08-16)
- 升级 vue 3.2,如果运行失败,删除 node_modules 后重装即可
### ✨ Features
- **BasicTree** 添加搜索功能相关属性和方法
- **BasicForm** 新增`alwaysShowLines`用于设置折叠时保留显示的行数
### 🐛 Bug Fixes
- **Cropper** 修复未能及时销毁的问题
- **BasicTable**
- 修复`CellFormat`无法使用`Map`类型数据的问题
- 修复可编辑单元格未能正确显示`0`值的问题
- 修复 selection-change 事件在取消勾选时未能正确触发的问题
- 修复浅色主题下的全屏状态背景颜色不正确的问题
- 修复`getSelectRows`不支持远程数据跨页选择时获取完整数据的问题
- 修复在`editComponentProps`中为编辑组件提供的`size`属性无效的问题
- **Qrcode** 修复二维码组件在创建时未能及时绘制的问题
- **BasicModal** 修复`helpMessage`属性不起作用的问题
- **BasicButton** 修复按钮样式表现与 antd 官方不一致的问题
- **其它** 修复`useRedo`(重新加载当前路由)会丢失路由`params`数据的问题
## 2.7.0(2021-08-03)
## (破坏性更新) Breaking changes
- 将项目`tailwindcss`还原回`windicss`,尝试了`tailwindcss`,问题可能还挺多,先切换回`windicss`提高开发效率,切换成本较低。
- 目前项目不兼容地方有
- `xl:!m-4` 之类的写法需要改为`!xl:m-4`,注意只有`!`这个不兼容,没用到则不用改
- 内存溢出问题可能还在(频率低,重启下即可,重启 vite 较快)
### ✨ Features
- **Preview** 添加新的属性及事件
- **Dark Theme** 新增对 tailwindcss 夜间模式的支持
- **其它** 为 useLoading 添加 setTip 方法
### 🐛 Bug Fixes
- **ApiTreeSelect** 修复未能正确监听`params`变化的问题
- **ImgRotateDragVerify** 修复组件`resume`方法无法调用的问题
- **TableAction** 修复 stopButtonPropagation 属性某些情况下不起作用的问题
- **PageWrapper** 修复`class`属性无效的问题
- **BasicTree** 修复`checkAll`方法会影响到`disabled`状态节点的问题
- **BasicTable**
- 修复可编辑单元格不支持`ellipsis`配置的问题
- 修复全屏模式下看不到子组件弹出层popconfirm 以及 select、treeSelect 等编辑组件)的问题
- 修复启用`expandRowByClick`时,点击不可展开的行可能会导致样式错误的问题
- 修复`pagination`属性动态改变不生效的问题
- 修复`getSelectRows`不支持树形表格子级数据的问题
- **Dark Theme** 黑暗主题下的配色问题修正
- 修复`Tree`组件被选中节点的背景颜色
- 修复`Alert`组件的颜色配置
- 修复禁用状态下的`link`类型的按钮颜色问题
- 修复`Tree`已勾选的复选框的样式问题
- **其它** 修复 useScript 未能自动移除 script 节点的问题
## 2.6.1(2021-07-19) ## 2.6.1(2021-07-19)
### ✨ Features ### ✨ Features
@ -5,8 +64,10 @@
- **NoticeList** 添加分页、超长自动省略、标题点击事件、标题删除线等功能 - **NoticeList** 添加分页、超长自动省略、标题点击事件、标题删除线等功能
- **MixSider** 优化 Mix 菜单布局时 底部折叠按钮 的样式,与其它菜单布局时的风格保持一致 - **MixSider** 优化 Mix 菜单布局时 底部折叠按钮 的样式,与其它菜单布局时的风格保持一致
- **ApiTreeSelect** 扩展`antdv`的`TreeSelect`组件,支持远程数据源,用法类似`ApiSelect` - **ApiTreeSelect** 扩展`antdv`的`TreeSelect`组件,支持远程数据源,用法类似`ApiSelect`
- **BasicTable** 新增`ApiTreeSelect`编辑组件 - **BasicTable**
- 可以为不同的用户指定不同的后台首页: - 新增`ApiTreeSelect`编辑组件
- 新增`headerTop`插槽
- **其它** 可以为不同的用户指定不同的后台首页:
- 在`getUserInfo`接口返回的用户信息中增加`homePath`字段(可选)即可为当前用户定制首页路径 - 在`getUserInfo`接口返回的用户信息中增加`homePath`字段(可选)即可为当前用户定制首页路径
### 🐛 Bug Fixes ### 🐛 Bug Fixes
@ -14,7 +75,6 @@
- **BasicTable** - **BasicTable**
- 修复滚动条样式问题(移除了滚动样式补丁) - 修复滚动条样式问题(移除了滚动样式补丁)
- 修复树形表格的带有展开图标的单元格的内容对齐问题 - 修复树形表格的带有展开图标的单元格的内容对齐问题
- 新增`headerTop`插槽
- 修复操作列的按钮在 disabled 状态下的颜色显示 - 修复操作列的按钮在 disabled 状态下的颜色显示
- 修复可编辑单元格的值不能直接通过修改`dataSource`来更新显示的问题 - 修复可编辑单元格的值不能直接通过修改`dataSource`来更新显示的问题
- 修复使用`ApiSelect`编辑组件时的数据回显问题 - 修复使用`ApiSelect`编辑组件时的数据回显问题
@ -40,7 +100,7 @@
- **其它** - **其它**
- 修复菜单默认折叠的配置不起作用的问题 - 修复菜单默认折叠的配置不起作用的问题
- 修复`safari`浏览器报错导致网站打不开 - 修复`safari`浏览器报错导致网站打不开
- 修复在 window 上,拉取代码后 eslint 因 endOfLine 而错问题 - 修复在 window 上,拉取代码后 eslint 因 endOfLine 而错问题
- 修复因动态路由而产生的 `Vue Router warn` - 修复因动态路由而产生的 `Vue Router warn`
### 🎫 Chores ### 🎫 Chores

View File

@ -28,7 +28,7 @@ export function generateModifyVars(dark = false) {
'success-color': '#55D187', // Success color 'success-color': '#55D187', // Success color
'error-color': '#ED6F6F', // False color 'error-color': '#ED6F6F', // False color
'warning-color': '#EFBD47', // Warning color 'warning-color': '#EFBD47', // Warning color
'border-color-base': '#EEEEEE', //'border-color-base': '#EEEEEE',
'font-size-base': '14px', // Main font size 'font-size-base': '14px', // Main font size
'border-radius-base': '2px', // Component/float fillet 'border-radius-base': '2px', // Component/float fillet
'link-color': primary, // Link color 'link-color': primary, // Link color

View File

@ -11,7 +11,7 @@ export const runBuild = async () => {
// Generate configuration file // Generate configuration file
if (!argvList.includes('disabled-config')) { if (!argvList.includes('disabled-config')) {
await runBuildConfig(); runBuildConfig();
} }
console.log(`${chalk.cyan(`[${pkg.name}]`)}` + ' - build successfully!'); console.log(`${chalk.cyan(`[${pkg.name}]`)}` + ' - build successfully!');

View File

@ -31,10 +31,16 @@ export function wrapperEnv(envConf: Recordable): ViteEnv {
if (envName === 'VITE_PROXY') { if (envName === 'VITE_PROXY') {
try { try {
realName = JSON.parse(realName); realName = JSON.parse(realName);
} catch (error) {} } catch (error) {
realName = '';
}
} }
ret[envName] = realName; ret[envName] = realName;
process.env[envName] = realName; if (typeof realName === 'string') {
process.env[envName] = realName;
} else if (typeof realName === 'object') {
process.env[envName] = JSON.stringify(realName);
}
} }
return ret; return ret;
} }

View File

@ -5,7 +5,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx';
import legacy from '@vitejs/plugin-legacy'; import legacy from '@vitejs/plugin-legacy';
import purgeIcons from 'vite-plugin-purge-icons'; import purgeIcons from 'vite-plugin-purge-icons';
import windiCSS from 'vite-plugin-windicss';
import { configHtmlPlugin } from './html'; import { configHtmlPlugin } from './html';
import { configPwaConfig } from './pwa'; import { configPwaConfig } from './pwa';
import { configMockPlugin } from './mock'; import { configMockPlugin } from './mock';
@ -33,6 +33,9 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
vueJsx(), vueJsx(),
]; ];
// vite-plugin-windicss
vitePlugins.push(windiCSS());
// TODO // TODO
!isBuild && vitePlugins.push(configHmrPlugin()); !isBuild && vitePlugins.push(configHmrPlugin());

View File

@ -33,14 +33,22 @@ export function configThemePlugin(isBuild: boolean): Plugin[] {
return s; return s;
case '.ant-steps-item-icon > .ant-steps-icon': case '.ant-steps-item-icon > .ant-steps-icon':
return s; return s;
case '.ant-select-item-option-selected:not(.ant-select-item-option-disabled)':
return s;
default:
if (s.indexOf('.ant-btn') >= -1) {
// 按钮被重新定制过需要过滤掉class防止覆盖
return s;
}
} }
return `[data-theme] ${s}`; return s.startsWith('[data-theme') ? s : `[data-theme] ${s}`;
}, },
colorVariables: [...getThemeColors(), ...colors], colorVariables: [...getThemeColors(), ...colors],
}), }),
antdDarkThemePlugin({ antdDarkThemePlugin({
preloadFiles: [ preloadFiles: [
path.resolve(process.cwd(), 'node_modules/ant-design-vue/dist/antd.less'), path.resolve(process.cwd(), 'node_modules/ant-design-vue/dist/antd.less'),
//path.resolve(process.cwd(), 'node_modules/ant-design-vue/dist/antd.dark.less'),
path.resolve(process.cwd(), 'src/design/index.less'), path.resolve(process.cwd(), 'src/design/index.less'),
], ],
filter: (id) => (isBuild ? !id.endsWith('antd.less') : true), filter: (id) => (isBuild ? !id.endsWith('antd.less') : true),
@ -48,8 +56,10 @@ export function configThemePlugin(isBuild: boolean): Plugin[] {
darkModifyVars: { darkModifyVars: {
...generateModifyVars(true), ...generateModifyVars(true),
'text-color': '#c9d1d9', 'text-color': '#c9d1d9',
'primary-1': 'rgb(255 255 255 / 8%)',
'text-color-base': '#c9d1d9', 'text-color-base': '#c9d1d9',
'component-background': '#151515', 'component-background': '#151515',
'heading-color': 'rgb(255 255 255 / 65%)',
// black: '#0e1117', // black: '#0e1117',
// #8b949e // #8b949e
'text-color-secondary': '#8b949e', 'text-color-secondary': '#8b949e',
@ -57,6 +67,20 @@ export function configThemePlugin(isBuild: boolean): Plugin[] {
// 'border-color-split': '#30363d', // 'border-color-split': '#30363d',
'item-active-bg': '#111b26', 'item-active-bg': '#111b26',
'app-content-background': 'rgb(255 255 255 / 4%)', 'app-content-background': 'rgb(255 255 255 / 4%)',
'tree-node-selected-bg': '#11263c',
'alert-success-border-color': '#274916',
'alert-success-bg-color': '#162312',
'alert-success-icon-color': '#49aa19',
'alert-info-border-color': '#153450',
'alert-info-bg-color': '#111b26',
'alert-info-icon-color': '#177ddc',
'alert-warning-border-color': '#594214',
'alert-warning-bg-color': '#2b2111',
'alert-warning-icon-color': '#d89614',
'alert-error-border-color': '#58181c',
'alert-error-bg-color': '#2a1215',
'alert-error-icon-color': '#a61d24',
}, },
}), }),
]; ];

View File

@ -1,5 +1,5 @@
import { MockMethod } from 'vite-plugin-mock'; import { MockMethod } from 'vite-plugin-mock';
import { resultPageSuccess, resultSuccess } from '../_util'; import { resultError, resultPageSuccess, resultSuccess } from '../_util';
const accountList = (() => { const accountList = (() => {
const result: any[] = []; const result: any[] = [];
@ -28,6 +28,7 @@ const roleList = (() => {
roleValue: '@first', roleValue: '@first',
createTime: '@datetime', createTime: '@datetime',
remark: '@cword(10,20)', remark: '@cword(10,20)',
menu: [['0', '1', '2'], ['0', '1'], ['0', '2'], ['2']][index],
'status|1': ['0', '1'], 'status|1': ['0', '1'],
}); });
} }
@ -185,4 +186,17 @@ export default [
return resultSuccess(menuList); return resultSuccess(menuList);
}, },
}, },
{
url: '/basic-api/system/accountExist',
timeout: 500,
method: 'post',
response: ({ body }) => {
const { account } = body || {};
if (account && account.indexOf('admin') !== -1) {
return resultError('该字段不能包含admin');
} else {
return resultSuccess(`${account} can use`);
}
},
},
] as MockMethod[]; ] as MockMethod[];

View File

@ -1,6 +1,6 @@
{ {
"name": "vben-admin", "name": "vben-admin",
"version": "2.6.1", "version": "2.7.1",
"author": { "author": {
"name": "vben", "name": "vben",
"email": "anncwb@126.com", "email": "anncwb@126.com",
@ -35,74 +35,75 @@
}, },
"dependencies": { "dependencies": {
"@iconify/iconify": "^2.0.3", "@iconify/iconify": "^2.0.3",
"@logicflow/core": "^0.6.1", "@logicflow/core": "^0.6.9",
"@logicflow/extension": "^0.6.1", "@logicflow/extension": "^0.6.9",
"@vueuse/core": "^5.1.4", "@vueuse/core": "^5.3.0",
"@zxcvbn-ts/core": "^1.0.0-beta.0", "@zxcvbn-ts/core": "^1.0.0-beta.0",
"ant-design-vue": "2.2.2", "ant-design-vue": "2.2.6",
"axios": "^0.21.1", "axios": "^0.21.1",
"crypto-js": "^4.0.0", "crypto-js": "^4.1.1",
"echarts": "^5.1.2", "echarts": "^5.1.2",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"path-to-regexp": "^6.2.0", "path-to-regexp": "^6.2.0",
"pinia": "^2.0.0-beta.5", "pinia": "2.0.0-rc.4",
"print-js": "^1.6.0",
"qrcode": "^1.4.4", "qrcode": "^1.4.4",
"resize-observer-polyfill": "^1.5.1", "resize-observer-polyfill": "^1.5.1",
"sortablejs": "^1.14.0", "sortablejs": "^1.14.0",
"vue": "3.1.5", "vue": "3.2.2",
"vue-i18n": "9.1.6", "vue-i18n": "9.1.7",
"vue-router": "^4.0.10", "vue-router": "^4.0.11",
"vue-types": "^4.0.0" "vue-types": "^4.0.3"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^12.1.4", "@commitlint/cli": "^13.1.0",
"@commitlint/config-conventional": "^12.1.4", "@commitlint/config-conventional": "^13.1.0",
"@iconify/json": "^1.1.374", "@iconify/json": "^1.1.386",
"@purge-icons/generated": "^0.7.0", "@purge-icons/generated": "^0.7.0",
"@types/codemirror": "^5.60.2", "@types/codemirror": "^5.60.2",
"@types/crypto-js": "^4.0.2", "@types/crypto-js": "^4.0.2",
"@types/fs-extra": "^9.0.12", "@types/fs-extra": "^9.0.12",
"@types/inquirer": "^7.3.3", "@types/inquirer": "^7.3.3",
"@types/intro.js": "^3.0.2", "@types/intro.js": "^3.0.2",
"@types/jest": "^26.0.24", "@types/jest": "^27.0.1",
"@types/lodash-es": "^4.17.4", "@types/lodash-es": "^4.17.4",
"@types/mockjs": "^1.0.4", "@types/mockjs": "^1.0.4",
"@types/node": "^16.3.3", "@types/node": "^16.6.1",
"@types/nprogress": "^0.2.0", "@types/nprogress": "^0.2.0",
"@types/qrcode": "^1.4.1", "@types/qrcode": "^1.4.1",
"@types/qs": "^6.9.7", "@types/qs": "^6.9.7",
"@types/sortablejs": "^1.10.7", "@types/sortablejs": "^1.10.7",
"@typescript-eslint/eslint-plugin": "^4.28.3", "@typescript-eslint/eslint-plugin": "^4.29.1",
"@typescript-eslint/parser": "^4.28.3", "@typescript-eslint/parser": "^4.29.1",
"@vitejs/plugin-legacy": "^1.4.4", "@vitejs/plugin-legacy": "^1.5.1",
"@vitejs/plugin-vue": "^1.2.5", "@vitejs/plugin-vue": "^1.4.0",
"@vitejs/plugin-vue-jsx": "^1.1.6", "@vitejs/plugin-vue-jsx": "^1.1.7",
"@vue/compiler-sfc": "3.1.5", "@vue/compiler-sfc": "3.2.2",
"@vue/test-utils": "^2.0.0-rc.10", "@vue/test-utils": "^2.0.0-rc.12",
"autoprefixer": "^10.3.1", "autoprefixer": "^10.3.1",
"commitizen": "^4.2.4", "commitizen": "^4.2.4",
"conventional-changelog-cli": "^2.1.1", "conventional-changelog-cli": "^2.1.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"dotenv": "^10.0.0", "dotenv": "^10.0.0",
"eslint": "^7.31.0", "eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.3.0",
"eslint-define-config": "^1.0.9", "eslint-define-config": "^1.0.9",
"eslint-plugin-jest": "^24.3.6", "eslint-plugin-jest": "^24.4.0",
"eslint-plugin-prettier": "^3.4.0", "eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-vue": "^7.14.0", "eslint-plugin-vue": "^7.16.0",
"esno": "^0.7.3", "esno": "^0.8.0",
"fs-extra": "^10.0.0", "fs-extra": "^10.0.0",
"http-server": "^0.12.3", "http-server": "^13.0.0",
"husky": "^7.0.1", "husky": "^7.0.1",
"inquirer": "^8.1.2", "inquirer": "^8.1.2",
"is-ci": "^3.0.0", "is-ci": "^3.0.0",
"jest": "^27.0.6", "jest": "^27.0.6",
"less": "^4.1.1", "less": "^4.1.1",
"lint-staged": "^11.0.1", "lint-staged": "^11.1.2",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"postcss": "^8.3.5", "postcss": "^8.3.6",
"prettier": "^2.3.2", "prettier": "^2.3.2",
"pretty-quick": "^3.1.1", "pretty-quick": "^3.1.1",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
@ -111,27 +112,27 @@
"stylelint-config-prettier": "^8.0.2", "stylelint-config-prettier": "^8.0.2",
"stylelint-config-standard": "^22.0.0", "stylelint-config-standard": "^22.0.0",
"stylelint-order": "^4.1.0", "stylelint-order": "^4.1.0",
"tailwindcss": "^2.2.4", "ts-jest": "^27.0.4",
"ts-jest": "^27.0.3", "ts-node": "^10.2.0",
"ts-node": "^10.1.0",
"typescript": "4.3.5", "typescript": "4.3.5",
"vite": "2.3.6", "vite": "2.5.0",
"vite-plugin-compression": "^0.3.1", "vite-plugin-compression": "^0.3.3",
"vite-plugin-html": "^2.0.7", "vite-plugin-html": "^2.0.7",
"vite-plugin-imagemin": "^0.4.1", "vite-plugin-imagemin": "^0.4.3",
"vite-plugin-mock": "^2.9.3", "vite-plugin-mock": "^2.9.4",
"vite-plugin-purge-icons": "^0.7.0", "vite-plugin-purge-icons": "^0.7.0",
"vite-plugin-pwa": "^0.8.1", "vite-plugin-pwa": "^0.10.0",
"vite-plugin-style-import": "^1.0.1", "vite-plugin-style-import": "^1.1.1",
"vite-plugin-svg-icons": "^1.0.1", "vite-plugin-svg-icons": "^1.0.4",
"vite-plugin-theme": "^0.8.1", "vite-plugin-theme": "^0.8.1",
"vue-eslint-parser": "^7.9.0", "vite-plugin-windicss": "^1.2.7",
"vue-tsc": "^0.2.1" "vue-eslint-parser": "^7.10.0",
"vue-tsc": "^0.2.3"
}, },
"resolutions": { "resolutions": {
"//": "Used to install imagemin dependencies, because imagemin may not be installed in China. If it is abroad, you can delete it", "//": "Used to install imagemin dependencies, because imagemin may not be installed in China. If it is abroad, you can delete it",
"bin-wrapper": "npm:bin-wrapper-china", "bin-wrapper": "npm:bin-wrapper-china",
"rollup": "^2.53.2" "rollup": "^2.56.2"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -1,6 +1,5 @@
module.exports = { module.exports = {
plugins: { plugins: {
tailwindcss: {},
autoprefixer: {}, autoprefixer: {},
}, },
}; };

View File

@ -6,23 +6,14 @@
</ConfigProvider> </ConfigProvider>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent } from 'vue';
import { ConfigProvider } from 'ant-design-vue'; import { ConfigProvider } from 'ant-design-vue';
import { AppProvider } from '/@/components/Application'; import { AppProvider } from '/@/components/Application';
import { useTitle } from '/@/hooks/web/useTitle'; import { useTitle } from '/@/hooks/web/useTitle';
import { useLocale } from '/@/locales/useLocale'; import { useLocale } from '/@/locales/useLocale';
export default defineComponent({ // support Multi-language
name: 'App', const { getAntdLocale } = useLocale();
components: { ConfigProvider, AppProvider },
setup() {
useTitle();
// support Multi-language useTitle();
const { getAntdLocale } = useLocale();
return { getAntdLocale };
},
});
</script> </script>

View File

@ -5,8 +5,8 @@
<SvgIcon size="14" name="moon" /> <SvgIcon size="14" name="moon" />
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, computed, unref } from 'vue'; import { computed, unref } from 'vue';
import { SvgIcon } from '/@/components/Icon'; import { SvgIcon } from '/@/components/Icon';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { useRootSetting } from '/@/hooks/setting/useRootSetting'; import { useRootSetting } from '/@/hooks/setting/useRootSetting';
@ -14,39 +14,25 @@
import { updateDarkTheme } from '/@/logics/theme/dark'; import { updateDarkTheme } from '/@/logics/theme/dark';
import { ThemeEnum } from '/@/enums/appEnum'; import { ThemeEnum } from '/@/enums/appEnum';
export default defineComponent({ const { prefixCls } = useDesign('dark-switch');
name: 'DarkModeToggle', const { getDarkMode, setDarkMode, getShowDarkModeToggle } = useRootSetting();
components: { SvgIcon },
setup() {
const { prefixCls } = useDesign('dark-switch');
const { getDarkMode, setDarkMode, getShowDarkModeToggle } = useRootSetting();
const isDark = computed(() => getDarkMode.value === ThemeEnum.DARK); const isDark = computed(() => getDarkMode.value === ThemeEnum.DARK);
const getClass = computed(() => [ const getClass = computed(() => [
prefixCls, prefixCls,
{ {
[`${prefixCls}--dark`]: unref(isDark), [`${prefixCls}--dark`]: unref(isDark),
},
]);
function toggleDarkMode() {
const darkMode = getDarkMode.value === ThemeEnum.DARK ? ThemeEnum.LIGHT : ThemeEnum.DARK;
setDarkMode(darkMode);
updateDarkTheme(darkMode);
updateHeaderBgColor();
updateSidebarBgColor();
}
return {
getClass,
isDark,
prefixCls,
toggleDarkMode,
getShowDarkModeToggle,
};
}, },
}); ]);
function toggleDarkMode() {
const darkMode = getDarkMode.value === ThemeEnum.DARK ? ThemeEnum.LIGHT : ThemeEnum.DARK;
setDarkMode(darkMode);
updateDarkTheme(darkMode);
updateHeaderBgColor();
updateSidebarBgColor();
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@prefix-cls: ~'@{namespace}-dark-switch'; @prefix-cls: ~'@{namespace}-dark-switch';

View File

@ -17,16 +17,16 @@
</span> </span>
</Dropdown> </Dropdown>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import type { LocaleType } from '/#/config'; import type { LocaleType } from '/#/config';
import type { DropMenu } from '/@/components/Dropdown'; import type { DropMenu } from '/@/components/Dropdown';
import { defineComponent, ref, watchEffect, unref, computed } from 'vue'; import { ref, watchEffect, unref, computed } from 'vue';
import { Dropdown } from '/@/components/Dropdown'; import { Dropdown } from '/@/components/Dropdown';
import { Icon } from '/@/components/Icon'; import { Icon } from '/@/components/Icon';
import { useLocale } from '/@/locales/useLocale'; import { useLocale } from '/@/locales/useLocale';
import { localeList } from '/@/settings/localeSetting'; import { localeList } from '/@/settings/localeSetting';
const props = { const props = defineProps({
/** /**
* Whether to display text * Whether to display text
*/ */
@ -35,45 +35,36 @@
* Whether to refresh the interface when changing * Whether to refresh the interface when changing
*/ */
reload: { type: Boolean }, reload: { type: Boolean },
};
export default defineComponent({
name: 'AppLocalPicker',
components: { Dropdown, Icon },
props,
setup(props) {
const selectedKeys = ref<string[]>([]);
const { changeLocale, getLocale } = useLocale();
const getLocaleText = computed(() => {
const key = selectedKeys.value[0];
if (!key) {
return '';
}
return localeList.find((item) => item.event === key)?.text;
});
watchEffect(() => {
selectedKeys.value = [unref(getLocale)];
});
async function toggleLocale(lang: LocaleType | string) {
await changeLocale(lang as LocaleType);
selectedKeys.value = [lang as string];
props.reload && location.reload();
}
function handleMenuEvent(menu: DropMenu) {
if (unref(getLocale) === menu.event) {
return;
}
toggleLocale(menu.event as string);
}
return { localeList, handleMenuEvent, selectedKeys, getLocaleText };
},
}); });
const selectedKeys = ref<string[]>([]);
const { changeLocale, getLocale } = useLocale();
const getLocaleText = computed(() => {
const key = selectedKeys.value[0];
if (!key) {
return '';
}
return localeList.find((item) => item.event === key)?.text;
});
watchEffect(() => {
selectedKeys.value = [unref(getLocale)];
});
async function toggleLocale(lang: LocaleType | string) {
await changeLocale(lang as LocaleType);
selectedKeys.value = [lang as string];
props.reload && location.reload();
}
function handleMenuEvent(menu: DropMenu) {
if (unref(getLocale) === menu.event) {
return;
}
toggleLocale(menu.event as string);
}
</script> </script>
<style lang="less"> <style lang="less">

View File

@ -10,8 +10,8 @@
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, computed, unref } from 'vue'; import { computed, unref } from 'vue';
import { useGlobSetting } from '/@/hooks/setting'; import { useGlobSetting } from '/@/hooks/setting';
import { useGo } from '/@/hooks/web/usePage'; import { useGo } from '/@/hooks/web/usePage';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
@ -19,11 +19,11 @@
import { PageEnum } from '/@/enums/pageEnum'; import { PageEnum } from '/@/enums/pageEnum';
import { useUserStore } from '/@/store/modules/user'; import { useUserStore } from '/@/store/modules/user';
const props = { const props = defineProps({
/** /**
* The theme of the current parent component * The theme of the current parent component
*/ */
theme: { type: String, validator: (v) => ['light', 'dark'].includes(v) }, theme: { type: String, validator: (v: string) => ['light', 'dark'].includes(v) },
/** /**
* Whether to show title * Whether to show title
*/ */
@ -32,45 +32,30 @@
* The title is also displayed when the menu is collapsed * The title is also displayed when the menu is collapsed
*/ */
alwaysShowTitle: { type: Boolean }, alwaysShowTitle: { type: Boolean },
};
export default defineComponent({
name: 'AppLogo',
props: props,
setup(props) {
const { prefixCls } = useDesign('app-logo');
const { getCollapsedShowTitle } = useMenuSetting();
const userStore = useUserStore();
const { title } = useGlobSetting();
const go = useGo();
const getAppLogoClass = computed(() => [
prefixCls,
props.theme,
{ 'collapsed-show-title': unref(getCollapsedShowTitle) },
]);
const getTitleClass = computed(() => [
`${prefixCls}__title`,
{
'xs:opacity-0': !props.alwaysShowTitle,
},
]);
function goHome() {
go(userStore.getUserInfo.homePath || PageEnum.BASE_HOME);
}
return {
getAppLogoClass,
getTitleClass,
getCollapsedShowTitle,
goHome,
title,
prefixCls,
};
},
}); });
const { prefixCls } = useDesign('app-logo');
const { getCollapsedShowTitle } = useMenuSetting();
const userStore = useUserStore();
const { title } = useGlobSetting();
const go = useGo();
const getAppLogoClass = computed(() => [
prefixCls,
props.theme,
{ 'collapsed-show-title': unref(getCollapsedShowTitle) },
]);
const getTitleClass = computed(() => [
`${prefixCls}__title`,
{
'xs:opacity-0': !props.alwaysShowTitle,
},
]);
function goHome() {
go(userStore.getUserInfo.homePath || PageEnum.BASE_HOME);
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@prefix-cls: ~'@{namespace}-app-logo'; @prefix-cls: ~'@{namespace}-app-logo';

View File

@ -10,20 +10,12 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent } from 'vue';
import AppSearchKeyItem from './AppSearchKeyItem.vue'; import AppSearchKeyItem from './AppSearchKeyItem.vue';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
export default defineComponent({ const { prefixCls } = useDesign('app-search-footer');
name: 'AppSearchFooter', const { t } = useI18n();
components: { AppSearchKeyItem },
setup() {
const { prefixCls } = useDesign('app-search-footer');
const { t } = useI18n();
return { prefixCls, t };
},
});
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@prefix-cls: ~'@{namespace}-app-search-footer'; @prefix-cls: ~'@{namespace}-app-search-footer';

View File

@ -3,11 +3,9 @@
<Icon :icon="icon" /> <Icon :icon="icon" />
</span> </span>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent } from 'vue';
import { Icon } from '/@/components/Icon'; import { Icon } from '/@/components/Icon';
export default defineComponent({ defineProps({
components: { Icon }, icon: String,
props: { icon: String },
}); });
</script> </script>

View File

@ -56,85 +56,61 @@
</transition> </transition>
</Teleport> </Teleport>
</template> </template>
<script lang="ts">
import { defineComponent, computed, unref, ref, watch, nextTick } from 'vue'; <script lang="ts" setup>
import { computed, unref, ref, watch, nextTick } from 'vue';
import { SearchOutlined } from '@ant-design/icons-vue'; import { SearchOutlined } from '@ant-design/icons-vue';
import AppSearchFooter from './AppSearchFooter.vue'; import AppSearchFooter from './AppSearchFooter.vue';
import Icon from '/@/components/Icon'; import Icon from '/@/components/Icon';
import clickOutside from '/@/directives/clickOutside'; import vClickOutside from '/@/directives/clickOutside';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { useRefs } from '/@/hooks/core/useRefs'; import { useRefs } from '/@/hooks/core/useRefs';
import { useMenuSearch } from './useMenuSearch'; import { useMenuSearch } from './useMenuSearch';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { useAppInject } from '/@/hooks/web/useAppInject'; import { useAppInject } from '/@/hooks/web/useAppInject';
const props = { const props = defineProps({
visible: { type: Boolean }, visible: { type: Boolean },
};
export default defineComponent({
name: 'AppSearchModal',
components: { Icon, SearchOutlined, AppSearchFooter },
directives: {
clickOutside,
},
props,
emits: ['close'],
setup(props, { emit }) {
const scrollWrap = ref<ElRef>(null);
const inputRef = ref<Nullable<HTMLElement>>(null);
const { t } = useI18n();
const { prefixCls } = useDesign('app-search-modal');
const [refs, setRefs] = useRefs();
const { getIsMobile } = useAppInject();
const { handleSearch, searchResult, keyword, activeIndex, handleEnter, handleMouseenter } =
useMenuSearch(refs, scrollWrap, emit);
const getIsNotData = computed(() => !keyword || unref(searchResult).length === 0);
const getClass = computed(() => {
return [
prefixCls,
{
[`${prefixCls}--mobile`]: unref(getIsMobile),
},
];
});
watch(
() => props.visible,
(visible: boolean) => {
visible &&
nextTick(() => {
unref(inputRef)?.focus();
});
}
);
function handleClose() {
searchResult.value = [];
emit('close');
}
return {
t,
prefixCls,
getClass,
handleSearch,
searchResult,
activeIndex,
getIsNotData,
handleEnter,
setRefs,
scrollWrap,
handleMouseenter,
handleClose,
inputRef,
};
},
}); });
const emit = defineEmits(['close']);
const scrollWrap = ref(null);
const inputRef = ref<Nullable<HTMLElement>>(null);
const { t } = useI18n();
const { prefixCls } = useDesign('app-search-modal');
const [refs, setRefs] = useRefs();
const { getIsMobile } = useAppInject();
const { handleSearch, searchResult, keyword, activeIndex, handleEnter, handleMouseenter } =
useMenuSearch(refs, scrollWrap, emit);
const getIsNotData = computed(() => !keyword || unref(searchResult).length === 0);
const getClass = computed(() => {
return [
prefixCls,
{
[`${prefixCls}--mobile`]: unref(getIsMobile),
},
];
});
watch(
() => props.visible,
(visible: boolean) => {
visible &&
nextTick(() => {
unref(inputRef)?.focus();
});
}
);
function handleClose() {
searchResult.value = [];
emit('close');
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@prefix-cls: ~'@{namespace}-app-search-modal'; @prefix-cls: ~'@{namespace}-app-search-modal';

View File

@ -7,12 +7,12 @@
<Icon icon="ion:chevron-forward" :style="$attrs.iconStyle" /> <Icon icon="ion:chevron-forward" :style="$attrs.iconStyle" />
</span> </span>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, computed } from 'vue'; import { computed } from 'vue';
import { Icon } from '/@/components/Icon'; import { Icon } from '/@/components/Icon';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
const props = { const props = defineProps({
/** /**
* Arrow expand state * Arrow expand state
*/ */
@ -29,31 +29,22 @@
* Cancel padding/margin for inline * Cancel padding/margin for inline
*/ */
inset: { type: Boolean }, inset: { type: Boolean },
}; });
export default defineComponent({ const { prefixCls } = useDesign('basic-arrow');
name: 'BasicArrow',
components: { Icon },
props,
setup(props) {
const { prefixCls } = useDesign('basic-arrow');
// get component class // get component class
const getClass = computed(() => { const getClass = computed(() => {
const { expand, up, down, inset } = props; const { expand, up, down, inset } = props;
return [ return [
prefixCls, prefixCls,
{ {
[`${prefixCls}--active`]: expand, [`${prefixCls}--active`]: expand,
up, up,
inset, inset,
down, down,
}, },
]; ];
});
return { getClass };
},
}); });
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@ -4,13 +4,13 @@
<BasicHelp :class="`${prefixCls}-help`" v-if="helpMessage" :text="helpMessage" /> <BasicHelp :class="`${prefixCls}-help`" v-if="helpMessage" :text="helpMessage" />
</span> </span>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { defineComponent, computed } from 'vue'; import { useSlots, computed } from 'vue';
import BasicHelp from './BasicHelp.vue'; import BasicHelp from './BasicHelp.vue';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
const props = { const props = defineProps({
/** /**
* Help text list or string * Help text list or string
* @default: '' * @default: ''
@ -29,24 +29,15 @@
* @default: false * @default: false
*/ */
normal: { type: Boolean }, normal: { type: Boolean },
};
export default defineComponent({
name: 'BasicTitle',
components: { BasicHelp },
props,
setup(props, { slots }) {
const { prefixCls } = useDesign('basic-title');
const getClass = computed(() => [
prefixCls,
{ [`${prefixCls}-show-span`]: props.span && slots.default },
{ [`${prefixCls}-normal`]: props.normal },
]);
return { prefixCls, getClass };
},
}); });
const { prefixCls } = useDesign('basic-title');
const slots = useSlots();
const getClass = computed(() => [
prefixCls,
{ [`${prefixCls}-show-span`]: props.span && slots.default },
{ [`${prefixCls}-normal`]: props.normal },
]);
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@prefix-cls: ~'@{namespace}-basic-title'; @prefix-cls: ~'@{namespace}-basic-title';

View File

@ -2,39 +2,38 @@
<Button v-bind="getBindValue" :class="getButtonClass" @click="onClick"> <Button v-bind="getBindValue" :class="getButtonClass" @click="onClick">
<template #default="data"> <template #default="data">
<Icon :icon="preIcon" v-if="preIcon" :size="iconSize" /> <Icon :icon="preIcon" v-if="preIcon" :size="iconSize" />
<slot v-bind="data"></slot> <slot v-bind="data || {}"></slot>
<Icon :icon="postIcon" v-if="postIcon" :size="iconSize" /> <Icon :icon="postIcon" v-if="postIcon" :size="iconSize" />
</template> </template>
</Button> </Button>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, computed } from 'vue'; export default {
name: 'AButton',
inheritAttrs: false,
};
</script>
<script lang="ts" setup>
import { computed, unref } from 'vue';
import { Button } from 'ant-design-vue'; import { Button } from 'ant-design-vue';
import Icon from '/@/components/Icon/src/Icon.vue'; import Icon from '/@/components/Icon/src/Icon.vue';
import { buttonProps } from './props'; import { buttonProps } from './props';
import { useAttrs } from '/@/hooks/core/useAttrs';
export default defineComponent({ const props = defineProps(buttonProps);
name: 'AButton', // get component class
components: { Button, Icon }, const attrs = useAttrs({ excludeDefaultKeys: false });
inheritAttrs: false, const getButtonClass = computed(() => {
props: buttonProps, const { color, disabled } = props;
setup(props, { attrs }) { return [
// get component class {
const getButtonClass = computed(() => { [`ant-btn-${color}`]: !!color,
const { color, disabled } = props; [`is-disabled`]: disabled,
return [ },
{ ];
[`ant-btn-${color}`]: !!color,
[`is-disabled`]: disabled,
},
attrs.class,
];
});
// get inherit binding value
const getBindValue = computed(() => ({ ...attrs, ...props }));
return { getBindValue, getButtonClass };
},
}); });
// get inherit binding value
const getBindValue = computed(() => ({ ...unref(attrs), ...props }));
</script> </script>

View File

@ -20,7 +20,6 @@
export default defineComponent({ export default defineComponent({
name: 'PopButton', name: 'PopButton',
components: { Popconfirm, BasicButton },
inheritAttrs: false, inheritAttrs: false,
props, props,
setup(props, { slots }) { setup(props, { slots }) {
@ -40,7 +39,7 @@
return () => { return () => {
const bindValues = omit(unref(getBindValues), 'icon'); const bindValues = omit(unref(getBindValues), 'icon');
const btnBind = omit(bindValues, 'title'); const btnBind = omit(bindValues, 'title') as Recordable;
if (btnBind.disabled) btnBind.color = ''; if (btnBind.disabled) btnBind.color = '';
const Button = h(BasicButton, btnBind, extendSlots(slots)); const Button = h(BasicButton, btnBind, extendSlots(slots));

View File

@ -3,24 +3,17 @@
<slot></slot> <slot></slot>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent, ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { onClickOutside } from '@vueuse/core'; import { onClickOutside } from '@vueuse/core';
export default defineComponent({ const emit = defineEmits(['mounted', 'clickOutside']);
name: 'ClickOutSide', const wrap = ref<ElRef>(null);
emits: ['mounted', 'clickOutside'],
setup(_, { emit }) {
const wrap = ref<ElRef>(null);
onClickOutside(wrap, () => { onClickOutside(wrap, () => {
emit('clickOutside'); emit('clickOutside');
}); });
onMounted(() => { onMounted(() => {
emit('mounted'); emit('mounted');
});
return { wrap };
},
}); });
</script> </script>

View File

@ -8,45 +8,39 @@
/> />
</div> </div>
</template> </template>
<script lang="ts">
import { defineComponent, computed } from 'vue';
import CodeMirrorEditor from './codemirror/CodeMirror.vue';
import { isString } from '/@/utils/is';
<script lang="ts">
const MODE = { const MODE = {
JSON: 'application/json', JSON: 'application/json',
html: 'htmlmixed', html: 'htmlmixed',
js: 'javascript', js: 'javascript',
}; };
</script>
<script lang="ts" setup>
import { computed } from 'vue';
import CodeMirrorEditor from './codemirror/CodeMirror.vue';
import { isString } from '/@/utils/is';
const props = { const props = defineProps({
value: { type: [Object, String] as PropType<Record<string, any> | string> }, value: { type: [Object, String] as PropType<Record<string, any> | string> },
mode: { type: String, default: MODE.JSON }, mode: { type: String, default: MODE.JSON },
readonly: { type: Boolean }, readonly: { type: Boolean },
};
export default defineComponent({
name: 'CodeEditor',
components: { CodeMirrorEditor },
props,
emits: ['change', 'update:value'],
setup(props, { emit }) {
const getValue = computed(() => {
const { value, mode } = props;
if (mode !== MODE.JSON) {
return value as string;
}
return isString(value)
? JSON.stringify(JSON.parse(value), null, 2)
: JSON.stringify(value, null, 2);
});
function handleValueChange(v) {
emit('update:value', v);
emit('change', v);
}
return { handleValueChange, getValue };
},
}); });
const emit = defineEmits(['change', 'update:value']);
const getValue = computed(() => {
const { value, mode } = props;
if (mode !== MODE.JSON) {
return value as string;
}
return isString(value)
? JSON.stringify(JSON.parse(value), null, 2)
: JSON.stringify(value, null, 2);
});
function handleValueChange(v) {
emit('update:value', v);
emit('change', v);
}
</script> </script>

View File

@ -2,17 +2,8 @@
<div class="relative !h-full w-full overflow-hidden" ref="el"> </div> <div class="relative !h-full w-full overflow-hidden" ref="el"> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { import { ref, onMounted, onUnmounted, watchEffect, watch, unref, nextTick } from 'vue';
ref,
onMounted,
onUnmounted,
watchEffect,
watch,
defineComponent,
unref,
nextTick,
} from 'vue';
import { useDebounceFn } from '@vueuse/core'; import { useDebounceFn } from '@vueuse/core';
import { useAppStore } from '/@/store/modules/app'; import { useAppStore } from '/@/store/modules/app';
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn'; import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
@ -26,95 +17,89 @@
import 'codemirror/mode/css/css'; import 'codemirror/mode/css/css';
import 'codemirror/mode/htmlmixed/htmlmixed'; import 'codemirror/mode/htmlmixed/htmlmixed';
const props = { const props = defineProps({
mode: { type: String, default: 'application/json' }, mode: { type: String, default: 'application/json' },
value: { type: String, default: '' }, value: { type: String, default: '' },
readonly: { type: Boolean, default: false }, readonly: { type: Boolean, default: false },
}; });
export default defineComponent({ const emit = defineEmits(['change']);
props,
emits: ['change'],
setup(props, { emit }) {
const el = ref();
let editor: Nullable<CodeMirror.Editor>;
const debounceRefresh = useDebounceFn(refresh, 100); const el = ref();
const appStore = useAppStore(); let editor: Nullable<CodeMirror.Editor>;
watch( const debounceRefresh = useDebounceFn(refresh, 100);
() => props.value, const appStore = useAppStore();
async (value) => {
await nextTick();
const oldValue = editor?.getValue();
if (value !== oldValue) {
editor?.setValue(value ? value : '');
}
},
{ flush: 'post' }
);
watchEffect(() => { watch(
editor?.setOption('mode', props.mode); () => props.value,
}); async (value) => {
await nextTick();
watch( const oldValue = editor?.getValue();
() => appStore.getDarkMode, if (value !== oldValue) {
async () => { editor?.setValue(value ? value : '');
setTheme();
},
{
immediate: true,
}
);
function setTheme() {
unref(editor)?.setOption(
'theme',
appStore.getDarkMode === 'light' ? 'idea' : 'material-palenight'
);
} }
function refresh() {
editor?.refresh();
}
async function init() {
const addonOptions = {
autoCloseBrackets: true,
autoCloseTags: true,
foldGutter: true,
gutters: ['CodeMirror-linenumbers'],
};
editor = CodeMirror(el.value!, {
value: '',
mode: props.mode,
readOnly: props.readonly,
tabSize: 2,
theme: 'material-palenight',
lineWrapping: true,
lineNumbers: true,
...addonOptions,
});
editor?.setValue(props.value);
setTheme();
editor?.on('change', () => {
emit('change', editor?.getValue());
});
}
onMounted(async () => {
await nextTick();
init();
useWindowSizeFn(debounceRefresh);
});
onUnmounted(() => {
editor = null;
});
return { el };
}, },
{ flush: 'post' }
);
watchEffect(() => {
editor?.setOption('mode', props.mode);
});
watch(
() => appStore.getDarkMode,
async () => {
setTheme();
},
{
immediate: true,
}
);
function setTheme() {
unref(editor)?.setOption(
'theme',
appStore.getDarkMode === 'light' ? 'idea' : 'material-palenight'
);
}
function refresh() {
editor?.refresh();
}
async function init() {
const addonOptions = {
autoCloseBrackets: true,
autoCloseTags: true,
foldGutter: true,
gutters: ['CodeMirror-linenumbers'],
};
editor = CodeMirror(el.value!, {
value: '',
mode: props.mode,
readOnly: props.readonly,
tabSize: 2,
theme: 'material-palenight',
lineWrapping: true,
lineNumbers: true,
...addonOptions,
});
editor?.setValue(props.value);
setTheme();
editor?.on('change', () => {
emit('change', editor?.getValue());
});
}
onMounted(async () => {
await nextTick();
init();
useWindowSizeFn(debounceRefresh);
});
onUnmounted(() => {
editor = null;
}); });
</script> </script>

View File

@ -2,13 +2,11 @@
<vue-json-pretty :path="'res'" :deep="3" :showLength="true" :data="data" /> <vue-json-pretty :path="'res'" :deep="3" :showLength="true" :data="data" />
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import VueJsonPretty from 'vue-json-pretty'; import VueJsonPretty from 'vue-json-pretty';
import 'vue-json-pretty/lib/styles.css'; import 'vue-json-pretty/lib/styles.css';
import { defineComponent } from 'vue';
export default defineComponent({ defineProps({
name: 'JsonPreview', data: Object,
components: { VueJsonPretty },
props: { data: Object },
}); });
</script> </script>

View File

@ -22,9 +22,9 @@
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { defineComponent, ref } from 'vue'; import { ref } from 'vue';
// component // component
import { Skeleton } from 'ant-design-vue'; import { Skeleton } from 'ant-design-vue';
import { CollapseTransition } from '/@/components/Transition'; import { CollapseTransition } from '/@/components/Transition';
@ -34,7 +34,7 @@
import { useTimeoutFn } from '/@/hooks/core/useTimeout'; import { useTimeoutFn } from '/@/hooks/core/useTimeout';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
const props = { const props = defineProps({
title: { type: String, default: '' }, title: { type: String, default: '' },
loading: { type: Boolean }, loading: { type: Boolean },
/** /**
@ -57,39 +57,22 @@
* Delayed loading time * Delayed loading time
*/ */
lazyTime: { type: Number, default: 0 }, lazyTime: { type: Number, default: 0 },
};
export default defineComponent({
name: 'CollapseContainer',
components: {
Skeleton,
CollapseHeader,
CollapseTransition,
},
props,
setup(props) {
const show = ref(true);
const { prefixCls } = useDesign('collapse-container');
/**
* @description: Handling development events
*/
function handleExpand() {
show.value = !show.value;
if (props.triggerWindowResize) {
// 200 milliseconds here is because the expansion has animation,
useTimeoutFn(triggerWindowResize, 200);
}
}
return {
show,
handleExpand,
prefixCls,
};
},
}); });
const show = ref(true);
const { prefixCls } = useDesign('collapse-container');
/**
* @description: Handling development events
*/
function handleExpand() {
show.value = !show.value;
if (props.triggerWindowResize) {
// 200 milliseconds here is because the expansion has animation,
useTimeoutFn(triggerWindowResize, 200);
}
}
</script> </script>
<style lang="less"> <style lang="less">
@prefix-cls: ~'@{namespace}-collapse-container'; @prefix-cls: ~'@{namespace}-collapse-container';

View File

@ -4,7 +4,7 @@
<CountButton :size="size" :count="count" :value="state" :beforeStartFunc="sendCodeApi" /> <CountButton :size="size" :count="count" :value="state" :beforeStartFunc="sendCodeApi" />
</template> </template>
<template #[item]="data" v-for="item in Object.keys($slots).filter((k) => k !== 'addonAfter')"> <template #[item]="data" v-for="item in Object.keys($slots).filter((k) => k !== 'addonAfter')">
<slot :name="item" v-bind="data"></slot> <slot :name="item" v-bind="data || {}"></slot>
</template> </template>
</a-input> </a-input>
</template> </template>

View File

@ -122,15 +122,17 @@
import { isFunction } from '/@/utils/is'; import { isFunction } from '/@/utils/is';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
type apiFunParams = { file: Blob; name: string; filename: string };
const props = { const props = {
circled: { type: Boolean, default: true }, circled: { type: Boolean, default: true },
uploadApi: { uploadApi: {
type: Function as PropType<({ file: Blob, name: string, filename: string }) => Promise<any>>, type: Function as PropType<(params: apiFunParams) => Promise<any>>,
}, },
}; };
export default defineComponent({ export default defineComponent({
name: 'CropperAvatar', name: 'CropperModal',
components: { BasicModal, Space, CropperImage, Upload, Avatar, Tooltip }, components: { BasicModal, Space, CropperImage, Upload, Avatar, Tooltip },
props, props,
emits: ['uploadSuccess', 'register'], emits: ['uploadSuccess', 'register'],

View File

@ -12,7 +12,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import type { CSSProperties } from 'vue'; import type { CSSProperties } from 'vue';
import { defineComponent, onMounted, ref, unref, computed } from 'vue'; import { defineComponent, onMounted, ref, unref, computed, onUnmounted } from 'vue';
import Cropper from 'cropperjs'; import Cropper from 'cropperjs';
import 'cropperjs/dist/cropper.css'; import 'cropperjs/dist/cropper.css';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
@ -93,6 +93,10 @@
onMounted(init); onMounted(init);
onUnmounted(() => {
cropper.value?.destroy();
});
async function init() { async function init() {
const imgEl = unref(imgElRef); const imgEl = unref(imgElRef);
if (!imgEl) { if (!imgEl) {

View File

@ -25,7 +25,7 @@
</ScrollContainer> </ScrollContainer>
<DrawerFooter v-bind="getProps" @close="onClose" @ok="handleOk" :height="getFooterHeight"> <DrawerFooter v-bind="getProps" @close="onClose" @ok="handleOk" :height="getFooterHeight">
<template #[item]="data" v-for="item in Object.keys($slots)"> <template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data"></slot> <slot :name="item" v-bind="data || {}"></slot>
</template> </template>
</DrawerFooter> </DrawerFooter>
</Drawer> </Drawer>

View File

@ -8,6 +8,7 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import type { Ref } from 'vue';
import type { Definition } from '@logicflow/core'; import type { Definition } from '@logicflow/core';
import { defineComponent, ref, onMounted, unref, nextTick, computed, watch } from 'vue'; import { defineComponent, ref, onMounted, unref, nextTick, computed, watch } from 'vue';
import FlowChartToolbar from './FlowChartToolbar.vue'; import FlowChartToolbar from './FlowChartToolbar.vue';
@ -46,10 +47,10 @@
}, },
}, },
setup(props) { setup(props) {
const lfElRef = ref<ElRef>(null); const lfElRef = ref(null);
const graphData = ref<Recordable>({}); const graphData = ref({});
const lfInstance = ref<Nullable<LogicFlow>>(null); const lfInstance = ref(null) as Ref<LogicFlow | null>;
const { prefixCls } = useDesign('flow-chart'); const { prefixCls } = useDesign('flow-chart');
const appStore = useAppStore(); const appStore = useAppStore();

View File

@ -19,7 +19,7 @@
:setFormModel="setFormModel" :setFormModel="setFormModel"
> >
<template #[item]="data" v-for="item in Object.keys($slots)"> <template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data"></slot> <slot :name="item" v-bind="data || {}"></slot>
</template> </template>
</FormItem> </FormItem>
</template> </template>
@ -231,9 +231,7 @@
formModel[key] = value; formModel[key] = value;
const { validateTrigger } = unref(getBindValue); const { validateTrigger } = unref(getBindValue);
if (!validateTrigger || validateTrigger === 'change') { if (!validateTrigger || validateTrigger === 'change') {
try { validateFields([key]).catch((_) => {});
validateFields([key]);
} catch (e) {}
} }
} }

View File

@ -7,7 +7,7 @@
v-model:value="state" v-model:value="state"
> >
<template #[item]="data" v-for="item in Object.keys($slots)"> <template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data"></slot> <slot :name="item" v-bind="data || {}"></slot>
</template> </template>
<template #suffixIcon v-if="loading"> <template #suffixIcon v-if="loading">
<LoadingOutlined spin /> <LoadingOutlined spin />

View File

@ -1,7 +1,7 @@
<template> <template>
<a-tree-select v-bind="getAttrs" @change="handleChange"> <a-tree-select v-bind="getAttrs" @change="handleChange">
<template #[item]="data" v-for="item in Object.keys($slots)"> <template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data"></slot> <slot :name="item" v-bind="data || {}"></slot>
</template> </template>
<template #suffixIcon v-if="loading"> <template #suffixIcon v-if="loading">
<LoadingOutlined spin /> <LoadingOutlined spin />
@ -45,7 +45,8 @@
() => props.params, () => props.params,
() => { () => {
isFirstLoaded.value && fetch(); isFirstLoaded.value && fetch();
} },
{ deep: true }
); );
watch( watch(

View File

@ -1,6 +1,6 @@
<template> <template>
<a-col v-bind="actionColOpt" :style="{ textAlign: 'right' }" v-if="showActionButtonGroup"> <a-col v-bind="actionColOpt" v-if="showActionButtonGroup">
<div style="width: 100%; text-align: right"> <div style="width: 100%" :style="{ textAlign: actionColOpt.style.textAlign }">
<FormItem> <FormItem>
<slot name="resetBefore"></slot> <slot name="resetBefore"></slot>
<Button <Button
@ -92,6 +92,7 @@
? { span: actionSpan < 6 ? 24 : actionSpan } ? { span: actionSpan < 6 ? 24 : actionSpan }
: {}; : {};
const actionColOpt: Partial<ColEx> = { const actionColOpt: Partial<ColEx> = {
style: { textAlign: 'right' },
span: showAdvancedButton ? 6 : 4, span: showAdvancedButton ? 6 : 4,
...advancedSpanObj, ...advancedSpanObj,
...actionColOptions, ...actionColOptions,

View File

@ -103,7 +103,7 @@ export default function ({
} }
return { isAdvanced: advanceState.isAdvanced, itemColSum }; return { isAdvanced: advanceState.isAdvanced, itemColSum };
} }
if (itemColSum > BASIC_COL_LEN) { if (itemColSum > BASIC_COL_LEN * (unref(getProps).alwaysShowLines || 1)) {
return { isAdvanced: advanceState.isAdvanced, itemColSum }; return { isAdvanced: advanceState.isAdvanced, itemColSum };
} else { } else {
// The first line is always displayed // The first line is always displayed

View File

@ -84,7 +84,7 @@ export function useFormEvents({
validKeys.push(key); validKeys.push(key);
} }
}); });
validateFields(validKeys); validateFields(validKeys).catch((_) => {});
} }
/** /**
* @description: Delete based on field name * @description: Delete based on field name

View File

@ -59,6 +59,8 @@ export const basicProps = {
rulesMessageJoinLabel: propTypes.bool.def(true), rulesMessageJoinLabel: propTypes.bool.def(true),
// 超过3行自动折叠 // 超过3行自动折叠
autoAdvancedLine: propTypes.number.def(3), autoAdvancedLine: propTypes.number.def(3),
// 不受折叠影响的行数
alwaysShowLines: propTypes.number.def(1),
// 是否显示操作按钮 // 是否显示操作按钮
showActionButtonGroup: propTypes.bool.def(true), showActionButtonGroup: propTypes.bool.def(true),

View File

@ -97,6 +97,8 @@ export interface FormProps {
autoFocusFirstItem?: boolean; autoFocusFirstItem?: boolean;
// Automatically collapse over the specified number of rows // Automatically collapse over the specified number of rows
autoAdvancedLine?: number; autoAdvancedLine?: number;
// Always show lines
alwaysShowLines?: number;
// Whether to show the operation button // Whether to show the operation button
showActionButtonGroup?: boolean; showActionButtonGroup?: boolean;

View File

@ -121,7 +121,7 @@
copy: propTypes.bool.def(false), copy: propTypes.bool.def(false),
mode: propTypes.oneOf<('svg' | 'iconify')[]>(['svg', 'iconify']).def('iconify'), mode: propTypes.oneOf<('svg' | 'iconify')[]>(['svg', 'iconify']).def('iconify'),
}, },
emits: ['change'], emits: ['change', 'update:value'],
setup(props, { emit }) { setup(props, { emit }) {
const isSvgMode = props.mode === 'svg'; const isSvgMode = props.mode === 'svg';
const icons = isSvgMode ? getSvgIcons() : getIcons(); const icons = isSvgMode ? getSvgIcons() : getIcons();
@ -148,7 +148,10 @@
watch( watch(
() => currentSelect.value, () => currentSelect.value,
(v) => emit('change', v) (v) => {
emit('update:value', v);
return emit('change', v);
}
); );
function handlePageChange(page: number) { function handlePageChange(page: number) {

View File

@ -12,10 +12,12 @@ interface Fn {
(): void; (): void;
} }
export function useLoading(props: Partial<LoadingProps>): [Fn, Fn]; export function useLoading(props: Partial<LoadingProps>): [Fn, Fn, (string) => void];
export function useLoading(opt: Partial<UseLoadingOptions>): [Fn, Fn]; export function useLoading(opt: Partial<UseLoadingOptions>): [Fn, Fn, (string) => void];
export function useLoading(opt: Partial<LoadingProps> | Partial<UseLoadingOptions>): [Fn, Fn] { export function useLoading(
opt: Partial<LoadingProps> | Partial<UseLoadingOptions>
): [Fn, Fn, (string) => void] {
let props: Partial<LoadingProps>; let props: Partial<LoadingProps>;
let target: HTMLElement | Ref<ElRef> = document.body; let target: HTMLElement | Ref<ElRef> = document.body;
@ -30,7 +32,7 @@ export function useLoading(opt: Partial<LoadingProps> | Partial<UseLoadingOption
const instance = createLoading(props, undefined, true); const instance = createLoading(props, undefined, true);
const open = (): void => { const open = (): void => {
const t = unref(target); const t = unref(target as Ref<ElRef>);
if (!t) return; if (!t) return;
instance.open(t); instance.open(t);
}; };
@ -39,5 +41,9 @@ export function useLoading(opt: Partial<LoadingProps> | Partial<UseLoadingOption
instance.close(); instance.close();
}; };
return [open, close]; const setTip = (tip: string) => {
instance.setTip(tip);
};
return [open, close, setTip];
} }

View File

@ -20,7 +20,7 @@
<template #footer v-if="!$slots.footer"> <template #footer v-if="!$slots.footer">
<ModalFooter v-bind="getBindValue" @ok="handleOk" @cancel="handleCancel"> <ModalFooter v-bind="getBindValue" @ok="handleOk" @cancel="handleCancel">
<template #[item]="data" v-for="item in Object.keys($slots)"> <template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data"></slot> <slot :name="item" v-bind="data || {}"></slot>
</template> </template>
</ModalFooter> </ModalFooter>
</template> </template>
@ -140,9 +140,9 @@
wrapClassName: unref(getWrapClassName), wrapClassName: unref(getWrapClassName),
}; };
if (unref(fullScreenRef)) { if (unref(fullScreenRef)) {
return omit(attr, 'height'); return omit(attr, ['height', 'title']);
} }
return attr; return omit(attr, 'title');
}); });
const getWrapperHeight = computed(() => { const getWrapperHeight = computed(() => {

View File

@ -5,3 +5,5 @@ import pageWrapper from './src/PageWrapper.vue';
export const PageFooter = withInstall(pageFooter); export const PageFooter = withInstall(pageFooter);
export const PageWrapper = withInstall(pageWrapper); export const PageWrapper = withInstall(pageWrapper);
export const PageWrapperFixedHeightKey = 'PageWrapperFixedHeight';

View File

@ -3,7 +3,7 @@
<PageHeader <PageHeader
:ghost="ghost" :ghost="ghost"
:title="title" :title="title"
v-bind="$attrs" v-bind="omit($attrs, 'class')"
ref="headerRef" ref="headerRef"
v-if="content || $slots.headerContent || title || getHeaderSlots.length" v-if="content || $slots.headerContent || title || getHeaderSlots.length"
> >
@ -14,7 +14,7 @@
<slot name="headerContent" v-else></slot> <slot name="headerContent" v-else></slot>
</template> </template>
<template #[item]="data" v-for="item in getHeaderSlots"> <template #[item]="data" v-for="item in getHeaderSlots">
<slot :name="item" v-bind="data"></slot> <slot :name="item" v-bind="data || {}"></slot>
</template> </template>
</PageHeader> </PageHeader>
@ -33,7 +33,7 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import type { CSSProperties, PropType } from 'vue'; import { CSSProperties, PropType, provide } from 'vue';
import { defineComponent, computed, watch, ref, unref } from 'vue'; import { defineComponent, computed, watch, ref, unref } from 'vue';
import PageFooter from './PageFooter.vue'; import PageFooter from './PageFooter.vue';
@ -43,6 +43,7 @@
import { omit } from 'lodash-es'; import { omit } from 'lodash-es';
import { PageHeader } from 'ant-design-vue'; import { PageHeader } from 'ant-design-vue';
import { useContentHeight } from '/@/hooks/web/useContentHeight'; import { useContentHeight } from '/@/hooks/web/useContentHeight';
import { PageWrapperFixedHeightKey } from '..';
export default defineComponent({ export default defineComponent({
name: 'PageWrapper', name: 'PageWrapper',
@ -61,13 +62,18 @@
contentClass: propTypes.string, contentClass: propTypes.string,
fixedHeight: propTypes.bool, fixedHeight: propTypes.bool,
}, },
setup(props, { slots }) { setup(props, { slots, attrs }) {
const wrapperRef = ref(null); const wrapperRef = ref(null);
const headerRef = ref(null); const headerRef = ref(null);
const contentRef = ref(null); const contentRef = ref(null);
const footerRef = ref(null); const footerRef = ref(null);
const { prefixCls } = useDesign('page-wrapper'); const { prefixCls } = useDesign('page-wrapper');
provide(
PageWrapperFixedHeightKey,
computed(() => props.fixedHeight)
);
const getIsContentFullHeight = computed(() => { const getIsContentFullHeight = computed(() => {
return props.contentFullHeight; return props.contentFullHeight;
}); });
@ -86,6 +92,7 @@
{ {
[`${prefixCls}--dense`]: props.dense, [`${prefixCls}--dense`]: props.dense,
}, },
attrs.class ?? {},
]; ];
}); });

View File

@ -1,6 +1,5 @@
<script lang="tsx"> <script lang="tsx">
import { defineComponent, ref, unref, computed, reactive, watchEffect } from 'vue'; import { defineComponent, ref, unref, computed, reactive, watchEffect } from 'vue';
import { Props } from './typing';
import { CloseOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons-vue'; import { CloseOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
import resumeSvg from '/@/assets/svg/preview/resume.svg'; import resumeSvg from '/@/assets/svg/preview/resume.svg';
import rotateSvg from '/@/assets/svg/preview/p-rotate.svg'; import rotateSvg from '/@/assets/svg/preview/p-rotate.svg';
@ -38,13 +37,33 @@
type: Number as PropType<number>, type: Number as PropType<number>,
default: 0, default: 0,
}, },
scaleStep: {
type: Number as PropType<number>,
},
defaultWidth: {
type: Number as PropType<number>,
},
maskClosable: {
type: Boolean as PropType<boolean>,
},
rememberState: {
type: Boolean as PropType<boolean>,
},
}; };
const prefixCls = 'img-preview'; const prefixCls = 'img-preview';
export default defineComponent({ export default defineComponent({
name: 'ImagePreview', name: 'ImagePreview',
props, props,
setup(props: Props) { emits: ['img-load', 'img-error'],
setup(props, { expose, emit }) {
interface stateInfo {
scale: number;
rotate: number;
top: number;
left: number;
}
const stateMap = new Map<string, stateInfo>();
const imgState = reactive<ImgState>({ const imgState = reactive<ImgState>({
currentUrl: '', currentUrl: '',
imgScale: 1, imgScale: 1,
@ -96,6 +115,15 @@
}; };
} }
const getScaleStep = computed(() => {
const scaleStep = props?.scaleStep ?? 0;
if (scaleStep ?? (0 > 0 && scaleStep < 100)) {
return scaleStep / 100;
} else {
return imgState.imgScale / 10;
}
});
// //
function scrollFunc(e: any) { function scrollFunc(e: any) {
e = e || window.event; e = e || window.event;
@ -104,11 +132,11 @@
e.preventDefault(); e.preventDefault();
if (e.delta > 0) { if (e.delta > 0) {
// //
scaleFunc(0.015); scaleFunc(getScaleStep.value);
} }
if (e.delta < 0) { if (e.delta < 0) {
// //
scaleFunc(-0.015); scaleFunc(-getScaleStep.value);
} }
} }
// //
@ -134,11 +162,54 @@
imgState.status = StatueEnum.LOADING; imgState.status = StatueEnum.LOADING;
const img = new Image(); const img = new Image();
img.src = url; img.src = url;
img.onload = () => { img.onload = (e: Event) => {
if (imgState.currentUrl !== url) {
const ele: any[] = e.composedPath();
if (props.rememberState) {
//
stateMap.set(imgState.currentUrl, {
scale: imgState.imgScale,
top: imgState.imgTop,
left: imgState.imgLeft,
rotate: imgState.imgRotate,
});
//
const stateInfo = stateMap.get(url);
if (stateInfo) {
imgState.imgScale = stateInfo.scale;
imgState.imgTop = stateInfo.top;
imgState.imgRotate = stateInfo.rotate;
imgState.imgLeft = stateInfo.left;
} else {
initState();
if (props.defaultWidth) {
imgState.imgScale = props.defaultWidth / ele[0].naturalWidth;
}
}
} else {
if (props.defaultWidth) {
imgState.imgScale = props.defaultWidth / ele[0].naturalWidth;
}
}
ele &&
emit('img-load', {
index: imgState.currentIndex,
dom: ele[0] as HTMLImageElement,
url,
});
}
imgState.currentUrl = url; imgState.currentUrl = url;
imgState.status = StatueEnum.DONE; imgState.status = StatueEnum.DONE;
}; };
img.onerror = () => { img.onerror = (e: Event) => {
const ele: EventTarget[] = e.composedPath();
ele &&
emit('img-error', {
index: imgState.currentIndex,
dom: ele[0] as HTMLImageElement,
url,
});
imgState.status = StatueEnum.FAIL; imgState.status = StatueEnum.FAIL;
}; };
} }
@ -146,6 +217,10 @@
// //
function handleClose(e: MouseEvent) { function handleClose(e: MouseEvent) {
e && e.stopPropagation(); e && e.stopPropagation();
close();
}
function close() {
imgState.show = false; imgState.show = false;
// //
document.body.removeEventListener('DOMMouseScroll', scrollFunc); document.body.removeEventListener('DOMMouseScroll', scrollFunc);
@ -158,6 +233,19 @@
initState(); initState();
} }
expose({
resume,
close,
prev: handleChange.bind(null, 'left'),
next: handleChange.bind(null, 'right'),
setScale: (scale: number) => {
if (scale > 0 && scale <= 10) imgState.imgScale = scale;
},
setRotate: (rotate: number) => {
imgState.imgRotate = rotate;
},
});
// //
function handleChange(direction: 'left' | 'right') { function handleChange(direction: 'left' | 'right') {
const { currentIndex } = imgState; const { currentIndex } = imgState;
@ -205,6 +293,7 @@
transform: `scale(${imgScale}) rotate(${imgRotate}deg)`, transform: `scale(${imgScale}) rotate(${imgRotate}deg)`,
marginTop: `${imgTop}px`, marginTop: `${imgTop}px`,
marginLeft: `${imgLeft}px`, marginLeft: `${imgLeft}px`,
maxWidth: props.defaultWidth ? 'unset' : '100%',
}; };
}); });
@ -222,6 +311,16 @@
} }
}); });
const handleMaskClick = (e: MouseEvent) => {
if (
props.maskClosable &&
e.target &&
(e.target as HTMLDivElement).classList.contains(`${prefixCls}-content`)
) {
handleClose(e);
}
};
const renderClose = () => { const renderClose = () => {
return ( return (
<div class={`${prefixCls}__close`} onClick={handleClose}> <div class={`${prefixCls}__close`} onClick={handleClose}>
@ -246,10 +345,16 @@
const renderController = () => { const renderController = () => {
return ( return (
<div class={`${prefixCls}__controller`}> <div class={`${prefixCls}__controller`}>
<div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(-0.15)}> <div
class={`${prefixCls}__controller-item`}
onClick={() => scaleFunc(-getScaleStep.value)}
>
<img src={unScaleSvg} /> <img src={unScaleSvg} />
</div> </div>
<div class={`${prefixCls}__controller-item`} onClick={() => scaleFunc(0.15)}> <div
class={`${prefixCls}__controller-item`}
onClick={() => scaleFunc(getScaleStep.value)}
>
<img src={scaleSvg} /> <img src={scaleSvg} />
</div> </div>
<div class={`${prefixCls}__controller-item`} onClick={resume}> <div class={`${prefixCls}__controller-item`} onClick={resume}>
@ -279,7 +384,12 @@
return () => { return () => {
return ( return (
imgState.show && ( imgState.show && (
<div class={prefixCls} ref={wrapElRef} onMouseup={handleMouseUp}> <div
class={prefixCls}
ref={wrapElRef}
onMouseup={handleMouseUp}
onClick={handleMaskClick}
>
<div class={`${prefixCls}-content`}> <div class={`${prefixCls}-content`}>
{/*<Spin*/} {/*<Spin*/}
{/* indicator={<LoadingOutlined style="font-size: 24px" spin />}*/} {/* indicator={<LoadingOutlined style="font-size: 24px" spin />}*/}

View File

@ -6,15 +6,12 @@ import { createVNode, render } from 'vue';
let instance: ReturnType<typeof createVNode> | null = null; let instance: ReturnType<typeof createVNode> | null = null;
export function createImgPreview(options: Options) { export function createImgPreview(options: Options) {
if (!isClient) return; if (!isClient) return;
const { imageList, show = true, index = 0 } = options;
const propsData: Partial<Props> = {}; const propsData: Partial<Props> = {};
const container = document.createElement('div'); const container = document.createElement('div');
propsData.imageList = imageList; Object.assign(propsData, { show: true, index: 0, scaleStep: 100 }, options);
propsData.show = show;
propsData.index = index;
instance = createVNode(ImgPreview, propsData); instance = createVNode(ImgPreview, propsData);
render(instance, container); render(instance, container);
document.body.appendChild(container); document.body.appendChild(container);
return instance.component?.exposed;
} }

View File

@ -2,6 +2,12 @@ export interface Options {
show?: boolean; show?: boolean;
imageList: string[]; imageList: string[];
index?: number; index?: number;
scaleStep?: number;
defaultWidth?: number;
maskClosable?: boolean;
rememberState?: boolean;
onImgLoad?: ({ index: number, url: string, dom: HTMLImageElement }) => void;
onImgError?: ({ index: number, url: string, dom: HTMLImageElement }) => void;
} }
export interface Props { export interface Props {
@ -9,6 +15,19 @@ export interface Props {
instance: Props; instance: Props;
imageList: string[]; imageList: string[];
index: number; index: number;
scaleStep: number;
defaultWidth: number;
maskClosable: boolean;
rememberState: boolean;
}
export interface PreviewActions {
resume: () => void;
close: () => void;
prev: () => void;
next: () => void;
setScale: (scale: number) => void;
setRotate: (rotate: number) => void;
} }
export interface ImageProps { export interface ImageProps {

View File

@ -4,7 +4,7 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, watchEffect, PropType, ref, unref } from 'vue'; import { defineComponent, watch, PropType, ref, unref, onMounted } from 'vue';
import { toCanvas, QRCodeRenderersOptions, LogoType } from './qrcodePlus'; import { toCanvas, QRCodeRenderersOptions, LogoType } from './qrcodePlus';
import { toDataURL } from 'qrcode'; import { toDataURL } from 'qrcode';
import { downloadByUrl } from '/@/utils/file/download'; import { downloadByUrl } from '/@/utils/file/download';
@ -93,11 +93,18 @@
}); });
} }
watchEffect(() => { onMounted(createQrcode);
setTimeout(() => {
//
watch(
props,
() => {
createQrcode(); createQrcode();
}, 30); },
}); {
deep: true,
}
);
return { wrapRef, download }; return { wrapRef, download };
}, },

View File

@ -1,7 +1,15 @@
import { toCanvas } from 'qrcode'; import { toCanvas } from 'qrcode';
import type { QRCodeRenderersOptions } from 'qrcode'; import type { QRCodeRenderersOptions } from 'qrcode';
import { RenderQrCodeParams, ContentType } from './typing'; import { RenderQrCodeParams, ContentType } from './typing';
export const renderQrCode = ({ canvas, content, width = 0, options = {} }: RenderQrCodeParams) => { import { cloneDeep } from 'lodash-es';
export const renderQrCode = ({
canvas,
content,
width = 0,
options: params = {},
}: RenderQrCodeParams) => {
const options = cloneDeep(params);
// 容错率,默认对内容少的二维码采用高容错率,内容多的二维码采用低容错率 // 容错率,默认对内容少的二维码采用高容错率,内容多的二维码采用低容错率
options.errorCorrectionLevel = options.errorCorrectionLevel || getErrorCorrectionLevel(content); options.errorCorrectionLevel = options.errorCorrectionLevel || getErrorCorrectionLevel(content);

View File

@ -9,7 +9,7 @@
:disabled="disabled" :disabled="disabled"
> >
<template #[item]="data" v-for="item in Object.keys($slots)"> <template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data"></slot> <slot :name="item" v-bind="data || {}"></slot>
</template> </template>
</InputPassword> </InputPassword>
<div :class="`${prefixCls}-bar`"> <div :class="`${prefixCls}-bar`">

View File

@ -22,7 +22,7 @@
@change="handleTableChange" @change="handleTableChange"
> >
<template #[item]="data" v-for="item in Object.keys($slots)" :key="item"> <template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
<slot :name="item" v-bind="data"></slot> <slot :name="item" v-bind="data || {}"></slot>
</template> </template>
<template #[`header-${column.dataIndex}`] v-for="column in columns" :key="column.dataIndex"> <template #[`header-${column.dataIndex}`] v-for="column in columns" :key="column.dataIndex">
@ -39,9 +39,10 @@
ColumnChangeParam, ColumnChangeParam,
} from './types/table'; } from './types/table';
import { defineComponent, ref, computed, unref, toRaw } from 'vue'; import { defineComponent, ref, computed, unref, toRaw, inject, watchEffect } from 'vue';
import { Table } from 'ant-design-vue'; import { Table } from 'ant-design-vue';
import { BasicForm, useForm } from '/@/components/Form/index'; import { BasicForm, useForm } from '/@/components/Form/index';
import { PageWrapperFixedHeightKey } from '/@/components/Page';
import expandIcon from './components/ExpandIcon'; import expandIcon from './components/ExpandIcon';
import HeaderCell from './components/HeaderCell.vue'; import HeaderCell from './components/HeaderCell.vue';
import { InnerHandlers } from './types/table'; import { InnerHandlers } from './types/table';
@ -64,6 +65,7 @@
import { omit } from 'lodash-es'; import { omit } from 'lodash-es';
import { basicProps } from './props'; import { basicProps } from './props';
import { isFunction } from '/@/utils/is'; import { isFunction } from '/@/utils/is';
import { warn } from '/@/utils/log';
export default defineComponent({ export default defineComponent({
components: { components: {
@ -91,10 +93,10 @@
'columns-change', 'columns-change',
], ],
setup(props, { attrs, emit, slots, expose }) { setup(props, { attrs, emit, slots, expose }) {
const tableElRef = ref<ComponentRef>(null); const tableElRef = ref(null);
const tableData = ref<Recordable[]>([]); const tableData = ref<Recordable[]>([]);
const wrapRef = ref<Nullable<HTMLDivElement>>(null); const wrapRef = ref(null);
const innerPropsRef = ref<Partial<BasicTableProps>>(); const innerPropsRef = ref<Partial<BasicTableProps>>();
const { prefixCls } = useDesign('basic-table'); const { prefixCls } = useDesign('basic-table');
@ -104,6 +106,15 @@
return { ...props, ...unref(innerPropsRef) } as BasicTableProps; return { ...props, ...unref(innerPropsRef) } as BasicTableProps;
}); });
const isFixedHeightPage = inject(PageWrapperFixedHeightKey, false);
watchEffect(() => {
unref(isFixedHeightPage) &&
props.canResize &&
warn(
"'canResize' of BasicTable may not work in PageWrapper with 'fixedHeight' (especially in hot updates)"
);
});
const { getLoading, setLoading } = useLoading(getProps); const { getLoading, setLoading } = useLoading(getProps);
const { const {
getPaginationInfo, getPaginationInfo,
@ -127,8 +138,10 @@
handleTableChange: onTableChange, handleTableChange: onTableChange,
getDataSourceRef, getDataSourceRef,
getDataSource, getDataSource,
getRawDataSource,
setTableData, setTableData,
updateTableDataRecord, updateTableDataRecord,
findTableDataRecord,
fetch, fetch,
getRowKey, getRowKey,
reload, reload,
@ -211,7 +224,7 @@
// ...(dataSource.length === 0 ? { getPopupContainer: () => document.body } : {}), // ...(dataSource.length === 0 ? { getPopupContainer: () => document.body } : {}),
...attrs, ...attrs,
customRow, customRow,
expandIcon: expandIcon(), expandIcon: slots.expandIcon ? null : expandIcon(),
...unref(getProps), ...unref(getProps),
...unref(getHeaderProps), ...unref(getHeaderProps),
scroll: unref(getScrollRef), scroll: unref(getScrollRef),
@ -266,11 +279,13 @@
setPagination, setPagination,
setTableData, setTableData,
updateTableDataRecord, updateTableDataRecord,
findTableDataRecord,
redoHeight, redoHeight,
setSelectedRowKeys, setSelectedRowKeys,
setColumns, setColumns,
setLoading, setLoading,
getDataSource, getDataSource,
getRawDataSource,
setProps, setProps,
getRowSelection, getRowSelection,
getPaginationRef: getPagination, getPaginationRef: getPagination,
@ -376,9 +391,9 @@
align-items: center; align-items: center;
} }
.ant-table-tbody > tr.ant-table-row-selected td { //.ant-table-tbody > tr.ant-table-row-selected td {
background-color: fade(@primary-color, 8%) !important; //background-color: fade(@primary-color, 8%) !important;
} //}
} }
.ant-pagination { .ant-pagination {

View File

@ -3,7 +3,7 @@ import { BasicArrow } from '/@/components/Basic';
export default () => { export default () => {
return (props: Recordable) => { return (props: Recordable) => {
if (!props.expandable) { if (!props.expandable) {
if (props.expanded) { if (props.needIndentSpaced) {
return <span class="ant-table-row-expand-icon ant-table-row-spaced" />; return <span class="ant-table-row-expand-icon ant-table-row-spaced" />;
} else { } else {
return <span />; return <span />;

View File

@ -35,7 +35,7 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, PropType, computed, toRaw } from 'vue'; import { defineComponent, PropType, computed, toRaw, unref } from 'vue';
import { MoreOutlined } from '@ant-design/icons-vue'; import { MoreOutlined } from '@ant-design/icons-vue';
import { Divider, Tooltip, TooltipProps } from 'ant-design-vue'; import { Divider, Tooltip, TooltipProps } from 'ant-design-vue';
import Icon from '/@/components/Icon/index'; import Icon from '/@/components/Icon/index';
@ -95,6 +95,7 @@
.map((action) => { .map((action) => {
const { popConfirm } = action; const { popConfirm } = action;
return { return {
getPopupContainer: () => unref(table?.wrapRef.value) ?? document.body,
type: 'link', type: 'link',
size: 'small', size: 'small',
...action, ...action,
@ -131,19 +132,20 @@
}); });
function getTooltip(data: string | TooltipProps): TooltipProps { function getTooltip(data: string | TooltipProps): TooltipProps {
if (isString(data)) { return {
return { title: data, placement: 'bottom' }; getPopupContainer: () => unref(table?.wrapRef.value) ?? document.body,
} else { placement: 'bottom',
return Object.assign({ placement: 'bottom' }, data); ...(isString(data) ? { title: data } : data),
} };
} }
function onCellClick(e: MouseEvent) { function onCellClick(e: MouseEvent) {
if (!props.stopButtonPropagation) return; if (!props.stopButtonPropagation) return;
const target = e.target as HTMLElement; const path = e.composedPath() as HTMLElement[];
if (target.tagName === 'BUTTON') { const isInButton = path.find((ele) => {
e.stopPropagation(); return ele.tagName?.toUpperCase() === 'BUTTON';
} });
isInButton && e.stopPropagation();
} }
return { prefixCls, getActions, getDropdownList, getAlign, onCellClick, getTooltip }; return { prefixCls, getActions, getDropdownList, getAlign, onCellClick, getTooltip };

View File

@ -3,7 +3,7 @@
<div v-if="$slots.headerTop" style="margin: 5px"> <div v-if="$slots.headerTop" style="margin: 5px">
<slot name="headerTop"></slot> <slot name="headerTop"></slot>
</div> </div>
<div style="width: 100%; display: flex"> <div class="flex items-center">
<slot name="tableTitle" v-if="$slots.tableTitle"></slot> <slot name="tableTitle" v-if="$slots.tableTitle"></slot>
<TableTitle <TableTitle
:helpMessage="titleHelpMessage" :helpMessage="titleHelpMessage"

View File

@ -10,21 +10,32 @@ export interface ComponentProps {
rule: boolean; rule: boolean;
popoverVisible: boolean; popoverVisible: boolean;
ruleMessage: string; ruleMessage: string;
getPopupContainer?: Fn;
} }
export const CellComponent: FunctionalComponent = ( export const CellComponent: FunctionalComponent = (
{ component = 'Input', rule = true, ruleMessage, popoverVisible }: ComponentProps, {
component = 'Input',
rule = true,
ruleMessage,
popoverVisible,
getPopupContainer,
}: ComponentProps,
{ attrs } { attrs }
) => { ) => {
const Comp = componentMap.get(component) as typeof defineComponent; const Comp = componentMap.get(component) as typeof defineComponent;
const DefaultComp = h(Comp, attrs); const DefaultComp = h(Comp, attrs);
if (!rule || !popoverVisible) { if (!rule) {
return DefaultComp; return DefaultComp;
} }
return h( return h(
Popover, Popover,
{ overlayClassName: 'edit-cell-rule-popover', visible: !!popoverVisible }, {
overlayClassName: 'edit-cell-rule-popover',
visible: !!popoverVisible,
...(getPopupContainer ? { getPopupContainer } : {}),
},
{ {
default: () => DefaultComp, default: () => DefaultComp,
content: () => ruleMessage, content: () => ruleMessage,

View File

@ -1,7 +1,13 @@
<template> <template>
<div :class="prefixCls"> <div :class="prefixCls">
<div v-show="!isEdit" :class="`${prefixCls}__normal`" @click="handleEdit"> <div
{{ getValues || '&nbsp;' }} v-show="!isEdit"
:class="{ [`${prefixCls}__normal`]: true, 'ellipsis-cell': column.ellipsis }"
@click="handleEdit"
>
<div class="cell-content" :title="column.ellipsis ? getValues ?? '' : ''">{{
getValues ?? '&nbsp;'
}}</div>
<FormOutlined :class="`${prefixCls}__normal-icon`" v-if="!column.editRow" /> <FormOutlined :class="`${prefixCls}__normal-icon`" v-if="!column.editRow" />
</div> </div>
@ -14,14 +20,13 @@
:rule="getRule" :rule="getRule"
:ruleMessage="ruleMessage" :ruleMessage="ruleMessage"
:class="getWrapperClass" :class="getWrapperClass"
size="small"
ref="elRef" ref="elRef"
@change="handleChange" @change="handleChange"
@options-change="handleOptionsChange" @options-change="handleOptionsChange"
@pressEnter="handleEnter" @pressEnter="handleEnter"
/> />
<div :class="`${prefixCls}__action`" v-if="!getRowEditable"> <div :class="`${prefixCls}__action`" v-if="!getRowEditable">
<CheckOutlined :class="[`${prefixCls}__icon`, 'mx-2']" @click="handleSubmit" /> <CheckOutlined :class="[`${prefixCls}__icon`, 'mx-2']" @click="handleSubmitClick" />
<CloseOutlined :class="`${prefixCls}__icon `" @click="handleCancel" /> <CloseOutlined :class="`${prefixCls}__icon `" @click="handleCancel" />
</div> </div>
</div> </div>
@ -29,11 +34,10 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import type { CSSProperties, PropType } from 'vue'; import type { CSSProperties, PropType } from 'vue';
import { computed, defineComponent, nextTick, ref, toRaw, unref, watchEffect } from 'vue';
import type { BasicColumn } from '../../types/table'; import type { BasicColumn } from '../../types/table';
import type { EditRecordRow } from './index'; import type { EditRecordRow } from './index';
import { CheckOutlined, CloseOutlined, FormOutlined } from '@ant-design/icons-vue';
import { defineComponent, ref, unref, nextTick, computed, watchEffect, toRaw } from 'vue';
import { FormOutlined, CloseOutlined, CheckOutlined } from '@ant-design/icons-vue';
import { CellComponent } from './CellComponent'; import { CellComponent } from './CellComponent';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
@ -42,9 +46,9 @@
import clickOutside from '/@/directives/clickOutside'; import clickOutside from '/@/directives/clickOutside';
import { propTypes } from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';
import { isString, isBoolean, isFunction, isNumber, isArray } from '/@/utils/is'; import { isArray, isBoolean, isFunction, isNumber, isString } from '/@/utils/is';
import { createPlaceholderMessage } from './helper'; import { createPlaceholderMessage } from './helper';
import { set, omit } from 'lodash-es'; import { omit, set } from 'lodash-es';
import { treeToList } from '/@/utils/helper/treeHelper'; import { treeToList } from '/@/utils/helper/treeHelper';
export default defineComponent({ export default defineComponent({
@ -107,6 +111,9 @@
const value = isCheckValue ? (isNumber(val) && isBoolean(val) ? val : !!val) : val; const value = isCheckValue ? (isNumber(val) && isBoolean(val) ? val : !!val) : val;
return { return {
size: 'small',
getPopupContainer: () => unref(table?.wrapRef.value) ?? document.body,
getCalendarContainer: () => unref(table?.wrapRef.value) ?? document.body,
placeholder: createPlaceholderMessage(unref(getComponent)), placeholder: createPlaceholderMessage(unref(getComponent)),
...apiSelectProps, ...apiSelectProps,
...omit(compProps, 'onChange'), ...omit(compProps, 'onChange'),
@ -206,8 +213,7 @@
if (isBoolean(editRule) && !currentValue && !isNumber(currentValue)) { if (isBoolean(editRule) && !currentValue && !isNumber(currentValue)) {
ruleVisible.value = true; ruleVisible.value = true;
const component = unref(getComponent); const component = unref(getComponent);
const message = createPlaceholderMessage(component); ruleMessage.value = createPlaceholderMessage(component);
ruleMessage.value = message;
return false; return false;
} }
if (isFunction(editRule)) { if (isFunction(editRule)) {
@ -253,6 +259,10 @@
handleSubmit(); handleSubmit();
} }
function handleSubmitClick() {
handleSubmit();
}
function handleCancel() { function handleCancel() {
isEdit.value = false; isEdit.value = false;
currentValueRef.value = defaultValueRef.value; currentValueRef.value = defaultValueRef.value;
@ -357,7 +367,7 @@
getRowEditable, getRowEditable,
getValues, getValues,
handleEnter, handleEnter,
// getSize, handleSubmitClick,
}; };
}, },
}); });
@ -420,6 +430,16 @@
} }
} }
.ellipsis-cell {
.cell-content {
overflow-wrap: break-word;
word-break: break-word;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
&__normal { &__normal {
&-icon { &-icon {
position: absolute; position: absolute;

View File

@ -8,6 +8,7 @@
trigger="click" trigger="click"
@visibleChange="handleVisibleChange" @visibleChange="handleVisibleChange"
:overlayClassName="`${prefixCls}__cloumn-list`" :overlayClassName="`${prefixCls}__cloumn-list`"
:getPopupContainer="getPopupContainer"
> >
<template #title> <template #title>
<div :class="`${prefixCls}__popover-title`"> <div :class="`${prefixCls}__popover-title`">
@ -47,7 +48,11 @@
{{ item.label }} {{ item.label }}
</Checkbox> </Checkbox>
<Tooltip placement="bottomLeft" :mouseLeaveDelay="0.4"> <Tooltip
placement="bottomLeft"
:mouseLeaveDelay="0.4"
:getPopupContainer="getPopupContainer"
>
<template #title> <template #title>
{{ t('component.table.settingFixedLeft') }} {{ t('component.table.settingFixedLeft') }}
</template> </template>
@ -64,7 +69,11 @@
/> />
</Tooltip> </Tooltip>
<Divider type="vertical" /> <Divider type="vertical" />
<Tooltip placement="bottomLeft" :mouseLeaveDelay="0.4"> <Tooltip
placement="bottomLeft"
:mouseLeaveDelay="0.4"
:getPopupContainer="getPopupContainer"
>
<template #title> <template #title>
{{ t('component.table.settingFixedRight') }} {{ t('component.table.settingFixedRight') }}
</template> </template>
@ -109,8 +118,8 @@
import { useTableContext } from '../../hooks/useTableContext'; import { useTableContext } from '../../hooks/useTableContext';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { useSortable } from '/@/hooks/web/useSortable'; import { useSortable } from '/@/hooks/web/useSortable';
import { isNullAndUnDef } from '/@/utils/is'; import { isFunction, isNullAndUnDef } from '/@/utils/is';
import { getPopupContainer } from '/@/utils'; import { getPopupContainer as getParentContainer } from '/@/utils';
import { omit } from 'lodash-es'; import { omit } from 'lodash-es';
interface State { interface State {
@ -140,7 +149,7 @@
}, },
emits: ['columns-change'], emits: ['columns-change'],
setup(_, { emit }) { setup(_, { emit, attrs }) {
const { t } = useI18n(); const { t } = useI18n();
const table = useTableContext(); const table = useTableContext();
@ -273,7 +282,7 @@
nextTick(() => { nextTick(() => {
const columnListEl = unref(columnListRef); const columnListEl = unref(columnListRef);
if (!columnListEl) return; if (!columnListEl) return;
const el = columnListEl.$el; const el = columnListEl.$el as any;
if (!el) return; if (!el) return;
// Drag and drop sort // Drag and drop sort
const { initSortable } = useSortable(el, { const { initSortable } = useSortable(el, {
@ -350,6 +359,12 @@
emit('columns-change', data); emit('columns-change', data);
} }
function getPopupContainer() {
return isFunction(attrs.getPopupContainer)
? attrs.getPopupContainer()
: getParentContainer();
}
return { return {
t, t,
...toRefs(state), ...toRefs(state),

View File

@ -1,20 +1,25 @@
<template> <template>
<div class="table-settings"> <div class="table-settings">
<RedoSetting v-if="getSetting.redo" /> <RedoSetting v-if="getSetting.redo" :getPopupContainer="getTableContainer" />
<SizeSetting v-if="getSetting.size" /> <SizeSetting v-if="getSetting.size" :getPopupContainer="getTableContainer" />
<ColumnSetting v-if="getSetting.setting" @columns-change="handleColumnChange" /> <ColumnSetting
<FullScreenSetting v-if="getSetting.fullScreen" /> v-if="getSetting.setting"
@columns-change="handleColumnChange"
:getPopupContainer="getTableContainer"
/>
<FullScreenSetting v-if="getSetting.fullScreen" :getPopupContainer="getTableContainer" />
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import type { TableSetting, ColumnChangeParam } from '../../types/table'; import type { TableSetting, ColumnChangeParam } from '../../types/table';
import { defineComponent, computed } from 'vue'; import { defineComponent, computed, unref } from 'vue';
import ColumnSetting from './ColumnSetting.vue'; import ColumnSetting from './ColumnSetting.vue';
import SizeSetting from './SizeSetting.vue'; import SizeSetting from './SizeSetting.vue';
import RedoSetting from './RedoSetting.vue'; import RedoSetting from './RedoSetting.vue';
import FullScreenSetting from './FullScreenSetting.vue'; import FullScreenSetting from './FullScreenSetting.vue';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext';
export default defineComponent({ export default defineComponent({
name: 'TableSetting', name: 'TableSetting',
@ -33,6 +38,7 @@
emits: ['columns-change'], emits: ['columns-change'],
setup(props, { emit }) { setup(props, { emit }) {
const { t } = useI18n(); const { t } = useI18n();
const table = useTableContext();
const getSetting = computed((): TableSetting => { const getSetting = computed((): TableSetting => {
return { return {
@ -48,7 +54,11 @@
emit('columns-change', data); emit('columns-change', data);
} }
return { getSetting, t, handleColumnChange }; function getTableContainer() {
return table ? unref(table.wrapRef) : document.body;
}
return { getSetting, t, handleColumnChange, getTableContainer };
}, },
}); });
</script> </script>

View File

@ -1,14 +1,14 @@
import type { BasicColumn, BasicTableProps, CellFormat, GetColumnsParams } from '../types/table'; import type { BasicColumn, BasicTableProps, CellFormat, GetColumnsParams } from '../types/table';
import type { PaginationProps } from '../types/pagination'; import type { PaginationProps } from '../types/pagination';
import type { ComputedRef } from 'vue'; import type { ComputedRef } from 'vue';
import { unref, Ref, computed, watch, ref, toRaw } from 'vue'; import { computed, Ref, ref, toRaw, unref, watch } from 'vue';
import { renderEditCell } from '../components/editable'; import { renderEditCell } from '../components/editable';
import { usePermission } from '/@/hooks/web/usePermission'; import { usePermission } from '/@/hooks/web/usePermission';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { isBoolean, isArray, isString, isObject, isFunction } from '/@/utils/is'; import { isArray, isBoolean, isFunction, isMap, isString } from '/@/utils/is';
import { isEqual, cloneDeep } from 'lodash-es'; import { cloneDeep, isEqual } from 'lodash-es';
import { formatToDate } from '/@/utils/dateUtil'; import { formatToDate } from '/@/utils/dateUtil';
import { DEFAULT_ALIGN, PAGE_SIZE, INDEX_COLUMN_FLAG, ACTION_COLUMN_FLAG } from '../const'; import { ACTION_COLUMN_FLAG, DEFAULT_ALIGN, INDEX_COLUMN_FLAG, PAGE_SIZE } from '../const';
function handleItem(item: BasicColumn, ellipsis: boolean) { function handleItem(item: BasicColumn, ellipsis: boolean) {
const { key, dataIndex, children } = item; const { key, dataIndex, children } = item;
@ -287,11 +287,9 @@ function sortFixedColumn(columns: BasicColumn[]) {
} }
defColumns.push(column); defColumns.push(column);
} }
const resultColumns = [...fixedLeftColumns, ...defColumns, ...fixedRightColumns].filter( return [...fixedLeftColumns, ...defColumns, ...fixedRightColumns].filter(
(item) => !item.defaultHidden (item) => !item.defaultHidden
); );
return resultColumns;
} }
// format cell // format cell
@ -317,8 +315,8 @@ export function formatCell(text: string, format: CellFormat, record: Recordable,
return formatToDate(text, dateFormat); return formatToDate(text, dateFormat);
} }
// enum // Map
if (isObject(format) && Reflect.has(format, 'size')) { if (isMap(format)) {
return format.get(text); return format.get(text);
} }
} catch (error) { } catch (error) {

View File

@ -47,6 +47,7 @@ export function useDataSource(
filterInfo: {}, filterInfo: {},
}); });
const dataSourceRef = ref<Recordable[]>([]); const dataSourceRef = ref<Recordable[]>([]);
const rawDataSourceRef = ref<Recordable>({});
watchEffect(() => { watchEffect(() => {
tableData.value = unref(dataSourceRef); tableData.value = unref(dataSourceRef);
@ -149,18 +150,8 @@ export function useDataSource(
rowKey: string | number, rowKey: string | number,
record: Recordable record: Recordable
): Recordable | undefined { ): Recordable | undefined {
if (!dataSourceRef.value || dataSourceRef.value.length == 0) return; const row = findTableDataRecord(rowKey);
const rowKeyName = unref(getRowKey);
if (!rowKeyName) {
return;
}
const row = dataSourceRef.value.find((r) => {
if (typeof rowKeyName === 'function') {
return (rowKeyName(r) as string) === rowKey;
} else {
return Reflect.has(r, rowKeyName) && r[rowKeyName] === rowKey;
}
});
if (row) { if (row) {
for (const field in row) { for (const field in row) {
if (Reflect.has(record, field)) row[field] = record[field]; if (Reflect.has(record, field)) row[field] = record[field];
@ -169,6 +160,43 @@ export function useDataSource(
} }
} }
function findTableDataRecord(rowKey: string | number) {
if (!dataSourceRef.value || dataSourceRef.value.length == 0) return;
const rowKeyName = unref(getRowKey);
if (!rowKeyName) return;
const { childrenColumnName = 'children' } = unref(propsRef);
const findRow = (array: any[]) => {
let ret;
array.some(function iter(r) {
if (typeof rowKeyName === 'function') {
if ((rowKeyName(r) as string) === rowKey) {
ret = r;
return true;
}
} else {
if (Reflect.has(r, rowKeyName) && r[rowKeyName] === rowKey) {
ret = r;
return true;
}
}
return r[childrenColumnName] && r[childrenColumnName].some(iter);
});
return ret;
};
// const row = dataSourceRef.value.find(r => {
// if (typeof rowKeyName === 'function') {
// return (rowKeyName(r) as string) === rowKey
// } else {
// return Reflect.has(r, rowKeyName) && r[rowKeyName] === rowKey
// }
// })
return findRow(dataSourceRef.value);
}
async function fetch(opt?: FetchParams) { async function fetch(opt?: FetchParams) {
const { api, searchInfo, fetchSetting, beforeFetch, afterFetch, useSearchForm, pagination } = const { api, searchInfo, fetchSetting, beforeFetch, afterFetch, useSearchForm, pagination } =
unref(propsRef); unref(propsRef);
@ -208,6 +236,7 @@ export function useDataSource(
} }
const res = await api(params); const res = await api(params);
rawDataSourceRef.value = res;
const isArrayResult = Array.isArray(res); const isArrayResult = Array.isArray(res);
@ -260,6 +289,10 @@ export function useDataSource(
return getDataSourceRef.value as T[]; return getDataSourceRef.value as T[];
} }
function getRawDataSource<T = Recordable>() {
return rawDataSourceRef.value as T;
}
async function reload(opt?: FetchParams) { async function reload(opt?: FetchParams) {
await fetch(opt); await fetch(opt);
} }
@ -273,6 +306,7 @@ export function useDataSource(
return { return {
getDataSourceRef, getDataSourceRef,
getDataSource, getDataSource,
getRawDataSource,
getRowKey, getRowKey,
setTableData, setTableData,
getAutoCreateKey, getAutoCreateKey,
@ -280,6 +314,7 @@ export function useDataSource(
reload, reload,
updateTableData, updateTableData,
updateTableDataRecord, updateTableDataRecord,
findTableDataRecord,
handleTableChange, handleTableChange,
}; };
} }

View File

@ -1,6 +1,6 @@
import type { PaginationProps } from '../types/pagination'; import type { PaginationProps } from '../types/pagination';
import type { BasicTableProps } from '../types/table'; import type { BasicTableProps } from '../types/table';
import { computed, unref, ref, ComputedRef } from 'vue'; import { computed, unref, ref, ComputedRef, watchEffect } from 'vue';
import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue'; import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
import { isBoolean } from '/@/utils/is'; import { isBoolean } from '/@/utils/is';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '../const'; import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '../const';
@ -27,6 +27,16 @@ export function usePagination(refProps: ComputedRef<BasicTableProps>) {
const configRef = ref<PaginationProps>({}); const configRef = ref<PaginationProps>({});
const show = ref(true); const show = ref(true);
watchEffect(() => {
const { pagination } = unref(refProps);
if (!isBoolean(pagination) && pagination) {
configRef.value = {
...unref(configRef),
...(pagination ?? {}),
};
}
});
const getPaginationInfo = computed((): PaginationProps | boolean => { const getPaginationInfo = computed((): PaginationProps | boolean => {
const { pagination } = unref(refProps); const { pagination } = unref(refProps);

View File

@ -1,8 +1,9 @@
import { isFunction } from '/@/utils/is'; import { isFunction } from '/@/utils/is';
import type { BasicTableProps, TableRowSelection } from '../types/table'; import type { BasicTableProps, TableRowSelection } from '../types/table';
import { computed, ref, unref, ComputedRef, Ref, toRaw, watch, nextTick } from 'vue'; import { computed, ComputedRef, nextTick, Ref, ref, toRaw, unref, watch } from 'vue';
import { ROW_KEY } from '../const'; import { ROW_KEY } from '../const';
import { omit } from 'lodash-es'; import { omit } from 'lodash-es';
import { findNodeAll } from '/@/utils/helper/treeHelper';
export function useRowSelection( export function useRowSelection(
propsRef: ComputedRef<BasicTableProps>, propsRef: ComputedRef<BasicTableProps>,
@ -21,11 +22,12 @@ export function useRowSelection(
return { return {
selectedRowKeys: unref(selectedRowKeysRef), selectedRowKeys: unref(selectedRowKeysRef),
hideDefaultSelections: false, hideDefaultSelections: false,
onChange: (selectedRowKeys: string[], selectedRows: Recordable[]) => { onChange: (selectedRowKeys: string[]) => {
selectedRowKeysRef.value = selectedRowKeys; setSelectedRowKeys(selectedRowKeys);
selectedRowRef.value = selectedRows; // selectedRowKeysRef.value = selectedRowKeys;
// selectedRowRef.value = selectedRows;
}, },
...omit(rowSelection === undefined ? {} : rowSelection, ['onChange']), ...omit(rowSelection, ['onChange']),
}; };
}); });
@ -50,7 +52,8 @@ export function useRowSelection(
rows: getSelectRows(), rows: getSelectRows(),
}); });
}); });
} },
{ deep: true }
); );
const getAutoCreateKey = computed(() => { const getAutoCreateKey = computed(() => {
@ -64,11 +67,19 @@ export function useRowSelection(
function setSelectedRowKeys(rowKeys: string[]) { function setSelectedRowKeys(rowKeys: string[]) {
selectedRowKeysRef.value = rowKeys; selectedRowKeysRef.value = rowKeys;
const allSelectedRows = findNodeAll(
const rows = toRaw(unref(tableData)).filter((item) => toRaw(unref(tableData)).concat(toRaw(unref(selectedRowRef))),
rowKeys.includes(item[unref(getRowKey) as string]) (item) => rowKeys.includes(item[unref(getRowKey) as string]),
{
children: propsRef.value.childrenColumnName ?? 'children',
}
); );
selectedRowRef.value = rows; const trueSelectedRows: any[] = [];
rowKeys.forEach((key: string) => {
const found = allSelectedRows.find((item) => item[unref(getRowKey) as string] === key);
found && trueSelectedRows.push(found);
});
selectedRowRef.value = trueSelectedRows;
} }
function setSelectedRows(rows: Recordable[]) { function setSelectedRows(rows: Recordable[]) {

View File

@ -82,6 +82,9 @@ export function useTable(tableProps?: Props): [
getDataSource: () => { getDataSource: () => {
return getTableInstance().getDataSource(); return getTableInstance().getDataSource();
}, },
getRawDataSource: () => {
return getTableInstance().getRawDataSource();
},
getColumns: ({ ignoreIndex = false }: { ignoreIndex?: boolean } = {}) => { getColumns: ({ ignoreIndex = false }: { ignoreIndex?: boolean } = {}) => {
const columns = getTableInstance().getColumns({ ignoreIndex }) || []; const columns = getTableInstance().getColumns({ ignoreIndex }) || [];
return toRaw(columns); return toRaw(columns);
@ -122,6 +125,9 @@ export function useTable(tableProps?: Props): [
updateTableDataRecord: (rowKey: string | number, record: Recordable) => { updateTableDataRecord: (rowKey: string | number, record: Recordable) => {
return getTableInstance().updateTableDataRecord(rowKey, record); return getTableInstance().updateTableDataRecord(rowKey, record);
}, },
findTableDataRecord: (rowKey: string | number) => {
return getTableInstance().findTableDataRecord(rowKey);
},
getRowSelection: () => { getRowSelection: () => {
return toRaw(getTableInstance().getRowSelection()); return toRaw(getTableInstance().getRowSelection());
}, },

View File

@ -21,9 +21,11 @@ export function useTableForm(
}; };
}); });
const getFormSlotKeys = computed(() => { const getFormSlotKeys: ComputedRef<string[]> = computed(() => {
const keys = Object.keys(slots); const keys = Object.keys(slots);
return keys.map((item) => (item.startsWith('form-') ? item : null)).filter(Boolean); return keys
.map((item) => (item.startsWith('form-') ? item : null))
.filter((item) => !!item) as string[];
}); });
function replaceFormSlotKey(key: string) { function replaceFormSlotKey(key: string) {

View File

@ -66,6 +66,7 @@ export function useTableScroll(
if (!bodyEl) { if (!bodyEl) {
bodyEl = tableEl.querySelector('.ant-table-body'); bodyEl = tableEl.querySelector('.ant-table-body');
if (!bodyEl) return;
} }
const hasScrollBarY = bodyEl.scrollHeight > bodyEl.clientHeight; const hasScrollBarY = bodyEl.scrollHeight > bodyEl.clientHeight;

View File

@ -95,9 +95,11 @@ export interface TableActionType {
setPagination: (info: Partial<PaginationProps>) => void; setPagination: (info: Partial<PaginationProps>) => void;
setTableData: <T = Recordable>(values: T[]) => void; setTableData: <T = Recordable>(values: T[]) => void;
updateTableDataRecord: (rowKey: string | number, record: Recordable) => Recordable | void; updateTableDataRecord: (rowKey: string | number, record: Recordable) => Recordable | void;
findTableDataRecord: (rowKey: string | number) => Recordable | void;
getColumns: (opt?: GetColumnsParams) => BasicColumn[]; getColumns: (opt?: GetColumnsParams) => BasicColumn[];
setColumns: (columns: BasicColumn[] | string[]) => void; setColumns: (columns: BasicColumn[] | string[]) => void;
getDataSource: <T = Recordable>() => T[]; getDataSource: <T = Recordable>() => T[];
getRawDataSource: <T = Recordable>() => T;
setLoading: (loading: boolean) => void; setLoading: (loading: boolean) => void;
setProps: (props: Partial<BasicTableProps>) => void; setProps: (props: Partial<BasicTableProps>) => void;
redoHeight: () => void; redoHeight: () => void;

View File

@ -278,8 +278,9 @@
if (!editor) { if (!editor) {
return; return;
} }
editor.execCommand('mceInsertContent', false, getUploadingImgName(name));
const content = editor?.getContent() ?? ''; const content = editor?.getContent() ?? '';
setValue(editor, `${content}\n${getUploadingImgName(name)}`); setValue(editor, content);
} }
function handleDone(name: string, url: string) { function handleDone(name: string, url: string) {

View File

@ -42,7 +42,14 @@
name: 'BasicTree', name: 'BasicTree',
inheritAttrs: false, inheritAttrs: false,
props: basicProps, props: basicProps,
emits: ['update:expandedKeys', 'update:selectedKeys', 'update:value', 'change', 'check'], emits: [
'update:expandedKeys',
'update:selectedKeys',
'update:value',
'change',
'check',
'update:searchValue',
],
setup(props, { attrs, slots, emit, expose }) { setup(props, { attrs, slots, emit, expose }) {
const state = reactive<State>({ const state = reactive<State>({
checkStrictly: props.checkStrictly, checkStrictly: props.checkStrictly,
@ -127,6 +134,7 @@
updateNodeByKey, updateNodeByKey,
getAllKeys, getAllKeys,
getChildrenKeys, getChildrenKeys,
getEnabledKeys,
} = useTree(treeDataRef, getReplaceFields); } = useTree(treeDataRef, getReplaceFields);
function getIcon(params: Recordable, icon?: string) { function getIcon(params: Recordable, icon?: string) {
@ -180,7 +188,7 @@
} }
function checkAll(checkAll: boolean) { function checkAll(checkAll: boolean) {
state.checkedKeys = checkAll ? getAllKeys() : ([] as Keys); state.checkedKeys = checkAll ? getEnabledKeys() : ([] as Keys);
} }
function expandAll(expandAll: boolean) { function expandAll(expandAll: boolean) {
@ -191,7 +199,14 @@
state.checkStrictly = strictly; state.checkStrictly = strictly;
} }
const searchText = ref('');
watchEffect(() => {
if (props.searchValue !== searchText.value) searchText.value = props.searchValue;
});
function handleSearch(searchValue: string) { function handleSearch(searchValue: string) {
if (searchValue !== searchText.value) searchText.value = searchValue;
emit('update:searchValue', searchValue);
if (!searchValue) { if (!searchValue) {
searchState.startSearch = false; searchState.startSearch = false;
return; return;
@ -292,6 +307,12 @@
filterByLevel: (level: number) => { filterByLevel: (level: number) => {
state.expandedKeys = filterByLevel(level); state.expandedKeys = filterByLevel(level);
}, },
setSearchValue: (value: string) => {
handleSearch(value);
},
getSearchValue: () => {
return searchText.value;
},
}; };
expose(instance); expose(instance);
@ -379,6 +400,7 @@
helpMessage={helpMessage} helpMessage={helpMessage}
onStrictlyChange={onStrictlyChange} onStrictlyChange={onStrictlyChange}
onSearch={handleSearch} onSearch={handleSearch}
searchText={unref(searchText)}
> >
{extendSlots(slots)} {extendSlots(slots)}
</TreeHeader> </TreeHeader>

View File

@ -11,7 +11,7 @@
:placeholder="t('common.searchText')" :placeholder="t('common.searchText')"
size="small" size="small"
allowClear allowClear
@change="handleSearch" v-model:value="searchValue"
/> />
</div> </div>
<Dropdown @click.prevent v-if="toolbar"> <Dropdown @click.prevent v-if="toolbar">
@ -32,7 +32,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { defineComponent, computed } from 'vue'; import { defineComponent, computed, ref, watch } from 'vue';
import { Dropdown, Menu, Input } from 'ant-design-vue'; import { Dropdown, Menu, Input } from 'ant-design-vue';
import { Icon } from '/@/components/Icon'; import { Icon } from '/@/components/Icon';
@ -77,10 +77,12 @@
search: propTypes.bool, search: propTypes.bool,
checkAll: propTypes.func, checkAll: propTypes.func,
expandAll: propTypes.func, expandAll: propTypes.func,
searchText: propTypes.string,
}, },
emits: ['strictly-change', 'search'], emits: ['strictly-change', 'search'],
setup(props, { emit }) { setup(props, { emit }) {
const { t } = useI18n(); const { t } = useI18n();
const searchValue = ref('');
const toolbarList = computed(() => { const toolbarList = computed(() => {
const { checkable } = props; const { checkable } = props;
@ -137,11 +139,25 @@
} }
const debounceEmitChange = useDebounceFn(emitChange, 200); const debounceEmitChange = useDebounceFn(emitChange, 200);
function handleSearch(e: ChangeEvent): void { watch(
debounceEmitChange(e.target.value); () => searchValue.value,
} (v) => {
debounceEmitChange(v);
}
);
watch(
() => props.searchText,
(v) => {
if (v !== searchValue.value) {
searchValue.value = v;
}
}
);
// function handleSearch(e: ChangeEvent): void {
// debounceEmitChange(e.target.value);
// }
return { t, toolbarList, handleMenuClick, handleSearch }; return { t, toolbarList, handleMenuClick, searchValue };
}, },
}); });
</script> </script>

View File

@ -20,6 +20,7 @@ export const basicProps = {
title: propTypes.string, title: propTypes.string,
toolbar: propTypes.bool, toolbar: propTypes.bool,
search: propTypes.bool, search: propTypes.bool,
searchValue: propTypes.string,
checkStrictly: propTypes.bool, checkStrictly: propTypes.bool,
clickRowToExpand: propTypes.bool.def(true), clickRowToExpand: propTypes.bool.def(true),
checkable: propTypes.bool.def(false), checkable: propTypes.bool.def(false),

View File

@ -34,6 +34,8 @@ export interface TreeActionType {
insertNodesByKey: (opt: InsertNodeParams) => void; insertNodesByKey: (opt: InsertNodeParams) => void;
deleteNodeByKey: (key: string) => void; deleteNodeByKey: (key: string) => void;
updateNodeByKey: (key: string, node: Omit<TreeDataItem, 'key'>) => void; updateNodeByKey: (key: string, node: Omit<TreeDataItem, 'key'>) => void;
setSearchValue: (value: string) => void;
getSearchValue: () => string;
} }
export interface InsertNodeParams { export interface InsertNodeParams {

View File

@ -26,6 +26,23 @@ export function useTree(
} }
return keys as Keys; return keys as Keys;
} }
// get keys that can be checked and selected
function getEnabledKeys(list?: TreeDataItem[]) {
const keys: string[] = [];
const treeData = list || unref(treeDataRef);
const { key: keyField, children: childrenField } = unref(getReplaceFields);
if (!childrenField || !keyField) return keys;
for (let index = 0; index < treeData.length; index++) {
const node = treeData[index];
node.disabled !== true && node.selectable !== false && keys.push(node[keyField]!);
const children = node[childrenField];
if (children && children.length) {
keys.push(...(getEnabledKeys(children) as string[]));
}
}
return keys as Keys;
}
function getChildrenKeys(nodeKey: string | number, list?: TreeDataItem[]): Keys { function getChildrenKeys(nodeKey: string | number, list?: TreeDataItem[]): Keys {
const keys: Keys = []; const keys: Keys = [];
@ -169,5 +186,6 @@ export function useTree(
updateNodeByKey, updateNodeByKey,
getAllKeys, getAllKeys,
getChildrenKeys, getChildrenKeys,
getEnabledKeys,
}; };
} }

View File

@ -1,6 +1,6 @@
<script lang="tsx"> <script lang="tsx">
import type { MoveData, DragVerifyActionType } from './typing'; import type { MoveData, DragVerifyActionType } from './typing';
import { defineComponent, computed, unref, reactive, watch, ref, getCurrentInstance } from 'vue'; import { defineComponent, computed, unref, reactive, watch, ref } from 'vue';
import { useTimeoutFn } from '/@/hooks/core/useTimeout'; import { useTimeoutFn } from '/@/hooks/core/useTimeout';
import BasicDragVerify from './DragVerify.vue'; import BasicDragVerify from './DragVerify.vue';
import { hackCss } from '/@/utils/domUtils'; import { hackCss } from '/@/utils/domUtils';
@ -8,11 +8,11 @@
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
export default defineComponent({ export default defineComponent({
name: 'ImgRotateDargVerify', name: 'ImgRotateDragVerify',
inheritAttrs: false, inheritAttrs: false,
props: rotateProps, props: rotateProps,
emits: ['success', 'change', 'update:value'], emits: ['success', 'change', 'update:value'],
setup(props, { emit, attrs }) { setup(props, { emit, attrs, expose }) {
const basicRef = ref<Nullable<DragVerifyActionType>>(null); const basicRef = ref<Nullable<DragVerifyActionType>>(null);
const state = reactive({ const state = reactive({
showTip: false, showTip: false,
@ -112,10 +112,8 @@
handleImgOnLoad(); handleImgOnLoad();
} }
const instance = getCurrentInstance() as any; expose({ resume });
if (instance) {
instance.resume = resume;
}
// handleImgOnLoad(); // handleImgOnLoad();
return () => { return () => {
const { src } = props; const { src } = props;
@ -138,6 +136,7 @@
onClick={() => { onClick={() => {
resume(); resume();
}} }}
alt="verify"
/> />
{state.showTip && ( {state.showTip && (
<span class={[`ir-dv-img__tip`, state.isPassing ? 'success' : 'error']}> <span class={[`ir-dv-img__tip`, state.isPassing ? 'success' : 'error']}>

View File

@ -23,21 +23,29 @@
&-primary { &-primary {
color: @white; color: @white;
background-color: @button-primary-color; background-color: @button-primary-color;
border-width: 0;
&:hover, &:hover,
&:focus { &:focus {
color: @white !important; color: @white;
background-color: @button-primary-hover-color; background-color: @button-primary-hover-color;
} }
//
&[disabled], //&[disabled],
&[disabled]:hover { //&[disabled]:hover {
color: @white; // color: fade(@button-cancel-color, 40%) !important;
background-color: fade(@button-primary-color, 40%); // background-color: fade(@button-cancel-bg-color, 40%) !important;
} // border-color: fade(@button-cancel-border-color, 40%) !important;
//}
} }
&-primary:not(&-background-ghost):not([disabled]) {
color: @white;
}
//&-primary:not(&-background-ghost) {
// border-width: 0;
//}
&-default { &-default {
color: @button-cancel-color; color: @button-cancel-color;
background-color: @button-cancel-bg-color; background-color: @button-cancel-bg-color;
@ -49,16 +57,16 @@
background-color: @button-cancel-hover-bg-color; background-color: @button-cancel-hover-bg-color;
border-color: @button-cancel-hover-border-color; border-color: @button-cancel-hover-border-color;
} }
//
&[disabled], //&[disabled],
&[disabled]:hover { //&[disabled]:hover {
color: fade(@button-cancel-color, 40%); // color: fade(@button-cancel-color, 40%) !important;
background: fade(@button-cancel-bg-color, 40%); // background: fade(@button-cancel-bg-color, 40%) !important;
border-color: fade(@button-cancel-border-color, 40%); // border-color: fade(@button-cancel-border-color, 40%) !important;
} //}
} }
&.ant-btn-link.is-disabled { [data-theme='light'] &.ant-btn-link.is-disabled {
color: rgba(0, 0, 0, 0.25); color: rgba(0, 0, 0, 0.25);
text-shadow: none; text-shadow: none;
cursor: not-allowed !important; cursor: not-allowed !important;
@ -67,6 +75,15 @@
box-shadow: none; box-shadow: none;
} }
[data-theme='dark'] &.ant-btn-link.is-disabled {
color: rgba(255, 255, 255, 0.25) !important;
text-shadow: none;
cursor: not-allowed !important;
background-color: transparent !important;
border-color: transparent !important;
box-shadow: none;
}
// color: @white; // color: @white;
&-success.ant-btn-link:not([disabled='disabled']) { &-success.ant-btn-link:not([disabled='disabled']) {
@ -77,6 +94,10 @@
color: @button-success-hover-color; color: @button-success-hover-color;
border-color: transparent; border-color: transparent;
} }
&:active {
color: @button-success-active-color;
}
} }
&-success.ant-btn-link.ant-btn-loading, &-success.ant-btn-link.ant-btn-loading,
@ -89,11 +110,11 @@
} }
} }
&-success:not(.ant-btn-link) { &-success:not(.ant-btn-link, .is-disabled) {
color: @white; color: @white;
background-color: @button-success-color; background-color: @button-success-color;
border-color: @button-success-color; border-color: @button-success-color;
border-width: 0; //border-width: 0;
&:hover, &:hover,
&:focus { &:focus {
@ -102,13 +123,17 @@
border-color: @button-success-hover-color; border-color: @button-success-hover-color;
} }
&[disabled], &:active {
&[disabled]:hover { background-color: @button-success-active-color;
color: @white; border-color: @button-success-active-color;
background-color: fade(@button-success-color, 40%);
// background-color: @button-success-disabled-color;
border-color: fade(@button-success-color, 40%);
} }
//&[disabled],
//&[disabled]:hover {
// color: @white;
// background-color: fade(@button-success-color, 40%);
// border-color: fade(@button-success-color, 40%);
//}
} }
&-warning.ant-btn-link:not([disabled='disabled']) { &-warning.ant-btn-link:not([disabled='disabled']) {
@ -119,13 +144,17 @@
color: @button-warn-hover-color; color: @button-warn-hover-color;
border-color: transparent; border-color: transparent;
} }
&:active {
color: @button-warn-active-color;
}
} }
&-warning:not(.ant-btn-link) { &-warning:not(.ant-btn-link, .is-disabled) {
color: @white; color: @white;
background-color: @button-warn-color; background-color: @button-warn-color;
border-color: @button-warn-color; border-color: @button-warn-color;
border-width: 0; //border-width: 0;
&:hover, &:hover,
&:focus { &:focus {
@ -134,15 +163,17 @@
border-color: @button-warn-hover-color; border-color: @button-warn-hover-color;
} }
&[disabled], &:active {
&[disabled]:hover { background-color: @button-warn-active-color;
color: @white; border-color: @button-warn-active-color;
background-color: fade(@button-warn-color, 40%);
border-color: fade(@button-warn-color, 40%);
// background-color: @button-warn-disabled-color;
// border-color: @button-warn-disabled-color ;
} }
//&[disabled],
//&[disabled]:hover {
// color: @white;
// background-color: fade(@button-warn-color, 40%);
// border-color: fade(@button-warn-color, 40%);
//}
} }
&-error.ant-btn-link:not([disabled='disabled']) { &-error.ant-btn-link:not([disabled='disabled']) {
@ -153,13 +184,17 @@
color: @button-error-hover-color; color: @button-error-hover-color;
border-color: transparent; border-color: transparent;
} }
&:active {
color: @button-error-active-color;
}
} }
&-error:not(.ant-btn-link) { &-error:not(.ant-btn-link, .is-disabled) {
color: @white; color: @white;
background-color: @button-error-color; background-color: @button-error-color;
border-color: @button-error-color; border-color: @button-error-color;
border-width: 0; //border-width: 0;
&:hover, &:hover,
&:focus { &:focus {
@ -168,41 +203,105 @@
border-color: @button-error-hover-color; border-color: @button-error-hover-color;
} }
&[disabled], &:active {
&[disabled]:hover { background-color: @button-error-active-color;
color: @white; border-color: @button-error-active-color;
background-color: fade(@button-error-color, 40%);
border-color: fade(@button-error-color, 40%);
} }
}
&-background-ghost.ant-btn-link, //&[disabled],
&.ant-btn-dashed:not([disabled='disabled']) { //&[disabled]:hover {
color: @text-color-call-out; // color: @white;
// background-color: fade(@button-error-color, 40%);
&:hover { // border-color: fade(@button-error-color, 40%);
color: @button-primary-color; //}
}
} }
&-background-ghost { &-background-ghost {
border-width: 1px;
background-color: transparent !important;
&[disabled],
&[disabled]:hover {
color: fade(@white, 40%) !important;
background-color: transparent !important;
border-color: fade(@white, 40%) !important;
}
}
&-dashed&-background-ghost,
&-default&-background-ghost {
color: @button-ghost-color; color: @button-ghost-color;
background-color: transparent;
border-color: @button-ghost-color; border-color: @button-ghost-color;
&:hover,
&:focus {
color: @button-ghost-hover-color;
border-color: @button-ghost-hover-color;
}
&:active {
color: @button-ghost-active-color;
border-color: @button-ghost-active-color;
}
&[disabled],
&[disabled]:hover {
color: fade(@white, 40%) !important;
border-color: fade(@white, 40%) !important;
}
}
&-background-ghost&-success:not(.ant-btn-link) {
color: @button-success-color;
background-color: transparent;
border-color: @button-success-color;
border-width: 1px; border-width: 1px;
&:hover, &:hover,
&:focus { &:focus {
color: @button-ghost-hover-color !important; color: @button-success-hover-color !important;
background-color: @button-ghost-hover-bg-color; border-color: @button-success-hover-color;
border-color: @button-ghost-hover-color;
} }
&[disabled], &:active {
&[disabled]:hover { color: @button-success-active-color;
color: @button-ghost-color; border-color: @button-success-active-color;
background-color: fade(@white, 40%); }
border-color: fade(@button-ghost-color, 40%); }
&-background-ghost&-warning:not(.ant-btn-link) {
color: @button-warn-color;
background-color: transparent;
border-color: @button-warn-color;
border-width: 1px;
&:hover,
&:focus {
color: @button-warn-hover-color !important;
border-color: @button-warn-hover-color;
}
&:active {
color: @button-warn-active-color;
border-color: @button-warn-active-color;
}
}
&-background-ghost&-error:not(.ant-btn-link) {
color: @button-error-color;
background-color: transparent;
border-color: @button-error-color;
border-width: 1px;
&:hover,
&:focus {
color: @button-error-hover-color !important;
border-color: @button-error-hover-color;
}
&:active {
color: @button-error-active-color;
border-color: @button-error-active-color;
} }
} }

View File

@ -57,3 +57,9 @@ span.anticon:not(.app-iconify) {
.modal-icon-info { .modal-icon-info {
color: @primary-color !important; color: @primary-color !important;
} }
.ant-checkbox-checked .ant-checkbox-inner::after,
.ant-tree-checkbox-checked .ant-tree-checkbox-inner::after {
border-top: 0 !important;
border-left: 0 !important;
}

View File

@ -108,20 +108,25 @@ html {
// ================================= // =================================
@button-primary-color: @primary-color; @button-primary-color: @primary-color;
@button-primary-hover-color: darken(@primary-color, 5%); @button-primary-hover-color: lighten(@primary-color, 5%);
@button-primary-active-color: darken(@primary-color, 5%);
@button-ghost-color: @primary-color; @button-ghost-color: @white;
@button-ghost-hover-color: lighten(@primary-color, 10%); @button-ghost-hover-color: lighten(@white, 10%);
@button-ghost-hover-bg-color: #e1ebf6; @button-ghost-hover-bg-color: #e1ebf6;
@button-ghost-active-color: darken(@white, 10%);
@button-success-color: @success-color; @button-success-color: @success-color;
@button-success-hover-color: darken(@success-color, 10%); @button-success-hover-color: lighten(@success-color, 10%);
@button-success-active-color: darken(@success-color, 10%);
@button-warn-color: @warning-color; @button-warn-color: @warning-color;
@button-warn-hover-color: darken(@warning-color, 10%); @button-warn-hover-color: lighten(@warning-color, 10%);
@button-warn-active-color: darken(@warning-color, 10%);
@button-error-color: @error-color; @button-error-color: @error-color;
@button-error-hover-color: darken(@error-color, 10%); @button-error-hover-color: lighten(@error-color, 10%);
@button-error-active-color: darken(@error-color, 10%);
@button-cancel-color: @text-color-call-out; @button-cancel-color: @text-color-call-out;
@button-cancel-bg-color: @white; @button-cancel-bg-color: @white;

View File

@ -1,4 +0,0 @@
/*! @import */
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@ -6,9 +6,28 @@ html[data-theme='light'] {
.text-secondary { .text-secondary {
color: rgba(0, 0, 0, 0.45); color: rgba(0, 0, 0, 0.45);
} }
.ant-alert-success {
background-color: #f6ffed;
border: 1px solid #b7eb8f;
}
.ant-alert-error {
background-color: #fff2f0;
border: 1px solid #ffccc7;
}
.ant-alert-warning {
background-color: #fffbe6;
border: 1px solid #ffe58f;
}
:not(:root):fullscreen::backdrop {
background-color: @layout-body-background !important;
}
} }
html[data-theme='dark'] { [data-theme='dark'] {
.text-secondary { .text-secondary {
color: #8b949e; color: #8b949e;
} }
@ -23,14 +42,11 @@ html[data-theme='dark'] {
0 1px 0 0 #434343 inset; 0 1px 0 0 #434343 inset;
} }
.ant-alert-message, .ant-calendar-selected-day .ant-calendar-date {
.ant-alert-with-description .ant-alert-message, color: rgba(0, 0, 0, 0.8);
.ant-alert-description {
color: rgba(0, 0, 0, 0.85);
} }
.ant-checkbox-checked .ant-checkbox-inner::after { .ant-select-tree li .ant-select-tree-node-content-wrapper.ant-select-tree-node-selected {
border-top: 0; color: rgba(0, 0, 0, 0.9);
border-left: 0;
} }
} }

View File

@ -3,6 +3,7 @@ import type { Ref } from 'vue';
interface Params { interface Params {
excludeListeners?: boolean; excludeListeners?: boolean;
excludeKeys?: string[]; excludeKeys?: string[];
excludeDefaultKeys?: boolean;
} }
const DEFAULT_EXCLUDE_KEYS = ['class', 'style']; const DEFAULT_EXCLUDE_KEYS = ['class', 'style'];
@ -16,9 +17,9 @@ export function useAttrs(params: Params = {}): Ref<Recordable> | {} {
const instance = getCurrentInstance(); const instance = getCurrentInstance();
if (!instance) return {}; if (!instance) return {};
const { excludeListeners = false, excludeKeys = [] } = params; const { excludeListeners = false, excludeKeys = [], excludeDefaultKeys = true } = params;
const attrs = shallowRef({}); const attrs = shallowRef({});
const allExcludeKeys = excludeKeys.concat(DEFAULT_EXCLUDE_KEYS); const allExcludeKeys = excludeKeys.concat(excludeDefaultKeys ? DEFAULT_EXCLUDE_KEYS : []);
// Since attrs are not reactive, make it reactive instead of doing in `onUpdated` hook for better performance // Since attrs are not reactive, make it reactive instead of doing in `onUpdated` hook for better performance
instance.attrs = reactive(instance.attrs); instance.attrs = reactive(instance.attrs);

View File

@ -1,7 +1,8 @@
import { ref, onBeforeUpdate, Ref } from 'vue'; import type { Ref } from 'vue';
import { ref, onBeforeUpdate } from 'vue';
export function useRefs(): [Ref<HTMLElement[]>, (index: number) => (el: HTMLElement) => void] { export function useRefs(): [Ref<HTMLElement[]>, (index: number) => (el: HTMLElement) => void] {
const refs = ref<HTMLElement[]>([]); const refs = ref([]) as Ref<HTMLElement[]>;
onBeforeUpdate(() => { onBeforeUpdate(() => {
refs.value = []; refs.value = [];

View File

@ -1,10 +1,8 @@
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import { ref, watch, unref } from 'vue'; import { ref, watch, unref } from 'vue';
import { useThrottleFn, useDebounceFn } from '@vueuse/core'; import { useThrottleFn, useDebounceFn } from '@vueuse/core';
export type RemoveEventFn = () => void; export type RemoveEventFn = () => void;
export interface UseEventParams { export interface UseEventParams {
el?: Element | Ref<Element | undefined> | Window | any; el?: Element | Ref<Element | undefined> | Window | any;
name: string; name: string;
@ -28,7 +26,7 @@ export function useEventListener({
const isAddRef = ref(false); const isAddRef = ref(false);
if (el) { if (el) {
const element: Ref<Element> = ref(el as Element); const element = ref(el as Element) as Ref<Element>;
const handler = isDebounce ? useDebounceFn(listener, wait) : useThrottleFn(listener, wait); const handler = isDebounce ? useDebounceFn(listener, wait) : useThrottleFn(listener, wait);
const realHandler = wait ? handler : listener; const realHandler = wait ? handler : listener;

View File

@ -1,13 +1,11 @@
import type { EChartsOption } from 'echarts'; import type { EChartsOption } from 'echarts';
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import { useTimeoutFn } from '/@/hooks/core/useTimeout'; import { useTimeoutFn } from '/@/hooks/core/useTimeout';
import { tryOnUnmounted } from '@vueuse/core'; import { tryOnUnmounted } from '@vueuse/core';
import { unref, nextTick, watch, computed, ref } from 'vue'; import { unref, nextTick, watch, computed, ref } from 'vue';
import { useDebounceFn } from '@vueuse/core'; import { useDebounceFn } from '@vueuse/core';
import { useEventListener } from '/@/hooks/event/useEventListener'; import { useEventListener } from '/@/hooks/event/useEventListener';
import { useBreakpoint } from '/@/hooks/event/useBreakpoint'; import { useBreakpoint } from '/@/hooks/event/useBreakpoint';
import echarts from '/@/utils/lib/echarts'; import echarts from '/@/utils/lib/echarts';
import { useRootSetting } from '/@/hooks/setting/useRootSetting'; import { useRootSetting } from '/@/hooks/setting/useRootSetting';
@ -18,19 +16,19 @@ export function useECharts(
const { getDarkMode } = useRootSetting(); const { getDarkMode } = useRootSetting();
let chartInstance: echarts.ECharts | null = null; let chartInstance: echarts.ECharts | null = null;
let resizeFn: Fn = resize; let resizeFn: Fn = resize;
const cacheOptions = ref<EChartsOption>({}); const cacheOptions = ref({}) as Ref<EChartsOption>;
let removeResizeFn: Fn = () => {}; let removeResizeFn: Fn = () => {};
resizeFn = useDebounceFn(resize, 200); resizeFn = useDebounceFn(resize, 200);
const getOptions = computed((): EChartsOption => { const getOptions = computed(() => {
if (getDarkMode.value !== 'dark') { if (getDarkMode.value !== 'dark') {
return cacheOptions.value; return cacheOptions.value as EChartsOption;
} }
return { return {
backgroundColor: 'transparent', backgroundColor: 'transparent',
...cacheOptions.value, ...cacheOptions.value,
}; } as EChartsOption;
}); });
function initCharts(t = theme) { function initCharts(t = theme) {

View File

@ -3,17 +3,17 @@ import type { ModalFunc, ModalFuncProps } from 'ant-design-vue/lib/modal/Modal';
import { Modal, message as Message, notification } from 'ant-design-vue'; import { Modal, message as Message, notification } from 'ant-design-vue';
import { InfoCircleFilled, CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons-vue'; import { InfoCircleFilled, CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons-vue';
import { ArgsProps, ConfigProps } from 'ant-design-vue/lib/notification'; import { NotificationArgsProps, ConfigProps } from 'ant-design-vue/lib/notification';
import { useI18n } from './useI18n'; import { useI18n } from './useI18n';
import { isString } from '/@/utils/is'; import { isString } from '/@/utils/is';
export interface NotifyApi { export interface NotifyApi {
info(config: ArgsProps): void; info(config: NotificationArgsProps): void;
success(config: ArgsProps): void; success(config: NotificationArgsProps): void;
error(config: ArgsProps): void; error(config: NotificationArgsProps): void;
warn(config: ArgsProps): void; warn(config: NotificationArgsProps): void;
warning(config: ArgsProps): void; warning(config: NotificationArgsProps): void;
open(args: ArgsProps): void; open(args: NotificationArgsProps): void;
close(key: String): void; close(key: String): void;
config(options: ConfigProps): void; config(options: ConfigProps): void;
destroy(): void; destroy(): void;

View File

@ -5,6 +5,7 @@ import { isString } from '/@/utils/is';
import { unref } from 'vue'; import { unref } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { REDIRECT_NAME } from '/@/router/constant';
export type RouteLocationRawEx = Omit<RouteLocationRaw, 'path'> & { path: PageEnum }; export type RouteLocationRawEx = Omit<RouteLocationRaw, 'path'> & { path: PageEnum };
@ -37,19 +38,18 @@ export function useGo(_router?: Router) {
* @description: redo current page * @description: redo current page
*/ */
export const useRedo = (_router?: Router) => { export const useRedo = (_router?: Router) => {
let router; const { push, currentRoute } = _router || useRouter();
if (!_router) { const { query, params = {}, name, fullPath } = unref(currentRoute.value);
router = useRouter();
}
const { push, currentRoute } = _router || router;
const { query, params } = currentRoute.value;
function redo(): Promise<boolean> { function redo(): Promise<boolean> {
return new Promise((resolve) => { return new Promise((resolve) => {
push({ if (name && Object.keys(params).length > 0) {
path: '/redirect' + unref(currentRoute).fullPath, params['_redirect_type'] = 'name';
query, params['path'] = String(name);
params, } else {
}).then(() => resolve(true)); params['_redirect_type'] = 'path';
params['path'] = fullPath;
}
push({ name: REDIRECT_NAME, params, query }).then(() => resolve(true));
}); });
} }
return redo; return redo;

View File

@ -1,4 +1,4 @@
import { onMounted, ref } from 'vue'; import { onMounted, onUnmounted, ref } from 'vue';
interface ScriptOptions { interface ScriptOptions {
src: string; src: string;
@ -8,10 +8,11 @@ export function useScript(opts: ScriptOptions) {
const isLoading = ref(false); const isLoading = ref(false);
const error = ref(false); const error = ref(false);
const success = ref(false); const success = ref(false);
let script: HTMLScriptElement;
const promise = new Promise((resolve, reject) => { const promise = new Promise((resolve, reject) => {
onMounted(() => { onMounted(() => {
const script = document.createElement('script'); script = document.createElement('script');
script.type = 'text/javascript'; script.type = 'text/javascript';
script.onload = function () { script.onload = function () {
isLoading.value = false; isLoading.value = false;
@ -32,6 +33,10 @@ export function useScript(opts: ScriptOptions) {
}); });
}); });
onUnmounted(() => {
script && script.remove();
});
return { return {
isLoading, isLoading,
error, error,

View File

@ -46,6 +46,15 @@ export function useTabs(_router?: Router) {
await tabStore.setTabTitle(title, targetTab); await tabStore.setTabTitle(title, targetTab);
} }
async function updateTabPath(path: string, tab?: RouteLocationNormalized) {
const canIUse = canIUseTabs;
if (!canIUse) {
return;
}
const targetTab = tab || getCurrentTab();
await tabStore.updateTabPath(path, targetTab);
}
async function handleTabAction(action: TableActionEnum, tab?: RouteLocationNormalized) { async function handleTabAction(action: TableActionEnum, tab?: RouteLocationNormalized) {
const canIUse = canIUseTabs; const canIUse = canIUseTabs;
if (!canIUse) { if (!canIUse) {
@ -87,9 +96,8 @@ export function useTabs(_router?: Router) {
closeRight: () => handleTabAction(TableActionEnum.CLOSE_RIGHT), closeRight: () => handleTabAction(TableActionEnum.CLOSE_RIGHT),
closeOther: () => handleTabAction(TableActionEnum.CLOSE_OTHER), closeOther: () => handleTabAction(TableActionEnum.CLOSE_OTHER),
closeCurrent: () => handleTabAction(TableActionEnum.CLOSE_CURRENT), closeCurrent: () => handleTabAction(TableActionEnum.CLOSE_CURRENT),
close: (tab?: RouteLocationNormalized) => { close: (tab?: RouteLocationNormalized) => handleTabAction(TableActionEnum.CLOSE, tab),
handleTabAction(TableActionEnum.CLOSE, tab);
},
setTitle: (title: string, tab?: RouteLocationNormalized) => updateTabTitle(title, tab), setTitle: (title: string, tab?: RouteLocationNormalized) => updateTabTitle(title, tab),
updatePath: (fullPath: string, tab?: RouteLocationNormalized) => updateTabPath(fullPath, tab),
}; };
} }

View File

@ -11,7 +11,9 @@
:style="{ cursor: isTitleClickable ? 'pointer' : '' }" :style="{ cursor: isTitleClickable ? 'pointer' : '' }"
:delete="!!item.titleDelete" :delete="!!item.titleDelete"
:ellipsis=" :ellipsis="
$props.titleRows > 0 ? { rows: $props.titleRows, tooltip: item.title } : false $props.titleRows && $props.titleRows > 0
? { rows: $props.titleRows, tooltip: !!item.title }
: false
" "
:content="item.title" :content="item.title"
/> />
@ -34,8 +36,8 @@
<a-typography-paragraph <a-typography-paragraph
style="width: 100%; margin-bottom: 0 !important" style="width: 100%; margin-bottom: 0 !important"
:ellipsis=" :ellipsis="
$props.descRows > 0 $props.descRows && $props.descRows > 0
? { rows: $props.descRows, tooltip: item.description } ? { rows: $props.descRows, tooltip: !!item.description }
: false : false
" "
:content="item.description" :content="item.description"
@ -97,7 +99,6 @@
const current = ref(props.currentPage || 1); const current = ref(props.currentPage || 1);
const getData = computed(() => { const getData = computed(() => {
const { pageSize, list } = props; const { pageSize, list } = props;
console.log('refreshData', list);
if (pageSize === false) return []; if (pageSize === false) return [];
let size = isNumber(pageSize) ? pageSize : 5; let size = isNumber(pageSize) ? pageSize : 5;
return list.slice(size * (unref(current) - 1), size * unref(current)); return list.slice(size * (unref(current) - 1), size * unref(current));

View File

@ -181,6 +181,12 @@
font-size: 16px !important; font-size: 16px !important;
} }
.ant-badge {
span {
color: @white;
}
}
&:hover { &:hover {
background-color: @header-dark-bg-hover-color; background-color: @header-dark-bg-hover-color;
} }

View File

@ -1,6 +1,6 @@
import { genMessage } from '../helper'; import { genMessage } from '../helper';
import antdLocale from 'ant-design-vue/es/locale/en_US'; import antdLocale from 'ant-design-vue/es/locale/en_US';
import momentLocale from 'moment/dist/locale/eu'; // import momentLocale from 'moment/dist/locale/en-us';
const modules = import.meta.globEager('./en/**/*.ts'); const modules = import.meta.globEager('./en/**/*.ts');
export default { export default {
@ -8,6 +8,6 @@ export default {
...genMessage(modules, 'en'), ...genMessage(modules, 'en'),
antdLocale, antdLocale,
}, },
momentLocale, momentLocale: null,
momentLocaleName: 'eu', momentLocaleName: 'en',
}; };

View File

@ -20,6 +20,7 @@ export default {
btn_scale_y: 'Flip vertical', btn_scale_y: 'Flip vertical',
btn_zoom_in: 'Zoom in', btn_zoom_in: 'Zoom in',
btn_zoom_out: 'Zoom out', btn_zoom_out: 'Zoom out',
preview: 'Preivew',
}, },
drawer: { drawer: {
loadingText: 'Loading...', loadingText: 'Loading...',

View File

@ -20,6 +20,7 @@ export default {
btn_scale_y: '垂直翻转', btn_scale_y: '垂直翻转',
btn_zoom_in: '放大', btn_zoom_in: '放大',
btn_zoom_out: '缩小', btn_zoom_out: '缩小',
preview: '预览',
}, },
drawer: { drawer: {
loadingText: '加载中...', loadingText: '加载中...',

Some files were not shown because too many files have changed in this diff Show More