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

This commit is contained in:
shisan 2021-09-14 00:26:52 +08:00
commit 9d3dc3c4d2
173 changed files with 2392 additions and 1370 deletions

2
.vscode/launch.json vendored
View File

@ -8,6 +8,6 @@
"url": "http://localhost:3100", "url": "http://localhost:3100",
"webRoot": "${workspaceFolder}/src", "webRoot": "${workspaceFolder}/src",
"sourceMaps": true "sourceMaps": true
}, }
] ]
} }

View File

@ -1,3 +1,31 @@
## 2.7.2(2021-09-14)
### ✨ Features
- **BasicForm** New `Divider` in the form component for dividing the area of longer forms
- **BasicTable**
- Cell editor adds submit callback, which will decide whether to submit data to the form based on the result returned by the callback function
- Add check method for row editing, allowing only check but not submit value, so asynchronously save data successfully before submit to table
- Fix the problem that the `rowClassName` property cannot be used at the same time as `striped`.
- New component **MarkdownViewer** for displaying rich text in Markdown format
### 🐛 Bug Fixes
- **CodeEditor** Fix JSON editor throwing exception when formatting invalid JSON text
- **Tinymce** fixes an issue where inline mode throws an exception in some scenarios
- **BasicTable**
- Repair the problem that the editing icon is not displayed when the content of editable cell is empty
- Repair the problem that the total row at the end of the table sometimes fails to align with the columns in the main part of the table.
- **MarkDown** Repair the problem that the value of initial value property does not work.
- **BasicUpload** Repair the problem that `accept` property does not support `MIME` and suffix name starting with dot.
- **ApiSelect** Fix the problem of type definition of `value` property.
- **Other**
- Repair the problem that some wrapper components give error when using slots.
- Repair the problem that `theme` parameter of `useECharts` does not work.
- Repair the problem that when `Token` is invalid, pressing F5 to refresh the page may cause abnormal page loading.
- Repair the problem that the improper call of `useRedo` may lead to `path` redirection abnormality.
- Repair the problem that `vite` custom mode name does not support underscore.
## 2.7.1(2021-08-16) ## 2.7.1(2021-08-16)
- Upgrade vue 3.2, if the operation fails, delete node_modules and reinstall it - Upgrade vue 3.2, if the operation fails, delete node_modules and reinstall it

View File

@ -1,3 +1,57 @@
## [2.7.2](https://github.com/anncwb/vue-vben-admin/compare/v2.7.1...v2.7.2) (2021-09-13)
### Bug Fixes
- fixed token clear error ([9640484](https://github.com/anncwb/vue-vben-admin/commit/96404848955f84d57b88dd240ab3a57b7017103c))
- improve type introduction, fix [#1196](https://github.com/anncwb/vue-vben-admin/issues/1196) ([2820d5a](https://github.com/anncwb/vue-vben-admin/commit/2820d5a627260bb8eddfcd25df1cd7d1196932e8))
- **api-select:** fixed `value` prop define ([f87b0f2](https://github.com/anncwb/vue-vben-admin/commit/f87b0f2f5efe4e9977c4cc0742dbcaefbad2ca02)), closes [#1175](https://github.com/anncwb/vue-vben-admin/issues/1175)
- **card-list:** fixed build error ([628e820](https://github.com/anncwb/vue-vben-admin/commit/628e820684ce5d81f130548505efe83e8d516131))
- **code-editor:** fixed formatting error ([e7c9636](https://github.com/anncwb/vue-vben-admin/commit/e7c96363a1963b7733a9ee498403eb6a062160e6))
- **echarts:** theme setting supported ([93812f7](https://github.com/anncwb/vue-vben-admin/commit/93812f734ec85529aa27fc3100a2eaef8c7a6df5)), closes [#1095](https://github.com/anncwb/vue-vben-admin/issues/1095)
- **markdown:** the hierarchy of markDown components after full screen ([c8017b1](https://github.com/anncwb/vue-vben-admin/commit/c8017b1365ea49f95a26148a539f8c30d8a8631f))
- **markdown:** `value` not worked on init ([0bb9c03](https://github.com/anncwb/vue-vben-admin/commit/0bb9c035f77588c58d36b3fd45d89b9730cd70d7))
- **modal:** avoid style pollution to the whole world ([#1128](https://github.com/anncwb/vue-vben-admin/issues/1128)) ([6e7f6f8](https://github.com/anncwb/vue-vben-admin/commit/6e7f6f82ed2819e02e2b3114884e665d0762d7e9))
- **table:** `rowClassName` not worked with `striped` ([044e2e4](https://github.com/anncwb/vue-vben-admin/commit/044e2e4e866dd5b120daab03c47aba1ca1f9140a)), closes [#1167](https://github.com/anncwb/vue-vben-admin/issues/1167)
- **table:** 修复表格背景颜色再深色模式下会被穿透问题 ([#1133](https://github.com/anncwb/vue-vben-admin/issues/1133)) ([30fa4cf](https://github.com/anncwb/vue-vben-admin/commit/30fa4cfa2ab6229efc67224fd082e32da0a95d49))
- **table:** editable icon not show with empty cell ([edc3096](https://github.com/anncwb/vue-vben-admin/commit/edc30965653831b4572c5d5e067f556f4757ce75)), closes [#1103](https://github.com/anncwb/vue-vben-admin/issues/1103)
- **table:** fix table footer style ([a426b90](https://github.com/anncwb/vue-vben-admin/commit/a426b9027ef524f9033d510d0c74cd17b2ad5bcf)), closes [#1112](https://github.com/anncwb/vue-vben-admin/issues/1112)
- **table:** Solve the bug of setting ifshow to false in table column ([#1166](https://github.com/anncwb/vue-vben-admin/issues/1166)) ([5fa730c](https://github.com/anncwb/vue-vben-admin/commit/5fa730c49ae46fa448d49d597dc7b2b6a019b268))
- **table-action:** `divider` not work as expected ([7593ef6](https://github.com/anncwb/vue-vben-admin/commit/7593ef6a4f081ed800658b70316ab2f1e3ee631d))
- **tinymce:** fixed `inline` mode ([8e01377](https://github.com/anncwb/vue-vben-admin/commit/8e01377481a34cda221de6bbb01fc7d5b2824c82)), closes [#1092](https://github.com/anncwb/vue-vben-admin/issues/1092)
- **upload:** `accept` not work as expected ([656ee4e](https://github.com/anncwb/vue-vben-admin/commit/656ee4e5c9b363b6ab59aa071915414e5ee95de4))
- `getUserinfo` is compatible with empty roles data ([1ddfc31](https://github.com/anncwb/vue-vben-admin/commit/1ddfc31c3c4c792c5f741f6d0f0754ffc9a6613c))
- `slots` worked in `basicTable` and `basicModal` ([5138e44](https://github.com/anncwb/vue-vben-admin/commit/5138e447e74ef01309457d22f44129c8b1b2f815))
- `useRedo` called duplicate may cause exception ([1235978](https://github.com/anncwb/vue-vben-admin/commit/1235978ab23740dfb11e3de7ac26a7d10a4899dc)), closes [#1121](https://github.com/anncwb/vue-vben-admin/issues/1121)
- 修复 `apiSelect` 绑定值 `attrs` 的问题 ([#1172](https://github.com/anncwb/vue-vben-admin/issues/1172)) ([c753d94](https://github.com/anncwb/vue-vben-admin/commit/c753d945e08f72cab5bc8a585601cab6a0523fca))
- 修复弹窗全屏按钮异常关闭的问题([#1177](https://github.com/anncwb/vue-vben-admin/issues/1177)) ([#1182](https://github.com/anncwb/vue-vben-admin/issues/1182)) ([9e9ea3f](https://github.com/anncwb/vue-vben-admin/commit/9e9ea3f43d8c4b88649c1998bf89186b5f7ee6a2))
- 修改 axios 中 urlPrefix 字段不生效问题 ([#1170](https://github.com/anncwb/vue-vben-admin/issues/1170)) ([7df9b51](https://github.com/anncwb/vue-vben-admin/commit/7df9b513447d8deab2fd8e86fa23c807adb6d440))
- add loss action for userStore ([a36825a](https://github.com/anncwb/vue-vben-admin/commit/a36825a6d423aae9aaf1936ce55947ba8c2104b0))
- fix all types of errors, compatible with volar plugin ([e15b4f1](https://github.com/anncwb/vue-vben-admin/commit/e15b4f14db51812effd55670b3d2da7b082e00a7))
- fixed build warning for style of `intro.js` ([d27633f](https://github.com/anncwb/vue-vben-admin/commit/d27633fb31824e92cbeb24f8d626d8e33ce7179e)), closes [#1130](https://github.com/anncwb/vue-vben-admin/issues/1130)
- Improve content height calculation ([#1136](https://github.com/anncwb/vue-vben-admin/issues/1136)) ([6717fe6](https://github.com/anncwb/vue-vben-admin/commit/6717fe654e88e6a939a16c523832870388ec1886))
- name of vite `mode` support more characters ([9f68229](https://github.com/anncwb/vue-vben-admin/commit/9f6822991c4b2da78e0a5d0c7d6e0288f0d9d1cb)), closes [#1115](https://github.com/anncwb/vue-vben-admin/issues/1115)
- refresh failed while token invalid ([3a5d1a5](https://github.com/anncwb/vue-vben-admin/commit/3a5d1a5757c0a2be17e6dd370cbb023ddbb30d5e)), closes [#1101](https://github.com/anncwb/vue-vben-admin/issues/1101)
- warning in logout action ([b3307fe](https://github.com/anncwb/vue-vben-admin/commit/b3307fe2836fb6f9806d602d5bdb7e540c49f1b0))
- **tinymce:** fixed `tinymce` destory method ([fb43fad](https://github.com/anncwb/vue-vben-admin/commit/fb43fad555b093af23194bdb3670bc1347c0010f))
### Features
- **demo:** add `JsonPreview` demo ([83c1683](https://github.com/anncwb/vue-vben-admin/commit/83c1683bfdcf4ea33de771895b46e41f276969e8)), closes [#1146](https://github.com/anncwb/vue-vben-admin/issues/1146)
- **form:** add `Divider` for schema component type ([47a448b](https://github.com/anncwb/vue-vben-admin/commit/47a448b8aea572e54dac97dc4f9fb6c1c005685a))
- **form:** component `Divider` support `helpMessage` ([a5ff592](https://github.com/anncwb/vue-vben-admin/commit/a5ff59237f2eb6ea4c1770acc594c75bf1f6e95f))
- **markdown-viewer:** add new component ([73dc492](https://github.com/anncwb/vue-vben-admin/commit/73dc492b2a49793d945ccdae7f5c429c874f298c)), closes [#1181](https://github.com/anncwb/vue-vben-admin/issues/1181)
- **table:** 添加和支持动态删除和插入数据 ([#1152](https://github.com/anncwb/vue-vben-admin/issues/1152)) ([59a9087](https://github.com/anncwb/vue-vben-admin/commit/59a90877287a289f746eec97d12c2d3a1d5476b0))
- **table:** add `beforeEditSubmit` for editable cell ([2c867b3](https://github.com/anncwb/vue-vben-admin/commit/2c867b3d636d57cdc526a4ca600af7d747b7d833))
- **table:** add `onValid` for editRow ([ee7c31d](https://github.com/anncwb/vue-vben-admin/commit/ee7c31db44fd8f99f0d26da368e1d82b5630f309))
- **tree:** 1. 添加自定义数据过滤判断方法 2. 添加搜索完成自动展开结果选项 3. 添加搜索完成自动选中结果选项 4. 树节点数据变化时强制搜索(同步 searchData 避免展示错误) ([#1132](https://github.com/anncwb/vue-vben-admin/issues/1132)) ([e00578c](https://github.com/anncwb/vue-vben-admin/commit/e00578c40a585a4a35f235c0228aebaf62cea1ba))
- add CardList component ([0f5ddbf](https://github.com/anncwb/vue-vben-admin/commit/0f5ddbf1ec777fc238a94bd037d37ea787316757))
### Performance Improvements
- **tree:** 优化 Tree 搜索功能,添加搜索高亮功能,优化样式表现 ([#1153](https://github.com/anncwb/vue-vben-admin/issues/1153)) ([3b6b4f7](https://github.com/anncwb/vue-vben-admin/commit/3b6b4f73033e8757fd3a032f0910dfcc30dee151))
- not waiting for router.isReady ([2884e86](https://github.com/anncwb/vue-vben-admin/commit/2884e863ce826cd92cd782f40cdee31588bc6d32))
- optimize css volume ([466d4ed](https://github.com/anncwb/vue-vben-admin/commit/466d4edcd02fc91e2b4cdbbc3c501bfd2fde7a3d))
## [2.7.1](https://github.com/anncwb/vue-vben-admin/compare/v2.6.1...v2.7.1) (2021-08-16) ## [2.7.1](https://github.com/anncwb/vue-vben-admin/compare/v2.6.1...v2.7.1) (2021-08-16)
### Bug Fixes ### Bug Fixes

View File

@ -1,3 +1,31 @@
## 2.7.2(2021-09-14)
### ✨ Features
- **BasicForm** 表单组件新增`Divider`,用于较长表单的区域分割
- **BasicTable**
- 单元格编辑新增提交回调,将根据回调函数返回的结果来决定是否将数据提交到表格
- 行编辑添加校验方法,允许只校验而不提交值,以便异步保存数据成功后才提交倒表格
- 修复`rowClassName`属性无法和`striped`同时使用的问题
- 新增组件 **MarkdownViewer** 用于显示 Markdown 格式的富文本
### 🐛 Bug Fixes
- **CodeEditor** 修复 JSON 编辑器在格式化无效 JSON 文本时会抛出异常的问题
- **Tinymce** 修复 inline 模式在一些场景下会出现异常的问题
- **BasicTable**
- 修复可编辑单元格的内容为空时,不会显示编辑图标的问题
- 修复表尾合计行与表格主体部分的列有时候未能对齐的问题
- **MarkDown** 修复初始 value 属性的值不起作用的问题
- **BasicUpload** 修复`accept`属性不支持`MIME`及点开头的后缀名的问题
- **ApiSelect** 修复`value`属性的类型定义问题
- **其它**
- 修复部分封装组件在使用插槽时报错的问题
- 修复`useECharts`的`theme`参数不起作用的问题
- 修复`Token`失效时,按 F5 刷新页面可能会出现页面加载异常的问题
- 修复`useRedo`的不当调用可能会导致重定向`path`异常的问题
- 修复`vite`自定义模式名称不支持下划线的问题
## 2.7.1(2021-08-16) ## 2.7.1(2021-08-16)
- 升级 vue 3.2,如果运行失败,删除 node_modules 后重装即可 - 升级 vue 3.2,如果运行失败,删除 node_modules 后重装即可

View File

@ -150,6 +150,7 @@ yarn build
## 后台整合示例 ## 后台整合示例
- [lamp-cloud](https://github.com/zuihou/lamp-cloud) - 基于 SpringCloud Alibaba 的微服务中后台快速开发平台 - [lamp-cloud](https://github.com/zuihou/lamp-cloud) - 基于 SpringCloud Alibaba 的微服务中后台快速开发平台
- [matecloud](https://github.com/matevip/matecloud) - MateCloud 微服务脚手架,基于 Spring Cloud 2020.0.3、SpringBoot 2.5.3 的全开源平台
## 维护者 ## 维护者

View File

@ -52,19 +52,19 @@ async function generateIcon() {
const { prefix } = data; const { prefix } = data;
const isLocal = useType === 'local'; const isLocal = useType === 'local';
const icons = Object.keys(data.icons).map( const icons = Object.keys(data.icons).map(
(item) => `${isLocal ? prefix + ':' : ''}${item}` (item) => `${isLocal ? prefix + ':' : ''}${item}`,
); );
await fs.writeFileSync( await fs.writeFileSync(
path.join(output, `icons.data.ts`), path.join(output, `icons.data.ts`),
`export default ${isLocal ? JSON.stringify(icons) : JSON.stringify({ prefix, icons })}` `export default ${isLocal ? JSON.stringify(icons) : JSON.stringify({ prefix, icons })}`,
); );
prefixSet.push(prefix); prefixSet.push(prefix);
} }
} }
fs.emptyDir(path.join(process.cwd(), 'node_modules/.vite')); fs.emptyDir(path.join(process.cwd(), 'node_modules/.vite'));
console.log( console.log(
`${chalk.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]` `${chalk.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]`,
); );
}); });
} }

View File

@ -15,7 +15,7 @@ function createConfig(
configName, configName,
config, config,
configFileName = GLOB_CONFIG_FILE_NAME, configFileName = GLOB_CONFIG_FILE_NAME,
}: { configName: string; config: any; configFileName?: string } = { configName: '', config: {} } }: { configName: string; config: any; configFileName?: string } = { configName: '', config: {} },
) { ) {
try { try {
const windowConf = `window.${configName}`; const windowConf = `window.${configName}`;

View File

@ -50,7 +50,7 @@ export function wrapperEnv(envConf: Recordable): ViteEnv {
*/ */
function getConfFiles() { function getConfFiles() {
const script = process.env.npm_lifecycle_script; const script = process.env.npm_lifecycle_script;
const reg = new RegExp('--mode ([a-z]+)'); const reg = new RegExp('--mode ([a-z_\\d]+)');
const result = reg.exec(script as string) as any; const result = reg.exec(script as string) as any;
if (result) { if (result) {
const mode = result[1] as string; const mode = result[1] as string;

View File

@ -3,12 +3,11 @@
* https://github.com/anncwb/vite-plugin-compression * https://github.com/anncwb/vite-plugin-compression
*/ */
import type { Plugin } from 'vite'; import type { Plugin } from 'vite';
import compressPlugin from 'vite-plugin-compression'; import compressPlugin from 'vite-plugin-compression';
export function configCompressPlugin( export function configCompressPlugin(
compress: 'gzip' | 'brotli' | 'none', compress: 'gzip' | 'brotli' | 'none',
deleteOriginFile = false deleteOriginFile = false,
): Plugin | Plugin[] { ): Plugin | Plugin[] {
const compressList = compress.split(','); const compressList = compress.split(',');
@ -19,16 +18,17 @@ export function configCompressPlugin(
compressPlugin({ compressPlugin({
ext: '.gz', ext: '.gz',
deleteOriginFile, deleteOriginFile,
}) }),
); );
} }
if (compressList.includes('brotli')) { if (compressList.includes('brotli')) {
plugins.push( plugins.push(
compressPlugin({ compressPlugin({
ext: '.br', ext: '.br',
algorithm: 'brotliCompress', algorithm: 'brotliCompress',
deleteOriginFile, deleteOriginFile,
}) }),
); );
} }
return plugins; return plugins;

View File

@ -3,9 +3,7 @@
* https://github.com/anncwb/vite-plugin-html * https://github.com/anncwb/vite-plugin-html
*/ */
import type { Plugin } from 'vite'; import type { Plugin } from 'vite';
import html from 'vite-plugin-html'; import html from 'vite-plugin-html';
import pkg from '../../../package.json'; import pkg from '../../../package.json';
import { GLOB_CONFIG_FILE_NAME } from '../../constant'; import { GLOB_CONFIG_FILE_NAME } from '../../constant';
@ -22,7 +20,7 @@ export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
minify: isBuild, minify: isBuild,
inject: { inject: {
// Inject data into ejs template // Inject data into ejs template
injectData: { data: {
title: VITE_GLOB_APP_TITLE, title: VITE_GLOB_APP_TITLE,
}, },
// Embed the generated app.config.js file // Embed the generated app.config.js file

View File

@ -1,6 +1,5 @@
// Image resource files used to compress the output of the production environment // Image resource files used to compress the output of the production environment
// https://github.com/anncwb/vite-plugin-imagemin // https://github.com/anncwb/vite-plugin-imagemin
import viteImagemin from 'vite-plugin-imagemin'; import viteImagemin from 'vite-plugin-imagemin';
export function configImageminPlugin() { export function configImageminPlugin() {

View File

@ -1,11 +1,10 @@
import type { Plugin } from 'vite'; import type { Plugin } from 'vite';
import vue from '@vitejs/plugin-vue'; import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx'; 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 windiCSS from 'vite-plugin-windicss';
import vueSetupExtend from 'vite-plugin-vue-setup-extend';
import { configHtmlPlugin } from './html'; import { configHtmlPlugin } from './html';
import { configPwaConfig } from './pwa'; import { configPwaConfig } from './pwa';
import { configMockPlugin } from './mock'; import { configMockPlugin } from './mock';
@ -31,6 +30,8 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
vue(), vue(),
// have to // have to
vueJsx(), vueJsx(),
// support name
vueSetupExtend(),
]; ];
// vite-plugin-windicss // vite-plugin-windicss
@ -70,7 +71,7 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
// rollup-plugin-gzip // rollup-plugin-gzip
vitePlugins.push( vitePlugins.push(
configCompressPlugin(VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE) configCompressPlugin(VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE),
); );
// vite-plugin-pwa // vite-plugin-pwa

View File

@ -2,7 +2,6 @@
* Zero-config PWA for Vite * Zero-config PWA for Vite
* https://github.com/antfu/vite-plugin-pwa * https://github.com/antfu/vite-plugin-pwa
*/ */
import { VitePWA } from 'vite-plugin-pwa'; import { VitePWA } from 'vite-plugin-pwa';
export function configPwaConfig(env: ViteEnv) { export function configPwaConfig(env: ViteEnv) {

View File

@ -2,11 +2,12 @@
* Introduces component library styles on demand. * Introduces component library styles on demand.
* https://github.com/anncwb/vite-plugin-style-import * https://github.com/anncwb/vite-plugin-style-import
*/ */
import styleImport from 'vite-plugin-style-import'; import styleImport from 'vite-plugin-style-import';
export function configStyleImportPlugin(isBuild: boolean) { export function configStyleImportPlugin(isBuild: boolean) {
if (!isBuild) return []; if (!isBuild) {
return [];
}
const styleImportPlugin = styleImport({ const styleImportPlugin = styleImport({
libs: [ libs: [
{ {

View File

@ -66,7 +66,7 @@ export function configThemePlugin(isBuild: boolean): Plugin[] {
'border-color-base': '#303030', 'border-color-base': '#303030',
// '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': '#1e1e1e',
'tree-node-selected-bg': '#11263c', 'tree-node-selected-bg': '#11263c',
'alert-success-border-color': '#274916', 'alert-success-border-color': '#274916',

View File

@ -13,7 +13,7 @@ export function resultPageSuccess<T = any>(
page: number, page: number,
pageSize: number, pageSize: number,
list: T[], list: T[],
{ message = 'ok' } = {} { message = 'ok' } = {},
) { ) {
const pageData = pagination(page, pageSize, list); const pageData = pagination(page, pageSize, list);

View File

@ -52,7 +52,7 @@ export default [
response: ({ body }) => { response: ({ body }) => {
const { username, password } = body; const { username, password } = body;
const checkUser = createFakeUserList().find( const checkUser = createFakeUserList().find(
(item) => item.username === username && password === item.password (item) => item.username === username && password === item.password,
); );
if (!checkUser) { if (!checkUser) {
return resultError('Incorrect account or password'); return resultError('Incorrect account or password');

View File

@ -1,6 +1,6 @@
{ {
"name": "vben-admin", "name": "vben-admin",
"version": "2.7.1", "version": "2.7.2",
"author": { "author": {
"name": "vben", "name": "vben",
"email": "anncwb@126.com", "email": "anncwb@126.com",
@ -34,55 +34,53 @@
"gen:icon": "esno ./build/generate/icon/index.ts" "gen:icon": "esno ./build/generate/icon/index.ts"
}, },
"dependencies": { "dependencies": {
"@iconify/iconify": "^2.0.3", "@iconify/iconify": "^2.0.4",
"@logicflow/core": "^0.6.9", "@vueuse/core": "^6.3.3",
"@logicflow/extension": "^0.6.9",
"@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.6", "ant-design-vue": "2.2.7",
"axios": "^0.21.1", "axios": "^0.21.4",
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"echarts": "^5.1.2", "echarts": "^5.2.0",
"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-rc.4", "pinia": "2.0.0-rc.9",
"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.2.2", "vue": "3.2.11",
"vue-i18n": "9.1.7", "vue-i18n": "9.1.7",
"vue-router": "^4.0.11", "vue-router": "^4.0.11",
"vue-types": "^4.0.3" "vue-types": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^13.1.0", "@commitlint/cli": "^13.1.0",
"@commitlint/config-conventional": "^13.1.0", "@commitlint/config-conventional": "^13.1.0",
"@iconify/json": "^1.1.386", "@iconify/json": "^1.1.401",
"@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": "^8.1.1",
"@types/intro.js": "^3.0.2", "@types/intro.js": "^3.0.2",
"@types/jest": "^27.0.1", "@types/jest": "^27.0.1",
"@types/lodash-es": "^4.17.4", "@types/lodash-es": "^4.17.5",
"@types/mockjs": "^1.0.4", "@types/mockjs": "^1.0.4",
"@types/node": "^16.6.1", "@types/node": "^16.9.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/showdown": "^1.9.4",
"@types/sortablejs": "^1.10.7", "@types/sortablejs": "^1.10.7",
"@typescript-eslint/eslint-plugin": "^4.29.1", "@typescript-eslint/eslint-plugin": "^4.31.0",
"@typescript-eslint/parser": "^4.29.1", "@typescript-eslint/parser": "^4.31.0",
"@vitejs/plugin-legacy": "^1.5.1", "@vitejs/plugin-legacy": "^1.5.3",
"@vitejs/plugin-vue": "^1.4.0", "@vitejs/plugin-vue": "^1.6.2",
"@vitejs/plugin-vue-jsx": "^1.1.7", "@vitejs/plugin-vue-jsx": "^1.1.8",
"@vue/compiler-sfc": "3.2.2", "@vue/compiler-sfc": "3.2.11",
"@vue/test-utils": "^2.0.0-rc.12", "@vue/test-utils": "^2.0.0-rc.14",
"autoprefixer": "^10.3.1", "autoprefixer": "^10.3.4",
"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",
@ -91,20 +89,20 @@
"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.4.0", "eslint-plugin-jest": "^24.4.0",
"eslint-plugin-prettier": "^3.4.0", "eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^7.16.0", "eslint-plugin-vue": "^7.17.0",
"esno": "^0.8.0", "esno": "^0.9.1",
"fs-extra": "^10.0.0", "fs-extra": "^10.0.0",
"http-server": "^13.0.0", "http-server": "^13.0.1",
"husky": "^7.0.1", "husky": "^7.0.2",
"inquirer": "^8.1.2", "inquirer": "^8.1.2",
"is-ci": "^3.0.0", "is-ci": "^3.0.0",
"jest": "^27.0.6", "jest": "^27.2.0",
"less": "^4.1.1", "less": "^4.1.1",
"lint-staged": "^11.1.2", "lint-staged": "^11.1.2",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"postcss": "^8.3.6", "postcss": "^8.3.6",
"prettier": "^2.3.2", "prettier": "^2.4.0",
"pretty-quick": "^3.1.1", "pretty-quick": "^3.1.1",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rollup-plugin-visualizer": "5.5.2", "rollup-plugin-visualizer": "5.5.2",
@ -112,27 +110,28 @@
"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",
"ts-jest": "^27.0.4", "ts-jest": "^27.0.5",
"ts-node": "^10.2.0", "ts-node": "^10.2.1",
"typescript": "4.3.5", "typescript": "4.4.3",
"vite": "2.5.0", "vite": "2.5.7",
"vite-plugin-compression": "^0.3.3", "vite-plugin-compression": "^0.3.5",
"vite-plugin-html": "^2.0.7", "vite-plugin-html": "^2.1.0",
"vite-plugin-imagemin": "^0.4.3", "vite-plugin-imagemin": "^0.4.5",
"vite-plugin-mock": "^2.9.4", "vite-plugin-mock": "^2.9.6",
"vite-plugin-purge-icons": "^0.7.0", "vite-plugin-purge-icons": "^0.7.0",
"vite-plugin-pwa": "^0.10.0", "vite-plugin-pwa": "^0.11.2",
"vite-plugin-style-import": "^1.1.1", "vite-plugin-style-import": "^1.2.1",
"vite-plugin-svg-icons": "^1.0.4", "vite-plugin-svg-icons": "^1.0.4",
"vite-plugin-theme": "^0.8.1", "vite-plugin-theme": "^0.8.1",
"vite-plugin-windicss": "^1.2.7", "vite-plugin-vue-setup-extend": "^0.1.0",
"vue-eslint-parser": "^7.10.0", "vite-plugin-windicss": "^1.4.2",
"vue-tsc": "^0.2.3" "vue-eslint-parser": "^7.11.0",
"vue-tsc": "^0.3.0"
}, },
"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.56.2" "rollup": "^2.56.3"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -7,8 +7,7 @@ module.exports = {
singleQuote: true, singleQuote: true,
quoteProps: 'as-needed', quoteProps: 'as-needed',
bracketSpacing: true, bracketSpacing: true,
trailingComma: 'es5', trailingComma: 'all',
jsxBracketSameLine: false,
jsxSingleQuote: false, jsxSingleQuote: false,
arrowParens: 'always', arrowParens: 'always',
insertPragma: false, insertPragma: false,
@ -16,5 +15,4 @@ module.exports = {
proseWrap: 'never', proseWrap: 'never',
htmlWhitespaceSensitivity: 'strict', htmlWhitespaceSensitivity: 'strict',
endOfLine: 'auto', endOfLine: 'auto',
rangeStart: 0,
}; };

View File

@ -15,5 +15,6 @@
// support Multi-language // support Multi-language
const { getAntdLocale } = useLocale(); const { getAntdLocale } = useLocale();
// Listening to page changes and dynamically changing site titles
useTitle(); useTitle();
</script> </script>

View File

@ -10,13 +10,13 @@ const { uploadUrl = '' } = useGlobSetting();
*/ */
export function uploadApi( export function uploadApi(
params: UploadFileParams, params: UploadFileParams,
onUploadProgress: (progressEvent: ProgressEvent) => void onUploadProgress: (progressEvent: ProgressEvent) => void,
) { ) {
return defHttp.uploadFile<UploadApiResult>( return defHttp.uploadFile<UploadApiResult>(
{ {
url: uploadUrl, url: uploadUrl,
onUploadProgress, onUploadProgress,
}, },
params params,
); );
} }

View File

@ -21,7 +21,7 @@ export function loginApi(params: LoginParams, mode: ErrorMessageMode = 'modal')
}, },
{ {
errorMessageMode: mode, errorMessageMode: mode,
} },
); );
} }
@ -29,7 +29,7 @@ export function loginApi(params: LoginParams, mode: ErrorMessageMode = 'modal')
* @description: getUserInfo * @description: getUserInfo
*/ */
export function getUserInfo() { export function getUserInfo() {
return defHttp.get<GetUserInfoModel>({ url: Api.GetUserInfo }); return defHttp.get<GetUserInfoModel>({ url: Api.GetUserInfo }, { errorMessageMode: 'none' });
} }
export function getPermCode() { export function getPermCode() {

View File

@ -1,6 +1,6 @@
<template> <template>
<div v-if="getShowDarkModeToggle" :class="getClass" @click="toggleDarkMode"> <div v-if="getShowDarkModeToggle" :class="getClass" @click="toggleDarkMode">
<div :class="`${prefixCls}-inner`"> </div> <div :class="`${prefixCls}-inner`"></div>
<SvgIcon size="14" name="sun" /> <SvgIcon size="14" name="sun" />
<SvgIcon size="14" name="moon" /> <SvgIcon size="14" name="moon" />
</div> </div>

View File

@ -62,6 +62,7 @@
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';
// @ts-ignore
import vClickOutside 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';
@ -104,7 +105,7 @@
nextTick(() => { nextTick(() => {
unref(inputRef)?.focus(); unref(inputRef)?.focus();
}); });
} },
); );
function handleClose() { function handleClose() {

View File

@ -47,7 +47,7 @@
const { prefixCls } = useDesign('basic-help'); const { prefixCls } = useDesign('basic-help');
const getTooltipStyle = computed( const getTooltipStyle = computed(
(): CSSProperties => ({ color: props.color, fontSize: props.fontSize }) (): CSSProperties => ({ color: props.color, fontSize: props.fontSize }),
); );
const getOverlayStyle = computed((): CSSProperties => ({ maxWidth: props.maxWidth })); const getOverlayStyle = computed((): CSSProperties => ({ maxWidth: props.maxWidth }));

View File

@ -9,10 +9,11 @@
</template> </template>
<script lang="ts"> <script lang="ts">
export default { import { defineComponent } from 'vue';
export default defineComponent({
name: 'AButton', name: 'AButton',
inheritAttrs: false, inheritAttrs: false,
}; });
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, unref } from 'vue'; import { computed, unref } from 'vue';

View File

@ -33,7 +33,7 @@
okText: t('common.okText'), okText: t('common.okText'),
cancelText: t('common.cancelText'), cancelText: t('common.cancelText'),
}, },
{ ...props, ...unref(attrs) } { ...props, ...unref(attrs) },
); );
}); });

View File

@ -0,0 +1,4 @@
import { withInstall } from '/@/utils';
import cardList from './src/CardList.vue';
export const CardList = withInstall(cardList);

View File

@ -0,0 +1,178 @@
<template>
<div class="p-2">
<div class="bg-white mb-2 p-4">
<BasicForm @register="registerForm" />
</div>
{{ sliderProp.width }}
<div class="bg-white p-2">
<List
:grid="{ gutter: 5, xs: 1, sm: 2, md: 4, lg: 4, xl: 6, xxl: grid }"
:data-source="data"
:pagination="paginationProp"
>
<template #header>
<div class="flex justify-end space-x-2"
><slot name="header"></slot>
<Tooltip>
<template #title>
<div class="w-50">每行显示数量</div
><Slider
id="slider"
v-bind="sliderProp"
v-model:value="grid"
@change="sliderChange"
/></template>
<Button><TableOutlined /></Button>
</Tooltip>
<Tooltip @click="fetch">
<template #title>刷新</template>
<Button><RedoOutlined /></Button>
</Tooltip>
</div>
</template>
<template #renderItem="{ item }">
<ListItem>
<Card>
<template #title></template>
<template #cover>
<div :class="height">
<Image :src="item.imgs[0]" />
</div>
</template>
<template class="ant-card-actions" #actions>
<!-- <SettingOutlined key="setting" />-->
<EditOutlined key="edit" />
<Dropdown
:trigger="['hover']"
:dropMenuList="[
{
text: '删除',
event: '1',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, item.id),
},
},
]"
popconfirm
>
<EllipsisOutlined key="ellipsis" />
</Dropdown>
</template>
<CardMeta>
<template #title>
<TypographyText :content="item.name" :ellipsis="{ tooltip: item.address }" />
</template>
<template #avatar>
<Avatar :src="item.avatar" />
</template>
<template #description>{{ item.time }}</template>
</CardMeta>
</Card>
</ListItem>
</template>
</List>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, onMounted, ref } from 'vue';
import {
EditOutlined,
EllipsisOutlined,
RedoOutlined,
TableOutlined,
} from '@ant-design/icons-vue';
import { List, Card, Image, Typography, Tooltip, Slider, Avatar } from 'ant-design-vue';
import { Dropdown } from '/@/components/Dropdown';
import { BasicForm, useForm } from '/@/components/Form';
import { propTypes } from '/@/utils/propTypes';
import { Button } from '/@/components/Button';
import { isFunction } from '/@/utils/is';
import { useSlider, grid } from './data';
const ListItem = List.Item;
const CardMeta = Card.Meta;
const TypographyText = Typography.Text;
// slider
const sliderProp = computed(() => useSlider(4));
//
const props = defineProps({
// API
params: propTypes.object.def({}),
//api
api: propTypes.func,
});
//
const emit = defineEmits(['getMethod', 'delete']);
//
const data = ref([]);
//
// cover
//pageSize
const height = computed(() => {
return `h-${120 - grid.value * 6}`;
});
//
const [registerForm, { validate }] = useForm({
schemas: [{ field: 'type', component: 'Input', label: '类型' }],
labelWidth: 80,
baseColProps: { span: 6 },
actionColOptions: { span: 24 },
autoSubmitOnEnter: true,
submitFunc: handleSubmit,
});
//
async function handleSubmit() {
const data = await validate();
await fetch(data);
}
function sliderChange(n) {
pageSize.value = n * 4;
fetch();
}
//
onMounted(() => {
fetch();
emit('getMethod', fetch);
});
async function fetch(p = {}) {
const { api, params } = props;
if (api && isFunction(api)) {
const res = await api({ ...params, page: page.value, pageSize: pageSize.value, ...p });
data.value = res.items;
total.value = res.total;
}
}
//
const page = ref(1);
const pageSize = ref(36);
const total = ref(0);
const paginationProp = ref({
showSizeChanger: false,
showQuickJumper: true,
pageSize,
current: page,
total,
showTotal: (total) => `${total}`,
onChange: pageChange,
onShowSizeChange: pageSizeChange,
});
function pageChange(p, pz) {
page.value = p;
pageSize.value = pz;
fetch();
}
function pageSizeChange(current, size) {
pageSize.value = size;
fetch();
}
async function handleDelete(id) {
emit('delete', id);
}
</script>

View File

@ -0,0 +1,25 @@
import { ref } from 'vue';
//每行个数
export const grid = ref(12);
// slider属性
export const useSlider = (min = 6, max = 12) => {
// 每行显示个数滑动条
const getMarks = () => {
const l = {};
for (let i = min; i < max + 1; i++) {
l[i] = {
style: {
color: '#fff',
},
label: i,
};
}
return l;
};
return {
min,
max,
marks: getMarks(),
step: 1,
};
};

View File

@ -10,7 +10,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
const MODE = { export const MODE = {
JSON: 'application/json', JSON: 'application/json',
html: 'htmlmixed', html: 'htmlmixed',
js: 'javascript', js: 'javascript',
@ -25,18 +25,26 @@
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 },
autoFormat: { type: Boolean, default: true },
}); });
const emit = defineEmits(['change', 'update:value']); const emit = defineEmits(['change', 'update:value', 'format-error']);
const getValue = computed(() => { const getValue = computed(() => {
const { value, mode } = props; const { value, mode, autoFormat } = props;
if (mode !== MODE.JSON) { if (!autoFormat || mode !== MODE.JSON) {
return value as string; return value as string;
} }
return isString(value) let result = value;
? JSON.stringify(JSON.parse(value), null, 2) if (isString(value)) {
: JSON.stringify(value, null, 2); try {
result = JSON.parse(value);
} catch (e) {
emit('format-error', value);
return value as string;
}
}
return JSON.stringify(result, null, 2);
}); });
function handleValueChange(v) { function handleValueChange(v) {

View File

@ -1,5 +1,5 @@
<template> <template>
<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" setup> <script lang="ts" setup>
@ -40,7 +40,7 @@
editor?.setValue(value ? value : ''); editor?.setValue(value ? value : '');
} }
}, },
{ flush: 'post' } { flush: 'post' },
); );
watchEffect(() => { watchEffect(() => {
@ -54,13 +54,13 @@
}, },
{ {
immediate: true, immediate: true,
} },
); );
function setTheme() { function setTheme() {
unref(editor)?.setOption( unref(editor)?.setOption(
'theme', 'theme',
appStore.getDarkMode === 'light' ? 'idea' : 'material-palenight' appStore.getDarkMode === 'light' ? 'idea' : 'material-palenight',
); );
} }

View File

@ -54,7 +54,7 @@
return; return;
} }
nextTick(() => { nextTick(() => {
const wrap = unref(scrollbar.wrap); const wrap = unref(scrollbar.wrap) as any;
if (!wrap) { if (!wrap) {
return; return;
} }

View File

@ -46,7 +46,7 @@
name: 'ContextMenu', name: 'ContextMenu',
props, props,
setup(props) { setup(props) {
const wrapRef = ref<ElRef>(null); const wrapRef = ref(null);
const showRef = ref(false); const showRef = ref(false);
const getStyle = computed((): CSSProperties => { const getStyle = computed((): CSSProperties => {

View File

@ -77,7 +77,7 @@
const getStyle = computed((): CSSProperties => ({ width: unref(getWidth) })); const getStyle = computed((): CSSProperties => ({ width: unref(getWidth) }));
const getImageWrapperStyle = computed( const getImageWrapperStyle = computed(
(): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) }) (): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) }),
); );
watchEffect(() => { watchEffect(() => {
@ -88,7 +88,7 @@
() => sourceValue.value, () => sourceValue.value,
(v: string) => { (v: string) => {
emit('update:value', v); emit('update:value', v);
} },
); );
function handleUploadSuccess({ source }) { function handleUploadSuccess({ source }) {
@ -103,7 +103,7 @@
t, t,
prefixCls, prefixCls,
register, register,
openModal, openModal: openModal as any,
getIconWidth, getIconWidth,
sourceValue, sourceValue,
getClass, getClass,

View File

@ -14,7 +14,7 @@ export interface DescItem {
// render // render
render?: ( render?: (
val: any, val: any,
data: Recordable data: Recordable,
) => VNode | undefined | JSX.Element | Element | string | number; ) => VNode | undefined | JSX.Element | Element | string | number;
} }

View File

@ -139,7 +139,7 @@
(newVal, oldVal) => { (newVal, oldVal) => {
if (newVal !== oldVal) visibleRef.value = newVal; if (newVal !== oldVal) visibleRef.value = newVal;
}, },
{ deep: true } { deep: true },
); );
watch( watch(
@ -149,7 +149,7 @@
emit('visible-change', visible); emit('visible-change', visible);
instance && drawerInstance.emitVisible?.(visible, instance.uid); instance && drawerInstance.emitVisible?.(visible, instance.uid);
}); });
} },
); );
// Cancel event // Cancel event
@ -181,9 +181,9 @@
onClose, onClose,
t, t,
prefixCls, prefixCls,
getMergeProps, getMergeProps: getMergeProps as any,
getScrollContentStyle, getScrollContentStyle,
getProps, getProps: getProps as any,
getLoading, getLoading,
getBindValues, getBindValues,
getFooterHeight, getFooterHeight,

View File

@ -79,7 +79,7 @@
() => props.data, () => props.data,
() => { () => {
onRender(); onRender();
} },
); );
// TODO // TODO
@ -94,7 +94,7 @@
() => unref(getFlowOptions), () => unref(getFlowOptions),
(options) => { (options) => {
unref(lfInstance)?.updateEditConfig(options); unref(lfInstance)?.updateEditConfig(options);
} },
); );
// init logicFlow // init logicFlow

View File

@ -24,12 +24,12 @@
</FormItem> </FormItem>
</template> </template>
<FormAction v-bind="{ ...getProps, ...advanceState }" @toggle-advanced="handleToggleAdvanced"> <FormAction v-bind="getFormActionBindProps" @toggle-advanced="handleToggleAdvanced">
<template <template
#[item]="data" #[item]="data"
v-for="item in ['resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']" v-for="item in ['resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']"
> >
<slot :name="item" v-bind="data"></slot> <slot :name="item" v-bind="data || {}"></slot>
</template> </template>
</FormAction> </FormAction>
<slot name="formFooter"></slot> <slot name="formFooter"></slot>
@ -62,8 +62,6 @@
import { basicProps } from './props'; import { basicProps } from './props';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import type { RowProps } from 'ant-design-vue/lib/grid/Row';
export default defineComponent({ export default defineComponent({
name: 'BasicForm', name: 'BasicForm',
components: { FormItem, Form, Row, FormAction }, components: { FormItem, Form, Row, FormAction },
@ -103,7 +101,7 @@
}); });
// Get uniform row style and Row configuration for the entire form // Get uniform row style and Row configuration for the entire form
const getRow = computed((): RowProps => { const getRow = computed((): Recordable => {
const { baseRowStyle = {}, rowProps } = unref(getProps); const { baseRowStyle = {}, rowProps } = unref(getProps);
return { return {
style: baseRowStyle, style: baseRowStyle,
@ -112,7 +110,7 @@
}); });
const getBindValue = computed( const getBindValue = computed(
() => ({ ...attrs, ...props, ...unref(getProps) } as Recordable) () => ({ ...attrs, ...props, ...unref(getProps) } as Recordable),
); );
const getSchema = computed((): FormSchema[] => { const getSchema = computed((): FormSchema[] => {
@ -132,7 +130,11 @@
} }
} }
} }
return schemas as FormSchema[]; if (unref(getProps).showAdvancedButton) {
return schemas.filter((schema) => schema.component !== 'Divider') as FormSchema[];
} else {
return schemas as FormSchema[];
}
}); });
const { handleToggleAdvanced } = useAdvanced({ const { handleToggleAdvanced } = useAdvanced({
@ -196,14 +198,14 @@
}, },
{ {
immediate: true, immediate: true,
} },
); );
watch( watch(
() => unref(getProps).schemas, () => unref(getProps).schemas,
(schemas) => { (schemas) => {
resetSchema(schemas ?? []); resetSchema(schemas ?? []);
} },
); );
watch( watch(
@ -220,7 +222,7 @@
initDefault(); initDefault();
isInitedDefaultRef.value = true; isInitedDefaultRef.value = true;
} }
} },
); );
async function setProps(formProps: Partial<FormProps>): Promise<void> { async function setProps(formProps: Partial<FormProps>): Promise<void> {
@ -278,10 +280,12 @@
getProps, getProps,
formElRef, formElRef,
getSchema, getSchema,
formActionType, formActionType: formActionType as any,
setFormModel, setFormModel,
prefixCls,
getFormClass, getFormClass,
getFormActionBindProps: computed(
(): Recordable => ({ ...getProps.value, ...advanceState }),
),
...formActionType, ...formActionType,
}; };
}, },

View File

@ -18,6 +18,7 @@ import {
TreeSelect, TreeSelect,
Slider, Slider,
Rate, Rate,
Divider,
} from 'ant-design-vue'; } from 'ant-design-vue';
import RadioButtonGroup from './components/RadioButtonGroup.vue'; import RadioButtonGroup from './components/RadioButtonGroup.vue';
@ -61,6 +62,7 @@ componentMap.set('IconPicker', IconPicker);
componentMap.set('InputCountDown', CountdownInput); componentMap.set('InputCountDown', CountdownInput);
componentMap.set('Upload', BasicUpload); componentMap.set('Upload', BasicUpload);
componentMap.set('Divider', Divider);
export function add(compName: ComponentType, component: Component) { export function add(compName: ComponentType, component: Component) {
componentMap.set(compName, component); componentMap.set(compName, component);

View File

@ -1,7 +1,7 @@
<template> <template>
<Select <Select
@dropdownVisibleChange="handleFetch" @dropdownVisibleChange="handleFetch"
v-bind="attrs" v-bind="$attrs"
@change="handleChange" @change="handleChange"
:options="getOptions" :options="getOptions"
v-model:value="state" v-model:value="state"
@ -41,12 +41,7 @@
}, },
inheritAttrs: false, inheritAttrs: false,
props: { props: {
value: propTypes.oneOfType([ value: [Array, Object, String, Number],
propTypes.object,
propTypes.number,
propTypes.string,
propTypes.array,
]),
numberToString: propTypes.bool, numberToString: propTypes.bool,
api: { api: {
type: Function as PropType<(arg?: Recordable) => Promise<OptionsItem[]>>, type: Function as PropType<(arg?: Recordable) => Promise<OptionsItem[]>>,
@ -100,7 +95,7 @@
() => { () => {
!unref(isFirstLoad) && fetch(); !unref(isFirstLoad) && fetch();
}, },
{ deep: true } { deep: true },
); );
async function fetch() { async function fetch() {

View File

@ -46,14 +46,14 @@
() => { () => {
isFirstLoaded.value && fetch(); isFirstLoaded.value && fetch();
}, },
{ deep: true } { deep: true },
); );
watch( watch(
() => props.immediate, () => props.immediate,
(v) => { (v) => {
v && !isFirstLoaded.value && fetch(); v && !isFirstLoaded.value && fetch();
} },
); );
onMounted(() => { onMounted(() => {

View File

@ -105,7 +105,7 @@
{ {
text: t('common.resetText'), text: t('common.resetText'),
}, },
props.resetButtonOptions props.resetButtonOptions,
); );
}); });
@ -114,7 +114,7 @@
{ {
text: t('common.queryText'), text: t('common.queryText'),
}, },
props.submitButtonOptions props.submitButtonOptions,
); );
}); });

View File

@ -5,7 +5,7 @@
import type { ValidationRule } from 'ant-design-vue/lib/form/Form'; import type { ValidationRule } from 'ant-design-vue/lib/form/Form';
import type { TableActionType } from '/@/components/Table'; import type { TableActionType } from '/@/components/Table';
import { defineComponent, computed, unref, toRefs } from 'vue'; import { defineComponent, computed, unref, toRefs } from 'vue';
import { Form, Col } from 'ant-design-vue'; import { Form, Col, Divider } from 'ant-design-vue';
import { componentMap } from '../componentMap'; import { componentMap } from '../componentMap';
import { BasicHelp } from '/@/components/Basic'; import { BasicHelp } from '/@/components/Basic';
import { isBoolean, isFunction, isNull } from '/@/utils/is'; import { isBoolean, isFunction, isNull } from '/@/utils/is';
@ -73,11 +73,17 @@
const getComponentsProps = computed(() => { const getComponentsProps = computed(() => {
const { schema, tableAction, formModel, formActionType } = props; const { schema, tableAction, formModel, formActionType } = props;
const { componentProps = {} } = schema; let { componentProps = {} } = schema;
if (!isFunction(componentProps)) { if (isFunction(componentProps)) {
return componentProps; componentProps = componentProps({ schema, tableAction, formModel, formActionType }) ?? {};
} }
return componentProps({ schema, tableAction, formModel, formActionType }) ?? {}; if (schema.component === 'Divider') {
componentProps = Object.assign({ type: 'horizontal' }, componentProps, {
orientation: 'left',
plain: true,
});
}
return componentProps as Recordable;
}); });
const getDisable = computed(() => { const getDisable = computed(() => {
@ -177,7 +183,7 @@
} }
const requiredRuleIndex: number = rules.findIndex( const requiredRuleIndex: number = rules.findIndex(
(rule) => Reflect.has(rule, 'required') && !Reflect.has(rule, 'validator') (rule) => Reflect.has(rule, 'required') && !Reflect.has(rule, 'validator'),
); );
if (requiredRuleIndex !== -1) { if (requiredRuleIndex !== -1) {
@ -300,38 +306,46 @@
} }
function renderItem() { function renderItem() {
const { itemProps, slot, render, field, suffix } = props.schema; const { itemProps, slot, render, field, suffix, component } = props.schema;
const { labelCol, wrapperCol } = unref(itemLabelWidthProp); const { labelCol, wrapperCol } = unref(itemLabelWidthProp);
const { colon } = props.formProps; const { colon } = props.formProps;
const getContent = () => { if (component === 'Divider') {
return slot return (
? getSlot(slots, slot, unref(getValues)) <Col span={24}>
: render <Divider {...unref(getComponentsProps)}>{renderLabelHelpMessage()}</Divider>
? render(unref(getValues)) </Col>
: renderComponent(); );
}; } else {
const getContent = () => {
return slot
? getSlot(slots, slot, unref(getValues))
: render
? render(unref(getValues))
: renderComponent();
};
const showSuffix = !!suffix; const showSuffix = !!suffix;
const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix; const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix;
return ( return (
<Form.Item <Form.Item
name={field} name={field}
colon={colon} colon={colon}
class={{ 'suffix-item': showSuffix }} class={{ 'suffix-item': showSuffix }}
{...(itemProps as Recordable)} {...(itemProps as Recordable)}
label={renderLabelHelpMessage()} label={renderLabelHelpMessage()}
rules={handleRules()} rules={handleRules()}
labelCol={labelCol} labelCol={labelCol}
wrapperCol={wrapperCol} wrapperCol={wrapperCol}
> >
<div style="display:flex"> <div style="display:flex">
<div style="flex:1">{getContent()}</div> <div style="flex:1">{getContent()}</div>
{showSuffix && <span class="suffix">{getSuffix}</span>} {showSuffix && <span class="suffix">{getSuffix}</span>}
</div> </div>
</Form.Item> </Form.Item>
); );
}
} }
return () => { return () => {

View File

@ -38,7 +38,7 @@ function genType() {
export function setComponentRuleType( export function setComponentRuleType(
rule: ValidationRule, rule: ValidationRule,
component: ComponentType, component: ComponentType,
valueFormat: string valueFormat: string,
) { ) {
if (['DatePicker', 'MonthPicker', 'WeekPicker', 'TimePicker'].includes(component)) { if (['DatePicker', 'MonthPicker', 'WeekPicker', 'TimePicker'].includes(component)) {
rule.type = valueFormat ? 'string' : 'object'; rule.type = valueFormat ? 'string' : 'object';

View File

@ -58,7 +58,7 @@ export default function ({
debounceUpdateAdvanced(); debounceUpdateAdvanced();
} }
}, },
{ immediate: true } { immediate: true },
); );
function getAdvanced(itemCol: Partial<ColEx>, itemColSum = 0, isLastAction = false) { function getAdvanced(itemCol: Partial<ColEx>, itemColSum = 0, isLastAction = false) {
@ -139,7 +139,7 @@ export default function ({
if (isShow && (colProps || baseColProps)) { if (isShow && (colProps || baseColProps)) {
const { itemColSum: sum, isAdvanced } = getAdvanced( const { itemColSum: sum, isAdvanced } = getAdvanced(
{ ...baseColProps, ...colProps }, { ...baseColProps, ...colProps },
itemColSum itemColSum,
); );
itemColSum = sum || 0; itemColSum = sum || 0;

View File

@ -18,7 +18,7 @@ export function useForm(props?: Props): UseFormReturnType {
const form = unref(formRef); const form = unref(formRef);
if (!form) { if (!form) {
error( error(
'The form instance has not been obtained, please make sure that the form has been rendered when performing the form operation!' 'The form instance has not been obtained, please make sure that the form has been rendered when performing the form operation!',
); );
} }
await nextTick(); await nextTick();
@ -44,7 +44,7 @@ export function useForm(props?: Props): UseFormReturnType {
{ {
immediate: true, immediate: true,
deep: true, deep: true,
} },
); );
} }
@ -96,7 +96,7 @@ export function useForm(props?: Props): UseFormReturnType {
appendSchemaByField: async ( appendSchemaByField: async (
schema: FormSchema, schema: FormSchema,
prefixField: string | undefined, prefixField: string | undefined,
first: boolean first: boolean,
) => { ) => {
const form = await getForm(); const form = await getForm();
form.appendSchemaByField(schema, prefixField, first); form.appendSchemaByField(schema, prefixField, first);

View File

@ -149,11 +149,13 @@ export function useFormEvents({
updateData = [...data]; updateData = [...data];
} }
const hasField = updateData.every((item) => Reflect.has(item, 'field') && item.field); const hasField = updateData.every(
(item) => item.component === 'Divider' || (Reflect.has(item, 'field') && item.field),
);
if (!hasField) { if (!hasField) {
error( error(
'All children of the form Schema array that need to be updated must contain the `field` field' 'All children of the form Schema array that need to be updated must contain the `field` field',
); );
return; return;
} }
@ -169,11 +171,13 @@ export function useFormEvents({
updateData = [...data]; updateData = [...data];
} }
const hasField = updateData.every((item) => Reflect.has(item, 'field') && item.field); const hasField = updateData.every(
(item) => item.component === 'Divider' || (Reflect.has(item, 'field') && item.field),
);
if (!hasField) { if (!hasField) {
error( error(
'All children of the form Schema array that need to be updated must contain the `field` field' 'All children of the form Schema array that need to be updated must contain the `field` field',
); );
return; return;
} }

View File

@ -37,7 +37,7 @@ export interface FormActionType {
appendSchemaByField: ( appendSchemaByField: (
schema: FormSchema, schema: FormSchema,
prefixField: string | undefined, prefixField: string | undefined,
first?: boolean | undefined first?: boolean | undefined,
) => Promise<void>; ) => Promise<void>;
validateFields: (nameList?: NamePath[]) => Promise<any>; validateFields: (nameList?: NamePath[]) => Promise<any>;
validate: (nameList?: NamePath[]) => Promise<any>; validate: (nameList?: NamePath[]) => Promise<any>;

View File

@ -109,4 +109,5 @@ export type ComponentType =
| 'IconPicker' | 'IconPicker'
| 'Render' | 'Render'
| 'Slider' | 'Slider'
| 'Rate'; | 'Rate'
| 'Divider';

View File

@ -63,7 +63,7 @@
</div> </div>
</div> </div>
<template v-else <template v-else
><div class="p-5"> <Empty /></div> ><div class="p-5"><Empty /></div>
</template> </template>
</template> </template>
@ -139,7 +139,7 @@
const { getPaginationList, getTotal, setCurrentPage } = usePagination( const { getPaginationList, getTotal, setCurrentPage } = usePagination(
currentList, currentList,
props.pageSize props.pageSize,
); );
watchEffect(() => { watchEffect(() => {
@ -151,7 +151,7 @@
(v) => { (v) => {
emit('update:value', v); emit('update:value', v);
return emit('change', v); return emit('change', v);
} },
); );
function handlePageChange(page: number) { function handlePageChange(page: number) {

View File

@ -4,7 +4,7 @@ import type { LoadingProps } from './typing';
import type { Ref } from 'vue'; import type { Ref } from 'vue';
export interface UseLoadingOptions { export interface UseLoadingOptions {
target?: HTMLElement | Ref<ElRef>; target?: any;
props?: Partial<LoadingProps>; props?: Partial<LoadingProps>;
} }
@ -16,7 +16,7 @@ export function useLoading(props: Partial<LoadingProps>): [Fn, Fn, (string) => v
export function useLoading(opt: Partial<UseLoadingOptions>): [Fn, Fn, (string) => void]; export function useLoading(opt: Partial<UseLoadingOptions>): [Fn, Fn, (string) => void];
export function useLoading( export function useLoading(
opt: Partial<LoadingProps> | Partial<UseLoadingOptions> opt: Partial<LoadingProps> | Partial<UseLoadingOptions>,
): [Fn, Fn, (string) => void] { ): [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;

View File

@ -1,5 +1,7 @@
import { withInstall } from '/@/utils'; import { withInstall } from '/@/utils';
import markDown from './src/Markdown.vue'; import markDown from './src/Markdown.vue';
import markDownViewer from './src/MarkdownViewer.vue';
export const MarkDown = withInstall(markDown); export const MarkDown = withInstall(markDown);
export const MarkdownViewer = withInstall(markDownViewer);
export * from './src/typing'; export * from './src/typing';

View File

@ -2,6 +2,7 @@
<div ref="wrapRef"></div> <div ref="wrapRef"></div>
</template> </template>
<script lang="ts"> <script lang="ts">
import type { Ref } from 'vue';
import { import {
defineComponent, defineComponent,
ref, ref,
@ -30,14 +31,14 @@
emits: ['change', 'get', 'update:value'], emits: ['change', 'get', 'update:value'],
setup(props, { attrs, emit }) { setup(props, { attrs, emit }) {
const wrapRef = ref<ElRef>(null); const wrapRef = ref<ElRef>(null);
const vditorRef = ref<Nullable<Vditor>>(null); const vditorRef = ref(null) as Ref<Nullable<Vditor>>;
const initedRef = ref(false); const initedRef = ref(false);
const modalFn = useModalContext(); const modalFn = useModalContext();
const { getLocale } = useLocale(); const { getLocale } = useLocale();
const { getDarkMode } = useRootSetting(); const { getDarkMode } = useRootSetting();
const valueRef = ref(''); const valueRef = ref(props.value || '');
watch( watch(
[() => getDarkMode.value, () => initedRef.value], [() => getDarkMode.value, () => initedRef.value],
@ -51,7 +52,7 @@
{ {
immediate: true, immediate: true,
flush: 'post', flush: 'post',
} },
); );
watch( watch(
@ -61,7 +62,7 @@
instance.getVditor()?.setValue(v); instance.getVditor()?.setValue(v);
} }
valueRef.value = v; valueRef.value = v;
} },
); );
const getCurrentLang = computed((): 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' => { const getCurrentLang = computed((): 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' => {
@ -89,6 +90,9 @@
theme: getDarkMode.value === 'dark' ? 'dark' : 'classic', theme: getDarkMode.value === 'dark' ? 'dark' : 'classic',
lang: unref(getCurrentLang), lang: unref(getCurrentLang),
mode: 'sv', mode: 'sv',
fullscreen: {
index: 520,
},
preview: { preview: {
actions: [], actions: [],
}, },

View File

@ -0,0 +1,22 @@
<template>
<div v-html="getHtmlData" :class="$props.class" class="markdown-viewer"></div>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import showdown from 'showdown';
const converter = new showdown.Converter();
converter.setOption('tables', true);
const props = defineProps({
value: { type: String },
class: { type: String },
});
const getHtmlData = computed(() => converter.makeHtml(props.value || ''));
</script>
<style scoped>
.markdown-viewer {
width: 100%;
}
</style>

View File

@ -56,15 +56,15 @@
const { prefixCls } = useDesign('basic-menu'); const { prefixCls } = useDesign('basic-menu');
const { items, mode, accordion } = toRefs(props); const { items, mode, accordion } = toRefs(props);
const { getCollapsed, getIsHorizontal, getTopMenuAlign, getSplit } = useMenuSetting(); const { getCollapsed, getTopMenuAlign, getSplit } = useMenuSetting();
const { currentRoute } = useRouter(); const { currentRoute } = useRouter();
const { handleOpenChange, setOpenKeys, getOpenKeys } = useOpenKeys( const { handleOpenChange, setOpenKeys, getOpenKeys } = useOpenKeys(
menuState, menuState,
items, items,
mode, mode as any,
accordion accordion,
); );
const getIsTopMenu = computed(() => { const getIsTopMenu = computed(() => {
@ -114,7 +114,7 @@
() => props.items, () => props.items,
() => { () => {
handleMenuChange(); handleMenuChange();
} },
); );
async function handleMenuClick({ key }: { key: string; keyPath: string[] }) { async function handleMenuClick({ key }: { key: string; keyPath: string[] }) {
@ -126,9 +126,6 @@
emit('menuClick', key); emit('menuClick', key);
isClickGo.value = true; isClickGo.value = true;
// const parentPath = await getCurrentParentPath(key);
// menuState.openKeys = [parentPath];
menuState.selectedKeys = [key]; menuState.selectedKeys = [key];
} }
@ -150,8 +147,6 @@
} }
return { return {
prefixCls,
getIsHorizontal,
handleMenuClick, handleMenuClick,
getInlineCollapseOptions, getInlineCollapseOptions,
getMenuClass, getMenuClass,

View File

@ -1,13 +1,11 @@
<template> <template>
<MenuItem :key="item.path"> <MenuItem :key="item.path">
<!-- <MenuItem :class="getLevelClass"> -->
<MenuItemContent v-bind="$props" :item="item" /> <MenuItemContent v-bind="$props" :item="item" />
</MenuItem> </MenuItem>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { Menu } from 'ant-design-vue'; import { Menu } from 'ant-design-vue';
import { useDesign } from '/@/hooks/web/useDesign';
import { itemProps } from '../props'; import { itemProps } from '../props';
import MenuItemContent from './MenuItemContent.vue'; import MenuItemContent from './MenuItemContent.vue';
@ -15,20 +13,8 @@
name: 'BasicMenuItem', name: 'BasicMenuItem',
components: { MenuItem: Menu.Item, MenuItemContent }, components: { MenuItem: Menu.Item, MenuItemContent },
props: itemProps, props: itemProps,
setup() // props setup() {
{ return {};
const { prefixCls } = useDesign('basic-menu-item');
// const getLevelClass = computed(() => {
// const { level, theme } = props;
// const levelCls = [`${prefixCls}__level${level}`, theme];
// return levelCls;
// });
return {
prefixCls,
// getLevelClass,
};
}, },
}); });
</script> </script>

View File

@ -4,6 +4,8 @@ import type { PropType } from 'vue';
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
import { ThemeEnum } from '/@/enums/appEnum'; import { ThemeEnum } from '/@/enums/appEnum';
import { propTypes } from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';
import type { MenuTheme } from 'ant-design-vue';
import type { MenuMode } from 'ant-design-vue/lib/menu/src/interface';
export const basicProps = { export const basicProps = {
items: { items: {
type: Array as PropType<Menu[]>, type: Array as PropType<Menu[]>,
@ -14,7 +16,7 @@ export const basicProps = {
inlineIndent: propTypes.number.def(20), inlineIndent: propTypes.number.def(20),
// 菜单组件的mode属性 // 菜单组件的mode属性
mode: { mode: {
type: String as PropType<MenuModeEnum>, type: String as PropType<MenuMode>,
default: MenuModeEnum.INLINE, default: MenuModeEnum.INLINE,
}, },
@ -22,7 +24,10 @@ export const basicProps = {
type: String as PropType<MenuTypeEnum>, type: String as PropType<MenuTypeEnum>,
default: MenuTypeEnum.MIX, default: MenuTypeEnum.MIX,
}, },
theme: propTypes.string.def(ThemeEnum.DARK), theme: {
type: String as PropType<MenuTheme>,
default: ThemeEnum.DARK,
},
inlineCollapsed: propTypes.bool, inlineCollapsed: propTypes.bool,
mixSider: propTypes.bool, mixSider: propTypes.bool,

View File

@ -14,7 +14,7 @@ export function useOpenKeys(
menuState: MenuState, menuState: MenuState,
menus: Ref<MenuType[]>, menus: Ref<MenuType[]>,
mode: Ref<MenuModeEnum>, mode: Ref<MenuModeEnum>,
accordion: Ref<boolean> accordion: Ref<boolean>,
) { ) {
const { getCollapsed, getIsMixSidebar } = useMenuSetting(); const { getCollapsed, getIsMixSidebar } = useMenuSetting();
@ -37,7 +37,7 @@ export function useOpenKeys(
} }
}, },
16, 16,
!native !native,
); );
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<Modal v-bind="getBindValue" @cancel="handleCancel"> <Modal v-bind="getBindValue">
<template #closeIcon v-if="!$slots.closeIcon"> <template #closeIcon v-if="!$slots.closeIcon">
<ModalClose <ModalClose
:canFullscreen="getProps.canFullscreen" :canFullscreen="getProps.canFullscreen"
@ -44,7 +44,7 @@
</ModalWrapper> </ModalWrapper>
<template #[item]="data" v-for="item in Object.keys(omit($slots, 'default'))"> <template #[item]="data" v-for="item in Object.keys(omit($slots, 'default'))">
<slot :name="item" v-bind="data"></slot> <slot :name="item" v-bind="data || {}"></slot>
</template> </template>
</Modal> </Modal>
</template> </template>
@ -104,7 +104,7 @@
} }
// Custom title component: get title // Custom title component: get title
const getMergeProps = computed((): ModalProps => { const getMergeProps = computed((): Recordable => {
return { return {
...props, ...props,
...(unref(propsRef) as any), ...(unref(propsRef) as any),
@ -118,7 +118,7 @@
}); });
// modal component does not need title and origin buttons // modal component does not need title and origin buttons
const getProps = computed((): ModalProps => { const getProps = computed((): Recordable => {
const opt = { const opt = {
...unref(getMergeProps), ...unref(getMergeProps),
visible: unref(visibleRef), visible: unref(visibleRef),
@ -169,7 +169,7 @@
}, },
{ {
immediate: false, immediate: false,
} },
); );
// //
@ -212,7 +212,7 @@
extHeightRef.value = height; extHeightRef.value = height;
} }
function handleTitleDbClick(e: ChangeEvent) { function handleTitleDbClick(e) {
if (!props.canFullscreen) return; if (!props.canFullscreen) return;
e.stopPropagation(); e.stopPropagation();
handleFullScreen(e); handleFullScreen(e);

View File

@ -62,7 +62,7 @@
{ {
attributes: true, attributes: true,
subtree: true, subtree: true,
} },
); );
createModalContext({ createModalContext({
@ -89,7 +89,7 @@
} else { } else {
minRealHeightRef.value = realHeightRef.value; minRealHeightRef.value = realHeightRef.value;
} }
} },
); );
onMounted(() => { onMounted(() => {
@ -125,7 +125,7 @@
const modalDom = bodyDom.parentElement && bodyDom.parentElement.parentElement; const modalDom = bodyDom.parentElement && bodyDom.parentElement.parentElement;
if (!modalDom) return; if (!modalDom) return;
const modalRect = getComputedStyle(modalDom).top; const modalRect = getComputedStyle(modalDom as Element).top;
const modalTop = Number.parseInt(modalRect); const modalTop = Number.parseInt(modalRect);
let maxHeight = let maxHeight =
window.innerHeight - window.innerHeight -

View File

@ -48,6 +48,7 @@ export function useModal(): UseModalReturnType {
if (unref(loaded) && isProdMode() && modalMethod === unref(modal)) return; if (unref(loaded) && isProdMode() && modalMethod === unref(modal)) return;
modal.value = modalMethod; modal.value = modalMethod;
loaded.value = true;
modalMethod.emitVisible = (visible: boolean, uid: number) => { modalMethod.emitVisible = (visible: boolean, uid: number) => {
visibleData[uid] = visible; visibleData[uid] = visible;
}; };

View File

@ -19,7 +19,7 @@
width: 520px; width: 520px;
padding-bottom: 0; padding-bottom: 0;
.scrollbar { .ant-modal-body > .scrollbar {
padding: 14px; padding: 14px;
} }

View File

@ -61,6 +61,7 @@
contentFullHeight: propTypes.bool, contentFullHeight: propTypes.bool,
contentClass: propTypes.string, contentClass: propTypes.string,
fixedHeight: propTypes.bool, fixedHeight: propTypes.bool,
upwardSpace: propTypes.oneOfType([propTypes.number, propTypes.string]).def(0),
}, },
setup(props, { slots, attrs }) { setup(props, { slots, attrs }) {
const wrapperRef = ref(null); const wrapperRef = ref(null);
@ -71,18 +72,20 @@
provide( provide(
PageWrapperFixedHeightKey, PageWrapperFixedHeightKey,
computed(() => props.fixedHeight) computed(() => props.fixedHeight),
); );
const getIsContentFullHeight = computed(() => { const getIsContentFullHeight = computed(() => {
return props.contentFullHeight; return props.contentFullHeight;
}); });
const getUpwardSpace = computed(() => props.upwardSpace);
const { redoHeight, setCompensation, contentHeight } = useContentHeight( const { redoHeight, setCompensation, contentHeight } = useContentHeight(
getIsContentFullHeight, getIsContentFullHeight,
wrapperRef, wrapperRef,
[headerRef, footerRef], [headerRef, footerRef],
[contentRef] [contentRef],
getUpwardSpace,
); );
setCompensation({ useLayoutFooter: true, elements: [footerRef] }); setCompensation({ useLayoutFooter: true, elements: [footerRef] });
@ -135,7 +138,7 @@
{ {
flush: 'post', flush: 'post',
immediate: true, immediate: true,
} },
); );
return { return {

View File

@ -56,7 +56,7 @@
setup(props) { setup(props) {
const { prefixCls } = useDesign('image-preview'); const { prefixCls } = useDesign('image-preview');
const getImageList = computed(() => { const getImageList = computed((): any[] => {
const { imageList } = props; const { imageList } = props;
if (!imageList) { if (!imageList) {
return []; return [];

View File

@ -103,7 +103,7 @@
}, },
{ {
deep: true, deep: true,
} },
); );
return { wrapRef, download }; return { wrapRef, download };

View File

@ -44,7 +44,7 @@ export default defineComponent({
const clickTrackHandler = (e: any) => { const clickTrackHandler = (e: any) => {
const offset = Math.abs( const offset = Math.abs(
e.target.getBoundingClientRect()[bar.value.direction] - e[bar.value.client] e.target.getBoundingClientRect()[bar.value.direction] - e[bar.value.client],
); );
const thumbHalf = thumb.value[bar.value.offset] / 2; const thumbHalf = thumb.value[bar.value.offset] / 2;
const thumbPositionPercentage = const thumbPositionPercentage =
@ -104,7 +104,7 @@ export default defineComponent({
move: props.move, move: props.move,
bar: bar.value, bar: bar.value,
}), }),
}) }),
); );
}, },
}); });

View File

@ -75,7 +75,7 @@
items, items,
accordion, accordion,
mixSider, mixSider,
collapse collapse,
); );
const getBindValues = computed(() => ({ ...attrs, ...props })); const getBindValues = computed(() => ({ ...attrs, ...props }));
@ -89,7 +89,7 @@
setOpenKeys(currentRoute.value.path); setOpenKeys(currentRoute.value.path);
} }
}, },
{ immediate: true } { immediate: true },
); );
watch( watch(
@ -100,7 +100,7 @@
} }
setOpenKeys(currentRoute.value.path); setOpenKeys(currentRoute.value.path);
}, },
{ flush: 'post' } { flush: 'post' },
); );
listenerRouteChange((route) => { listenerRouteChange((route) => {

View File

@ -87,7 +87,7 @@
nextTick(() => { nextTick(() => {
updateOpened(); updateOpened();
}); });
} },
); );
function updateOpened() { function updateOpened() {
@ -124,7 +124,7 @@
isRemoveAllPopup, isRemoveAllPopup,
sliceIndex, sliceIndex,
level: 0, level: 0,
props, props: props as any,
}); });
onMounted(() => { onMounted(() => {

View File

@ -98,7 +98,7 @@
active.value = false; active.value = false;
} }
}, },
{ immediate: true } { immediate: true },
); );
return { getClass, prefixCls, getItemStyle, getCollapse, handleClickItem, showTooptip }; return { getClass, prefixCls, getItemStyle, getCollapse, handleClickItem, showTooptip };

View File

@ -286,7 +286,7 @@
if (props.name && Array.isArray(data)) { if (props.name && Array.isArray(data)) {
state.opened = (data as (string | number)[]).includes(props.name); state.opened = (data as (string | number)[]).includes(props.name);
} }
} },
); );
rootMenuEmitter.on('on-update-active-name:submenu', (data: number[]) => { rootMenuEmitter.on('on-update-active-name:submenu', (data: number[]) => {

View File

@ -1,9 +1,9 @@
import type { InjectionKey, Ref } from 'vue'; import type { InjectionKey, Ref } from 'vue';
import type { Emitter } from '/@/utils/mitt';
import { createContext, useContext } from '/@/hooks/core/useContext'; import { createContext, useContext } from '/@/hooks/core/useContext';
import mitt from '/@/utils/mitt';
export interface SimpleRootMenuContextProps { export interface SimpleRootMenuContextProps {
rootMenuEmitter: typeof mitt; rootMenuEmitter: Emitter;
activeName: Ref<string | number>; activeName: Ref<string | number>;
} }

View File

@ -15,7 +15,7 @@ export function useOpenKeys(
menus: Ref<MenuType[]>, menus: Ref<MenuType[]>,
accordion: Ref<boolean>, accordion: Ref<boolean>,
mixSider: Ref<boolean>, mixSider: Ref<boolean>,
collapse: Ref<boolean> collapse: Ref<boolean>,
) { ) {
const debounceSetOpenKeys = useDebounceFn(setOpenKeys, 50); const debounceSetOpenKeys = useDebounceFn(setOpenKeys, 50);
async function setOpenKeys(path: string) { async function setOpenKeys(path: string) {
@ -38,7 +38,7 @@ export function useOpenKeys(
menuState.activeSubMenuNames = menuState.openNames; menuState.activeSubMenuNames = menuState.openNames;
}, },
30, 30,
native native,
); );
} }

View File

@ -59,7 +59,7 @@
() => unref(innerValueRef), () => unref(innerValueRef),
(val) => { (val) => {
emit('change', val); emit('change', val);
} },
); );
return { return {

View File

@ -10,7 +10,7 @@
@advanced-change="redoHeight" @advanced-change="redoHeight"
> >
<template #[replaceFormSlotKey(item)]="data" v-for="item in getFormSlotKeys"> <template #[replaceFormSlotKey(item)]="data" v-for="item in getFormSlotKeys">
<slot :name="item" v-bind="data"></slot> <slot :name="item" v-bind="data || {}"></slot>
</template> </template>
</BasicForm> </BasicForm>
@ -111,7 +111,7 @@
unref(isFixedHeightPage) && unref(isFixedHeightPage) &&
props.canResize && props.canResize &&
warn( warn(
"'canResize' of BasicTable may not work in PageWrapper with 'fixedHeight' (especially in hot updates)" "'canResize' of BasicTable may not work in PageWrapper with 'fixedHeight' (especially in hot updates)",
); );
}); });
@ -141,6 +141,8 @@
getRawDataSource, getRawDataSource,
setTableData, setTableData,
updateTableDataRecord, updateTableDataRecord,
deleteTableDataRecord,
insertTableDataRecord,
findTableDataRecord, findTableDataRecord,
fetch, fetch,
getRowKey, getRowKey,
@ -157,7 +159,7 @@
getFieldsValue: formActions.getFieldsValue, getFieldsValue: formActions.getFieldsValue,
clearSelectedRowKeys, clearSelectedRowKeys,
}, },
emit emit,
); );
function handleTableChange(...args) { function handleTableChange(...args) {
@ -182,7 +184,7 @@
tableElRef, tableElRef,
getColumnsRef, getColumnsRef,
getRowSelectionRef, getRowSelectionRef,
getDataSourceRef getDataSourceRef,
); );
const { customRow } = useCustomRow(getProps, { const { customRow } = useCustomRow(getProps, {
@ -211,7 +213,7 @@
getProps, getProps,
getScrollRef, getScrollRef,
tableElRef, tableElRef,
getDataSourceRef getDataSourceRef,
); );
const { getFormProps, replaceFormSlotKey, getFormSlotKeys, handleSearchInfoChange } = const { getFormProps, replaceFormSlotKey, getFormSlotKeys, handleSearchInfoChange } =
@ -279,6 +281,8 @@
setPagination, setPagination,
setTableData, setTableData,
updateTableDataRecord, updateTableDataRecord,
deleteTableDataRecord,
insertTableDataRecord,
findTableDataRecord, findTableDataRecord,
redoHeight, redoHeight,
setSelectedRowKeys, setSelectedRowKeys,
@ -320,7 +324,7 @@
wrapRef, wrapRef,
tableAction, tableAction,
redoHeight, redoHeight,
getFormProps, getFormProps: getFormProps as any,
replaceFormSlotKey, replaceFormSlotKey,
getFormSlotKeys, getFormSlotKeys,
getWrapperClass, getWrapperClass,
@ -334,6 +338,13 @@
@prefix-cls: ~'@{namespace}-basic-table'; @prefix-cls: ~'@{namespace}-basic-table';
[data-theme='dark'] {
.ant-table-tbody > tr:hover.ant-table-row-selected > td,
.ant-table-tbody > tr.ant-table-row-selected td {
background-color: #262626;
}
}
.@{prefix-cls} { .@{prefix-cls} {
max-width: 100%; max-width: 100%;
@ -413,7 +424,7 @@
.ant-table-body { .ant-table-body {
overflow-x: hidden !important; overflow-x: hidden !important;
overflow-y: scroll !important; // overflow-y: scroll !important;
} }
td { td {

View File

@ -14,11 +14,7 @@
<Divider <Divider
type="vertical" type="vertical"
class="action-divider" class="action-divider"
v-if=" v-if="divider && index < getActions.length - 1"
divider &&
index < getActions.length - (dropDownActions ? 0 : 1) &&
getDropdownList.length > 0
"
/> />
</template> </template>
<Dropdown <Dropdown
@ -95,7 +91,7 @@
.map((action) => { .map((action) => {
const { popConfirm } = action; const { popConfirm } = action;
return { return {
getPopupContainer: () => unref(table?.wrapRef.value) ?? document.body, getPopupContainer: () => unref((table as any)?.wrapRef.value) ?? document.body,
type: 'link', type: 'link',
size: 'small', size: 'small',
...action, ...action,
@ -107,7 +103,7 @@
}); });
}); });
const getDropdownList = computed(() => { const getDropdownList = computed((): any[] => {
return (toRaw(props.dropDownActions) || []) return (toRaw(props.dropDownActions) || [])
.filter((action) => { .filter((action) => {
return hasPermission(action.auth) && isIfShow(action); return hasPermission(action.auth) && isIfShow(action);
@ -133,7 +129,7 @@
function getTooltip(data: string | TooltipProps): TooltipProps { function getTooltip(data: string | TooltipProps): TooltipProps {
return { return {
getPopupContainer: () => unref(table?.wrapRef.value) ?? document.body, getPopupContainer: () => unref((table as any)?.wrapRef.value) ?? document.body,
placement: 'bottom', placement: 'bottom',
...(isString(data) ? { title: data } : data), ...(isString(data) ? { title: data } : data),
}; };

View File

@ -24,7 +24,7 @@
<template v-for="(img, index) in imgList" :key="img"> <template v-for="(img, index) in imgList" :key="img">
<Image <Image
:width="size" :width="size"
:style="{ 'margin-left': index === 0 ? 0 : margin }" :style="{ marginLeft: index === 0 ? 0 : margin }"
:src="srcPrefix + img" :src="srcPrefix + img"
/> />
</template> </template>

View File

@ -21,7 +21,7 @@ export const CellComponent: FunctionalComponent = (
popoverVisible, popoverVisible,
getPopupContainer, getPopupContainer,
}: ComponentProps, }: ComponentProps,
{ attrs } { attrs },
) => { ) => {
const Comp = componentMap.get(component) as typeof defineComponent; const Comp = componentMap.get(component) as typeof defineComponent;
@ -39,6 +39,6 @@ export const CellComponent: FunctionalComponent = (
{ {
default: () => DefaultComp, default: () => DefaultComp,
content: () => ruleMessage, content: () => ruleMessage,
} },
); );
}; };

View File

@ -5,31 +5,33 @@
:class="{ [`${prefixCls}__normal`]: true, 'ellipsis-cell': column.ellipsis }" :class="{ [`${prefixCls}__normal`]: true, 'ellipsis-cell': column.ellipsis }"
@click="handleEdit" @click="handleEdit"
> >
<div class="cell-content" :title="column.ellipsis ? getValues ?? '' : ''">{{ <div class="cell-content" :title="column.ellipsis ? getValues ?? '' : ''">
getValues ?? '&nbsp;' {{ getValues ? getValues : '&nbsp;' }}
}}</div> </div>
<FormOutlined :class="`${prefixCls}__normal-icon`" v-if="!column.editRow" /> <FormOutlined :class="`${prefixCls}__normal-icon`" v-if="!column.editRow" />
</div> </div>
<div v-if="isEdit" :class="`${prefixCls}__wrapper`" v-click-outside="onClickOutside"> <a-spin v-if="isEdit" :spinning="spinning">
<CellComponent <div :class="`${prefixCls}__wrapper`" v-click-outside="onClickOutside">
v-bind="getComponentProps" <CellComponent
:component="getComponent" v-bind="getComponentProps"
:style="getWrapperStyle" :component="getComponent"
:popoverVisible="getRuleVisible" :style="getWrapperStyle"
:rule="getRule" :popoverVisible="getRuleVisible"
:ruleMessage="ruleMessage" :rule="getRule"
:class="getWrapperClass" :ruleMessage="ruleMessage"
ref="elRef" :class="getWrapperClass"
@change="handleChange" ref="elRef"
@options-change="handleOptionsChange" @change="handleChange"
@pressEnter="handleEnter" @options-change="handleOptionsChange"
/> @pressEnter="handleEnter"
<div :class="`${prefixCls}__action`" v-if="!getRowEditable"> />
<CheckOutlined :class="[`${prefixCls}__icon`, 'mx-2']" @click="handleSubmitClick" /> <div :class="`${prefixCls}__action`" v-if="!getRowEditable">
<CloseOutlined :class="`${prefixCls}__icon `" @click="handleCancel" /> <CheckOutlined :class="[`${prefixCls}__icon`, 'mx-2']" @click="handleSubmitClick" />
<CloseOutlined :class="`${prefixCls}__icon `" @click="handleCancel" />
</div>
</div> </div>
</div> </a-spin>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -48,12 +50,13 @@
import { propTypes } from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';
import { isArray, isBoolean, isFunction, isNumber, isString } from '/@/utils/is'; import { isArray, isBoolean, isFunction, isNumber, isString } from '/@/utils/is';
import { createPlaceholderMessage } from './helper'; import { createPlaceholderMessage } from './helper';
import { omit, set } from 'lodash-es'; import { omit, pick, set } from 'lodash-es';
import { treeToList } from '/@/utils/helper/treeHelper'; import { treeToList } from '/@/utils/helper/treeHelper';
import { Spin } from 'ant-design-vue';
export default defineComponent({ export default defineComponent({
name: 'EditableCell', name: 'EditableCell',
components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent }, components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent, ASpin: Spin },
directives: { directives: {
clickOutside, clickOutside,
}, },
@ -80,6 +83,7 @@
const optionsRef = ref<LabelValueOptions>([]); const optionsRef = ref<LabelValueOptions>([]);
const currentValueRef = ref<any>(props.value); const currentValueRef = ref<any>(props.value);
const defaultValueRef = ref<any>(props.value); const defaultValueRef = ref<any>(props.value);
const spinning = ref<boolean>(false);
const { prefixCls } = useDesign('editable-cell'); const { prefixCls } = useDesign('editable-cell');
@ -246,6 +250,35 @@
const dataKey = (dataIndex || key) as string; const dataKey = (dataIndex || key) as string;
if (!record.editable) {
const { getBindValues } = table;
const { beforeEditSubmit, columns } = unref(getBindValues);
if (beforeEditSubmit && isFunction(beforeEditSubmit)) {
spinning.value = true;
const keys: string[] = columns
.map((_column) => _column.dataIndex)
.filter((field) => !!field) as string[];
let result: any = true;
try {
result = await beforeEditSubmit({
record: pick(record, keys),
index,
key,
value,
});
} catch (e) {
result = false;
} finally {
spinning.value = false;
}
if (result === false) {
return;
}
}
}
set(record, dataKey, value); set(record, dataKey, value);
//const record = await table.updateTableData(index, dataKey, value); //const record = await table.updateTableData(index, dataKey, value);
needEmit && table.emit?.('edit-end', { record, index, key, value }); needEmit && table.emit?.('edit-end', { record, index, key, value });
@ -331,13 +364,7 @@
/* eslint-disable */ /* eslint-disable */
props.record.onSubmitEdit = async () => { props.record.onSubmitEdit = async () => {
if (isArray(props.record?.submitCbs)) { if (isArray(props.record?.submitCbs)) {
const validFns = (props.record?.validCbs || []).map((fn) => fn()); if (!props.record?.onValid?.()) return;
const res = await Promise.all(validFns);
const pass = res.every((item) => !!item);
if (!pass) return;
const submitFns = props.record?.submitCbs || []; const submitFns = props.record?.submitCbs || [];
submitFns.forEach((fn) => fn(false, false)); submitFns.forEach((fn) => fn(false, false));
table.emit?.('edit-row-end'); table.emit?.('edit-row-end');
@ -368,6 +395,7 @@
getValues, getValues,
handleEnter, handleEnter,
handleSubmitClick, handleSubmitClick,
spinning,
}; };
}, },
}); });

View File

@ -3,6 +3,7 @@ import type { BasicColumn } from '/@/components/Table/src/types/table';
import { h, Ref } from 'vue'; import { h, Ref } from 'vue';
import EditableCell from './EditableCell.vue'; import EditableCell from './EditableCell.vue';
import { isArray } from '/@/utils/is';
interface Params { interface Params {
text: string; text: string;
@ -12,12 +13,23 @@ interface Params {
export function renderEditCell(column: BasicColumn) { export function renderEditCell(column: BasicColumn) {
return ({ text: value, record, index }: Params) => { return ({ text: value, record, index }: Params) => {
record.onValid = async () => {
if (isArray(record?.validCbs)) {
const validFns = (record?.validCbs || []).map((fn) => fn());
const res = await Promise.all(validFns);
return res.every((item) => !!item);
} else {
return false;
}
};
record.onEdit = async (edit: boolean, submit = false) => { record.onEdit = async (edit: boolean, submit = false) => {
if (!submit) { if (!submit) {
record.editable = edit; record.editable = edit;
} }
if (!edit && submit) { if (!edit && submit) {
if (!(await record.onValid())) return false;
const res = await record.onSubmitEdit?.(); const res = await record.onSubmitEdit?.();
if (res) { if (res) {
record.editable = false; record.editable = false;
@ -44,6 +56,7 @@ export function renderEditCell(column: BasicColumn) {
export type EditRecordRow<T = Recordable> = Partial< export type EditRecordRow<T = Recordable> = Partial<
{ {
onEdit: (editable: boolean, submit?: boolean) => Promise<boolean>; onEdit: (editable: boolean, submit?: boolean) => Promise<boolean>;
onValid: () => Promise<boolean>;
editable: boolean; editable: boolean;
onCancel: Fn; onCancel: Fn;
onSubmit: Fn; onSubmit: Fn;

View File

@ -42,7 +42,7 @@
<ScrollContainer> <ScrollContainer>
<CheckboxGroup v-model:value="checkedList" @change="onChange" ref="columnListRef"> <CheckboxGroup v-model:value="checkedList" @change="onChange" ref="columnListRef">
<template v-for="item in plainOptions" :key="item.value"> <template v-for="item in plainOptions" :key="item.value">
<div :class="`${prefixCls}__check-item`"> <div :class="`${prefixCls}__check-item`" v-if="!('ifShow' in item && !item.ifShow)">
<DragOutlined class="table-coulmn-drag-icon" /> <DragOutlined class="table-coulmn-drag-icon" />
<Checkbox :value="item.value"> <Checkbox :value="item.value">
{{ item.label }} {{ item.label }}
@ -351,7 +351,7 @@
const visible = const visible =
columns.findIndex( columns.findIndex(
(c: BasicColumn | string) => (c: BasicColumn | string) =>
c === col.value || (typeof c !== 'string' && c.dataIndex === col.value) c === col.value || (typeof c !== 'string' && c.dataIndex === col.value),
) !== -1; ) !== -1;
return { dataIndex: col.value, fixed: col.fixed, visible }; return { dataIndex: col.value, fixed: col.fixed, visible };
}); });

View File

@ -40,7 +40,7 @@ function handleChildren(children: BasicColumn[] | undefined, ellipsis: boolean)
function handleIndexColumn( function handleIndexColumn(
propsRef: ComputedRef<BasicTableProps>, propsRef: ComputedRef<BasicTableProps>,
getPaginationRef: ComputedRef<boolean | PaginationProps>, getPaginationRef: ComputedRef<boolean | PaginationProps>,
columns: BasicColumn[] columns: BasicColumn[],
) { ) {
const { t } = useI18n(); const { t } = useI18n();
@ -102,7 +102,7 @@ function handleActionColumn(propsRef: ComputedRef<BasicTableProps>, columns: Bas
export function useColumns( export function useColumns(
propsRef: ComputedRef<BasicTableProps>, propsRef: ComputedRef<BasicTableProps>,
getPaginationRef: ComputedRef<boolean | PaginationProps> getPaginationRef: ComputedRef<boolean | PaginationProps>,
) { ) {
const columnsRef = ref(unref(propsRef).columns) as unknown as Ref<BasicColumn[]>; const columnsRef = ref(unref(propsRef).columns) as unknown as Ref<BasicColumn[]>;
let cacheColumns = unref(propsRef).columns; let cacheColumns = unref(propsRef).columns;
@ -122,7 +122,7 @@ export function useColumns(
handleItem( handleItem(
item, item,
Reflect.has(item, 'ellipsis') ? !!item.ellipsis : !!ellipsis && !customRender && !slots Reflect.has(item, 'ellipsis') ? !!item.ellipsis : !!ellipsis && !customRender && !slots,
); );
}); });
return columns; return columns;
@ -179,7 +179,7 @@ export function useColumns(
(columns) => { (columns) => {
columnsRef.value = columns; columnsRef.value = columns;
cacheColumns = columns?.filter((item) => !item.flag) ?? []; cacheColumns = columns?.filter((item) => !item.flag) ?? [];
} },
); );
function setCacheColumnsByField(dataIndex: string | undefined, value: Partial<BasicColumn>) { function setCacheColumnsByField(dataIndex: string | undefined, value: Partial<BasicColumn>) {
@ -288,7 +288,7 @@ function sortFixedColumn(columns: BasicColumn[]) {
defColumns.push(column); defColumns.push(column);
} }
return [...fixedLeftColumns, ...defColumns, ...fixedRightColumns].filter( return [...fixedLeftColumns, ...defColumns, ...fixedRightColumns].filter(
(item) => !item.defaultHidden (item) => !item.defaultHidden,
); );
} }

View File

@ -15,7 +15,7 @@ interface Options {
function getKey( function getKey(
record: Recordable, record: Recordable,
rowKey: string | ((record: Record<string, any>) => string) | undefined, rowKey: string | ((record: Record<string, any>) => string) | undefined,
autoCreateKey?: boolean autoCreateKey?: boolean,
) { ) {
if (!rowKey || autoCreateKey) { if (!rowKey || autoCreateKey) {
return record[ROW_KEY]; return record[ROW_KEY];
@ -31,7 +31,7 @@ function getKey(
export function useCustomRow( export function useCustomRow(
propsRef: ComputedRef<BasicTableProps>, propsRef: ComputedRef<BasicTableProps>,
{ setSelectedRowKeys, getSelectRowKeys, getAutoCreateKey, clearSelectedRowKeys, emit }: Options { setSelectedRowKeys, getSelectRowKeys, getAutoCreateKey, clearSelectedRowKeys, emit }: Options,
) { ) {
const customRow = (record: Recordable, index: number) => { const customRow = (record: Recordable, index: number) => {
return { return {

View File

@ -40,7 +40,7 @@ export function useDataSource(
clearSelectedRowKeys, clearSelectedRowKeys,
tableData, tableData,
}: ActionType, }: ActionType,
emit: EmitType emit: EmitType,
) { ) {
const searchState = reactive<SearchState>({ const searchState = reactive<SearchState>({
sortInfo: {}, sortInfo: {},
@ -61,13 +61,13 @@ export function useDataSource(
}, },
{ {
immediate: true, immediate: true,
} },
); );
function handleTableChange( function handleTableChange(
pagination: PaginationProps, pagination: PaginationProps,
filters: Partial<Recordable<string[]>>, filters: Partial<Recordable<string[]>>,
sorter: SorterResult sorter: SorterResult,
) { ) {
const { clearSelectOnPageChange, sortFn, filterFn } = unref(propsRef); const { clearSelectOnPageChange, sortFn, filterFn } = unref(propsRef);
if (clearSelectOnPageChange) { if (clearSelectOnPageChange) {
@ -148,7 +148,7 @@ export function useDataSource(
function updateTableDataRecord( function updateTableDataRecord(
rowKey: string | number, rowKey: string | number,
record: Recordable record: Recordable,
): Recordable | undefined { ): Recordable | undefined {
const row = findTableDataRecord(rowKey); const row = findTableDataRecord(rowKey);
@ -160,6 +160,31 @@ export function useDataSource(
} }
} }
function deleteTableDataRecord(record: Recordable | Recordable[]): Recordable | undefined {
if (!dataSourceRef.value || dataSourceRef.value.length == 0) return;
const records = !Array.isArray(record) ? [record] : record;
const recordIndex = records
.map((item) => dataSourceRef.value.findIndex((s) => s.key === item.key)) // 取序号
.filter((item) => item !== undefined)
.sort((a, b) => b - a); // 从大到小排序
for (const index of recordIndex) {
unref(dataSourceRef).splice(index, 1);
unref(propsRef).dataSource?.splice(index, 1);
}
setPagination({
total: unref(propsRef).dataSource?.length,
});
return unref(propsRef).dataSource;
}
function insertTableDataRecord(record: Recordable, index: number): Recordable | undefined {
if (!dataSourceRef.value || dataSourceRef.value.length == 0) return;
index = index ?? dataSourceRef.value?.length;
unref(dataSourceRef).splice(index, 0, record);
unref(propsRef).dataSource?.splice(index, 0, record);
return unref(propsRef).dataSource;
}
function findTableDataRecord(rowKey: string | number) { function findTableDataRecord(rowKey: string | number) {
if (!dataSourceRef.value || dataSourceRef.value.length == 0) return; if (!dataSourceRef.value || dataSourceRef.value.length == 0) return;
@ -206,7 +231,7 @@ export function useDataSource(
const { pageField, sizeField, listField, totalField } = Object.assign( const { pageField, sizeField, listField, totalField } = Object.assign(
{}, {},
FETCH_SETTING, FETCH_SETTING,
fetchSetting fetchSetting,
); );
let pageParams: Recordable = {}; let pageParams: Recordable = {};
@ -314,6 +339,8 @@ export function useDataSource(
reload, reload,
updateTableData, updateTableData,
updateTableDataRecord, updateTableDataRecord,
deleteTableDataRecord,
insertTableDataRecord,
findTableDataRecord, findTableDataRecord,
handleTableChange, handleTableChange,
}; };

View File

@ -8,7 +8,7 @@ export function useLoading(props: ComputedRef<BasicTableProps>) {
() => unref(props).loading, () => unref(props).loading,
(loading) => { (loading) => {
loadingRef.value = loading; loadingRef.value = loading;
} },
); );
const getLoading = computed(() => unref(loadingRef)); const getLoading = computed(() => unref(loadingRef));

View File

@ -8,7 +8,7 @@ import { findNodeAll } from '/@/utils/helper/treeHelper';
export function useRowSelection( export function useRowSelection(
propsRef: ComputedRef<BasicTableProps>, propsRef: ComputedRef<BasicTableProps>,
tableData: Ref<Recordable[]>, tableData: Ref<Recordable[]>,
emit: EmitType emit: EmitType,
) { ) {
const selectedRowKeysRef = ref<string[]>([]); const selectedRowKeysRef = ref<string[]>([]);
const selectedRowRef = ref<Recordable[]>([]); const selectedRowRef = ref<Recordable[]>([]);
@ -35,7 +35,7 @@ export function useRowSelection(
() => unref(propsRef).rowSelection?.selectedRowKeys, () => unref(propsRef).rowSelection?.selectedRowKeys,
(v: string[]) => { (v: string[]) => {
setSelectedRowKeys(v); setSelectedRowKeys(v);
} },
); );
watch( watch(
@ -53,7 +53,7 @@ export function useRowSelection(
}); });
}); });
}, },
{ deep: true } { deep: true },
); );
const getAutoCreateKey = computed(() => { const getAutoCreateKey = computed(() => {
@ -72,7 +72,7 @@ export function useRowSelection(
(item) => rowKeys.includes(item[unref(getRowKey) as string]), (item) => rowKeys.includes(item[unref(getRowKey) as string]),
{ {
children: propsRef.value.childrenColumnName ?? 'children', children: propsRef.value.childrenColumnName ?? 'children',
} },
); );
const trueSelectedRows: any[] = []; const trueSelectedRows: any[] = [];
rowKeys.forEach((key: string) => { rowKeys.forEach((key: string) => {

View File

@ -18,7 +18,7 @@ export function useTable(tableProps?: Props): [
(instance: TableActionType, formInstance: UseTableMethod) => void, (instance: TableActionType, formInstance: UseTableMethod) => void,
TableActionType & { TableActionType & {
getForm: () => FormActionType; getForm: () => FormActionType;
} },
] { ] {
const tableRef = ref<Nullable<TableActionType>>(null); const tableRef = ref<Nullable<TableActionType>>(null);
const loadedRef = ref<Nullable<boolean>>(false); const loadedRef = ref<Nullable<boolean>>(false);
@ -50,7 +50,7 @@ export function useTable(tableProps?: Props): [
{ {
immediate: true, immediate: true,
deep: true, deep: true,
} },
); );
} }
@ -58,7 +58,7 @@ export function useTable(tableProps?: Props): [
const table = unref(tableRef); const table = unref(tableRef);
if (!table) { if (!table) {
error( error(
'The table instance has not been obtained yet, please make sure the table is presented when performing the table operation!' 'The table instance has not been obtained yet, please make sure the table is presented when performing the table operation!',
); );
} }
return table as TableActionType; return table as TableActionType;
@ -122,6 +122,12 @@ export function useTable(tableProps?: Props): [
updateTableData: (index: number, key: string, value: any) => { updateTableData: (index: number, key: string, value: any) => {
return getTableInstance().updateTableData(index, key, value); return getTableInstance().updateTableData(index, key, value);
}, },
deleteTableDataRecord: (record: Recordable | Recordable[]) => {
return getTableInstance().deleteTableDataRecord(record);
},
insertTableDataRecord: (record: Recordable | Recordable[], index?: number) => {
return getTableInstance().insertTableDataRecord(record, index);
},
updateTableDataRecord: (rowKey: string | number, record: Recordable) => { updateTableDataRecord: (rowKey: string | number, record: Recordable) => {
return getTableInstance().updateTableDataRecord(rowKey, record); return getTableInstance().updateTableDataRecord(rowKey, record);
}, },

View File

@ -6,7 +6,7 @@ import { ROW_KEY } from '../const';
export function useTableExpand( export function useTableExpand(
propsRef: ComputedRef<BasicTableProps>, propsRef: ComputedRef<BasicTableProps>,
tableData: Ref<Recordable[]>, tableData: Ref<Recordable[]>,
emit: EmitType emit: EmitType,
) { ) {
const expandedRowKeys = ref<string[]>([]); const expandedRowKeys = ref<string[]>([]);

View File

@ -12,7 +12,7 @@ export function useTableFooter(
scrollToFirstRowOnChange: boolean; scrollToFirstRowOnChange: boolean;
}>, }>,
tableElRef: Ref<ComponentRef>, tableElRef: Ref<ComponentRef>,
getDataSourceRef: ComputedRef<Recordable> getDataSourceRef: ComputedRef<Recordable>,
) { ) {
const getIsEmptyData = computed(() => { const getIsEmptyData = computed(() => {
return (unref(getDataSourceRef) || []).length === 0; return (unref(getDataSourceRef) || []).length === 0;
@ -43,7 +43,7 @@ export function useTableFooter(
name: 'scroll', name: 'scroll',
listener: () => { listener: () => {
const footerBodyDom = tableEl.$el.querySelector( const footerBodyDom = tableEl.$el.querySelector(
'.ant-table-footer .ant-table-body' '.ant-table-footer .ant-table-body',
) as HTMLDivElement; ) as HTMLDivElement;
if (!footerBodyDom || !bodyDom) return; if (!footerBodyDom || !bodyDom) return;
footerBodyDom.scrollLeft = bodyDom.scrollLeft; footerBodyDom.scrollLeft = bodyDom.scrollLeft;

View File

@ -8,7 +8,7 @@ export function useTableForm(
propsRef: ComputedRef<BasicTableProps>, propsRef: ComputedRef<BasicTableProps>,
slots: Slots, slots: Slots,
fetch: (opt?: FetchParams | undefined) => Promise<void>, fetch: (opt?: FetchParams | undefined) => Promise<void>,
getLoading: ComputedRef<boolean | undefined> getLoading: ComputedRef<boolean | undefined>,
) { ) {
const getFormProps = computed((): Partial<FormProps> => { const getFormProps = computed((): Partial<FormProps> => {
const { formConfig } = unref(propsRef); const { formConfig } = unref(propsRef);

View File

@ -8,7 +8,7 @@ import { getSlot } from '/@/utils/helper/tsxHelper';
export function useTableHeader( export function useTableHeader(
propsRef: ComputedRef<BasicTableProps>, propsRef: ComputedRef<BasicTableProps>,
slots: Slots, slots: Slots,
handlers: InnerHandlers handlers: InnerHandlers,
) { ) {
const getHeaderProps = computed((): Recordable => { const getHeaderProps = computed((): Recordable => {
const { title, showTableSetting, titleHelpMessage, tableSetting } = unref(propsRef); const { title, showTableSetting, titleHelpMessage, tableSetting } = unref(propsRef);
@ -46,7 +46,7 @@ export function useTableHeader(
headerTop: () => getSlot(slots, 'headerTop'), headerTop: () => getSlot(slots, 'headerTop'),
} }
: {}), : {}),
} },
), ),
}; };
}); });

View File

@ -13,7 +13,7 @@ export function useTableScroll(
tableElRef: Ref<ComponentRef>, tableElRef: Ref<ComponentRef>,
columnsRef: ComputedRef<BasicColumn[]>, columnsRef: ComputedRef<BasicColumn[]>,
rowSelectionRef: ComputedRef<TableRowSelection<any> | null>, rowSelectionRef: ComputedRef<TableRowSelection<any> | null>,
getDataSourceRef: ComputedRef<Recordable[]> getDataSourceRef: ComputedRef<Recordable[]>,
) { ) {
const tableHeightRef: Ref<Nullable<number>> = ref(null); const tableHeightRef: Ref<Nullable<number>> = ref(null);
@ -34,7 +34,7 @@ export function useTableScroll(
}, },
{ {
flush: 'post', flush: 'post',
} },
); );
function redoHeight() { function redoHeight() {

View File

@ -6,11 +6,14 @@ import { isFunction } from '/@/utils/is';
export function useTableStyle(propsRef: ComputedRef<BasicTableProps>, prefixCls: string) { export function useTableStyle(propsRef: ComputedRef<BasicTableProps>, prefixCls: string) {
function getRowClassName(record: TableCustomRecord, index: number) { function getRowClassName(record: TableCustomRecord, index: number) {
const { striped, rowClassName } = unref(propsRef); const { striped, rowClassName } = unref(propsRef);
if (!striped) return; const classNames: string[] = [];
if (rowClassName && isFunction(rowClassName)) { if (striped) {
return rowClassName(record); classNames.push((index || 0) % 2 === 1 ? `${prefixCls}-row__striped` : '');
} }
return (index || 0) % 2 === 1 ? `${prefixCls}-row__striped` : ''; if (rowClassName && isFunction(rowClassName)) {
classNames.push(rowClassName(record, index));
}
return classNames.filter((cls) => !!cls).join(' ');
} }
return { getRowClassName }; return { getRowClassName };

View File

@ -126,4 +126,14 @@ export const basicProps = {
type: Object as PropType<{ x: number | true; y: number }>, type: Object as PropType<{ x: number | true; y: number }>,
default: null, default: null,
}, },
beforeEditSubmit: {
type: Function as PropType<
(data: {
record: Recordable;
index: number;
key: string | number;
value: any;
}) => Promise<any>
>,
},
}; };

View File

@ -25,7 +25,7 @@ export interface TableRowSelection<T = any> extends ITableRowSelection {
/** /**
* Callback executed when select/deselect one row * Callback executed when select/deselect one row
* @type FunctionT * @type Function
*/ */
onSelect?: (record: T, selected: boolean, selectedRows: Object[], nativeEvent: Event) => any; onSelect?: (record: T, selected: boolean, selectedRows: Object[], nativeEvent: Event) => any;
@ -95,6 +95,8 @@ 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;
deleteTableDataRecord: (record: Recordable | Recordable[]) => Recordable | void;
insertTableDataRecord: (record: Recordable, index?: number) => Recordable | void;
findTableDataRecord: (rowKey: string | number) => 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;
@ -289,7 +291,7 @@ export interface BasicTableProps<T = any> {
* Row's className * Row's className
* @type Function * @type Function
*/ */
rowClassName?: (record: TableCustomRecord<T>) => string; rowClassName?: (record: TableCustomRecord<T>, index: number) => string;
/** /**
* Row selection config * Row selection config
@ -362,6 +364,18 @@ export interface BasicTableProps<T = any> {
*/ */
transformCellText?: Function; transformCellText?: Function;
/**
* Callback executed before editable cell submit value, not for row-editor
*
* The cell will not submit data while callback return false
*/
beforeEditSubmit?: (data: {
record: Recordable;
index: number;
key: string | number;
value: any;
}) => Promise<any>;
/** /**
* Callback executed when pagination, filters or sorter is changed * Callback executed when pagination, filters or sorter is changed
* @param pagination * @param pagination

View File

@ -34,7 +34,7 @@
() => { () => {
setTime(); setTime();
}, },
{ immediate: true } { immediate: true },
); );
function getTime() { function getTime() {

View File

@ -8,12 +8,18 @@
v-show="editorRef" v-show="editorRef"
:disabled="disabled" :disabled="disabled"
/> />
<textarea :id="tinymceId" ref="elRef" :style="{ visibility: 'hidden' }"></textarea> <textarea
:id="tinymceId"
ref="elRef"
:style="{ visibility: 'hidden' }"
v-if="!initOptions.inline"
></textarea>
<slot v-else></slot>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import type { RawEditorSettings } from 'tinymce'; import type { Editor, RawEditorSettings } from 'tinymce';
import tinymce from 'tinymce/tinymce'; import tinymce from 'tinymce/tinymce';
import 'tinymce/themes/silver'; import 'tinymce/themes/silver';
import 'tinymce/icons/default/icons'; import 'tinymce/icons/default/icons';
@ -54,8 +60,8 @@
ref, ref,
unref, unref,
watch, watch,
onUnmounted,
onDeactivated, onDeactivated,
onBeforeUnmount,
} from 'vue'; } from 'vue';
import ImgUpload from './ImgUpload.vue'; import ImgUpload from './ImgUpload.vue';
import { toolbar, plugins } from './tinymce'; import { toolbar, plugins } from './tinymce';
@ -108,9 +114,9 @@
components: { ImgUpload }, components: { ImgUpload },
inheritAttrs: false, inheritAttrs: false,
props: tinymceProps, props: tinymceProps,
emits: ['change', 'update:modelValue'], emits: ['change', 'update:modelValue', 'inited', 'init-error'],
setup(props, { emit, attrs }) { setup(props, { emit, attrs }) {
const editorRef = ref(); const editorRef = ref<Nullable<Editor>>(null);
const fullscreen = ref(false); const fullscreen = ref(false);
const tinymceId = ref<string>(buildShortUUID('tiny-vue')); const tinymceId = ref<string>(buildShortUUID('tiny-vue'));
const elRef = ref<Nullable<HTMLElement>>(null); const elRef = ref<Nullable<HTMLElement>>(null);
@ -159,7 +165,7 @@
content_css: content_css:
publicPath + 'resource/tinymce/skins/ui/' + skinName.value + '/content.min.css', publicPath + 'resource/tinymce/skins/ui/' + skinName.value + '/content.min.css',
...options, ...options,
setup: (editor) => { setup: (editor: Editor) => {
editorRef.value = editor; editorRef.value = editor;
editor.on('init', (e) => initSetup(e)); editor.on('init', (e) => initSetup(e));
}, },
@ -184,11 +190,13 @@
return; return;
} }
editor.setMode(attrs.disabled ? 'readonly' : 'design'); editor.setMode(attrs.disabled ? 'readonly' : 'design');
} },
); );
onMountedOrActivated(() => { onMountedOrActivated(() => {
tinymceId.value = buildShortUUID('tiny-vue'); if (!initOptions.value.inline) {
tinymceId.value = buildShortUUID('tiny-vue');
}
nextTick(() => { nextTick(() => {
setTimeout(() => { setTimeout(() => {
initEditor(); initEditor();
@ -196,7 +204,7 @@
}); });
}); });
onUnmounted(() => { onBeforeUnmount(() => {
destory(); destory();
}); });
@ -206,7 +214,7 @@
function destory() { function destory() {
if (tinymce !== null) { if (tinymce !== null) {
tinymce?.remove?.(unref(editorRef)); tinymce?.remove?.(unref(initOptions).selector!);
} }
} }
@ -215,7 +223,14 @@
if (el) { if (el) {
el.style.visibility = ''; el.style.visibility = '';
} }
tinymce.init(unref(initOptions)); tinymce
.init(unref(initOptions))
.then((editor) => {
emit('inited', editor);
})
.catch((err) => {
emit('init-error', err);
});
} }
function initSetup(e) { function initSetup(e) {
@ -249,7 +264,7 @@
() => props.modelValue, () => props.modelValue,
(val: string, prevVal: string) => { (val: string, prevVal: string) => {
setValue(editor, val, prevVal); setValue(editor, val, prevVal);
} },
); );
watch( watch(
@ -259,7 +274,7 @@
}, },
{ {
immediate: true, immediate: true,
} },
); );
editor.on(normalizedEvents ? normalizedEvents : 'change keyup undo redo', () => { editor.on(normalizedEvents ? normalizedEvents : 'change keyup undo redo', () => {

View File

@ -18,10 +18,10 @@ export const ScaleRotateTransition = createSimpleTransition('scale-rotate-transi
export const ExpandXTransition = createJavascriptTransition( export const ExpandXTransition = createJavascriptTransition(
'expand-x-transition', 'expand-x-transition',
ExpandTransitionGenerator('', true) ExpandTransitionGenerator('', true),
); );
export const ExpandTransition = createJavascriptTransition( export const ExpandTransition = createJavascriptTransition(
'expand-transition', 'expand-transition',
ExpandTransitionGenerator('') ExpandTransitionGenerator(''),
); );

View File

@ -41,7 +41,7 @@ export function createSimpleTransition(name: string, origin = 'top center 0', mo
export function createJavascriptTransition( export function createJavascriptTransition(
name: string, name: string,
functions: Recordable, functions: Recordable,
mode: Mode = 'in-out' mode: Mode = 'in-out',
) { ) {
return defineComponent({ return defineComponent({
name, name,

View File

@ -19,9 +19,9 @@
import { ScrollContainer } from '/@/components/Container'; import { ScrollContainer } from '/@/components/Container';
import { omit, get, difference } from 'lodash-es'; import { omit, get, difference } from 'lodash-es';
import { isArray, isBoolean, isFunction } from '/@/utils/is'; import { isArray, isBoolean, isEmpty, isFunction } from '/@/utils/is';
import { extendSlots, getSlot } from '/@/utils/helper/tsxHelper'; import { extendSlots, getSlot } from '/@/utils/helper/tsxHelper';
import { filter } from '/@/utils/helper/treeHelper'; import { filter, treeToList } from '/@/utils/helper/treeHelper';
import { useTree } from './useTree'; import { useTree } from './useTree';
import { useContextMenu } from '/@/hooks/web/useContextMenu'; import { useContextMenu } from '/@/hooks/web/useContextMenu';
@ -60,6 +60,7 @@
const searchState = reactive({ const searchState = reactive({
startSearch: false, startSearch: false,
searchText: '',
searchData: [] as TreeItem[], searchData: [] as TreeItem[],
}); });
@ -119,7 +120,7 @@
}); });
const getTreeData = computed((): TreeItem[] => const getTreeData = computed((): TreeItem[] =>
searchState.startSearch ? searchState.searchData : unref(treeDataRef) searchState.startSearch ? searchState.searchData : unref(treeDataRef),
); );
const getNotFound = computed((): boolean => { const getNotFound = computed((): boolean => {
@ -199,28 +200,70 @@
state.checkStrictly = strictly; state.checkStrictly = strictly;
} }
const searchText = ref(''); watch(
watchEffect(() => { () => props.searchValue,
if (props.searchValue !== searchText.value) searchText.value = props.searchValue; (val) => {
}); if (val !== searchState.searchText) {
searchState.searchText = val;
}
},
{
immediate: true,
},
);
watch(
() => props.treeData,
(val) => {
if (val) {
handleSearch(searchState.searchText);
}
},
);
function handleSearch(searchValue: string) { function handleSearch(searchValue: string) {
if (searchValue !== searchText.value) searchText.value = searchValue; if (searchValue !== searchState.searchText) searchState.searchText = searchValue;
emit('update:searchValue', searchValue); emit('update:searchValue', searchValue);
if (!searchValue) { if (!searchValue) {
searchState.startSearch = false; searchState.startSearch = false;
return; return;
} }
const { filterFn, checkable, expandOnSearch, checkOnSearch, selectedOnSearch } =
unref(props);
searchState.startSearch = true; searchState.startSearch = true;
const { title: titleField } = unref(getReplaceFields); const { title: titleField, key: keyField } = unref(getReplaceFields);
const matchedKeys: string[] = [];
searchState.searchData = filter( searchState.searchData = filter(
unref(treeDataRef), unref(treeDataRef),
(node) => { (node) => {
return node[titleField]?.includes(searchValue) ?? false; const result = filterFn
? filterFn(searchValue, node, unref(getReplaceFields))
: node[titleField]?.includes(searchValue) ?? false;
if (result) {
matchedKeys.push(node[keyField]);
}
return result;
}, },
unref(getReplaceFields) unref(getReplaceFields),
); );
if (expandOnSearch) {
const expandKeys = treeToList(searchState.searchData).map((val) => {
return val[keyField];
});
if (expandKeys && expandKeys.length) {
setExpandedKeys(expandKeys);
}
}
if (checkOnSearch && checkable && matchedKeys.length) {
setCheckedKeys(matchedKeys);
}
if (selectedOnSearch && matchedKeys.length) {
setSelectedKeys(matchedKeys);
}
} }
function handleClickNode(key: string, children: TreeItem[]) { function handleClickNode(key: string, children: TreeItem[]) {
@ -266,7 +309,7 @@
() => props.value, () => props.value,
() => { () => {
state.checkedKeys = toRaw(props.value || []); state.checkedKeys = toRaw(props.value || []);
} },
); );
watch( watch(
@ -275,7 +318,7 @@
const v = toRaw(state.checkedKeys); const v = toRaw(state.checkedKeys);
emit('update:value', v); emit('update:value', v);
emit('change', v); emit('change', v);
} },
); );
// watchEffect(() => { // watchEffect(() => {
@ -311,7 +354,7 @@
handleSearch(value); handleSearch(value);
}, },
getSearchValue: () => { getSearchValue: () => {
return searchText.value; return searchState.searchText;
}, },
}; };
@ -342,6 +385,8 @@
if (!data) { if (!data) {
return null; return null;
} }
const searchText = searchState.searchText;
const { highlight } = unref(props);
return data.map((item) => { return data.map((item) => {
const { const {
title: titleField, title: titleField,
@ -352,6 +397,23 @@
const propsData = omit(item, 'title'); const propsData = omit(item, 'title');
const icon = getIcon({ ...item, level }, item.icon); const icon = getIcon({ ...item, level }, item.icon);
const children = get(item, childrenField) || []; const children = get(item, childrenField) || [];
const title = get(item, titleField);
const searchIdx = title.indexOf(searchText);
const isHighlight =
searchState.startSearch && !isEmpty(searchText) && highlight && searchIdx !== -1;
const highlightStyle = `color: ${isBoolean(highlight) ? '#f50' : highlight}`;
const titleDom = isHighlight ? (
<span class={unref(getBindValues)?.blockNode ? `${prefixCls}__content` : ''}>
<span>{title.substr(0, searchIdx)}</span>
<span style={highlightStyle}>{searchText}</span>
<span>{title.substr(searchIdx + searchText.length)}</span>
</span>
) : (
title
);
return ( return (
<Tree.TreeNode {...propsData} node={toRaw(item)} key={get(item, keyField)}> <Tree.TreeNode {...propsData} node={toRaw(item)} key={get(item, keyField)}>
{{ {{
@ -365,11 +427,8 @@
) : ( ) : (
<> <>
{icon && <TreeIcon icon={icon} />} {icon && <TreeIcon icon={icon} />}
<span {titleDom}
class={unref(getBindValues)?.blockNode ? `${prefixCls}__content` : ''} {/*{get(item, titleField)}*/}
>
{get(item, titleField)}
</span>
<span class={`${prefixCls}__actions`}> <span class={`${prefixCls}__actions`}>
{renderAction({ ...item, level })} {renderAction({ ...item, level })}
</span> </span>
@ -400,7 +459,7 @@
helpMessage={helpMessage} helpMessage={helpMessage}
onStrictlyChange={onStrictlyChange} onStrictlyChange={onStrictlyChange}
onSearch={handleSearch} onSearch={handleSearch}
searchText={unref(searchText)} searchText={searchState.searchText}
> >
{extendSlots(slots)} {extendSlots(slots)}
</TreeHeader> </TreeHeader>

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