mirror of
https://github.com/vbenjs/vben-admin-thin-next.git
synced 2025-01-23 01:30:23 +08:00
chore: merge branch 'main' of github.com:anncwb/vue-vben-admin into main
This commit is contained in:
commit
9d3dc3c4d2
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -8,6 +8,6 @@
|
||||
"url": "http://localhost:3100",
|
||||
"webRoot": "${workspaceFolder}/src",
|
||||
"sourceMaps": true
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
- Upgrade vue 3.2, if the operation fails, delete node_modules and reinstall it
|
||||
|
54
CHANGELOG.md
54
CHANGELOG.md
@ -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)
|
||||
|
||||
### Bug Fixes
|
||||
|
@ -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)
|
||||
|
||||
- 升级 vue 3.2,如果运行失败,删除 node_modules 后重装即可
|
||||
|
@ -150,6 +150,7 @@ yarn build
|
||||
## 后台整合示例
|
||||
|
||||
- [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 的全开源平台
|
||||
|
||||
## 维护者
|
||||
|
||||
|
@ -52,19 +52,19 @@ async function generateIcon() {
|
||||
const { prefix } = data;
|
||||
const isLocal = useType === 'local';
|
||||
const icons = Object.keys(data.icons).map(
|
||||
(item) => `${isLocal ? prefix + ':' : ''}${item}`
|
||||
(item) => `${isLocal ? prefix + ':' : ''}${item}`,
|
||||
);
|
||||
|
||||
await fs.writeFileSync(
|
||||
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);
|
||||
}
|
||||
}
|
||||
fs.emptyDir(path.join(process.cwd(), 'node_modules/.vite'));
|
||||
console.log(
|
||||
`✨ ${chalk.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]`
|
||||
`✨ ${chalk.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]`,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ function createConfig(
|
||||
configName,
|
||||
config,
|
||||
configFileName = GLOB_CONFIG_FILE_NAME,
|
||||
}: { configName: string; config: any; configFileName?: string } = { configName: '', config: {} }
|
||||
}: { configName: string; config: any; configFileName?: string } = { configName: '', config: {} },
|
||||
) {
|
||||
try {
|
||||
const windowConf = `window.${configName}`;
|
||||
|
@ -50,7 +50,7 @@ export function wrapperEnv(envConf: Recordable): ViteEnv {
|
||||
*/
|
||||
function getConfFiles() {
|
||||
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;
|
||||
if (result) {
|
||||
const mode = result[1] as string;
|
||||
|
@ -3,12 +3,11 @@
|
||||
* https://github.com/anncwb/vite-plugin-compression
|
||||
*/
|
||||
import type { Plugin } from 'vite';
|
||||
|
||||
import compressPlugin from 'vite-plugin-compression';
|
||||
|
||||
export function configCompressPlugin(
|
||||
compress: 'gzip' | 'brotli' | 'none',
|
||||
deleteOriginFile = false
|
||||
deleteOriginFile = false,
|
||||
): Plugin | Plugin[] {
|
||||
const compressList = compress.split(',');
|
||||
|
||||
@ -19,16 +18,17 @@ export function configCompressPlugin(
|
||||
compressPlugin({
|
||||
ext: '.gz',
|
||||
deleteOriginFile,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (compressList.includes('brotli')) {
|
||||
plugins.push(
|
||||
compressPlugin({
|
||||
ext: '.br',
|
||||
algorithm: 'brotliCompress',
|
||||
deleteOriginFile,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
return plugins;
|
||||
|
@ -3,9 +3,7 @@
|
||||
* https://github.com/anncwb/vite-plugin-html
|
||||
*/
|
||||
import type { Plugin } from 'vite';
|
||||
|
||||
import html from 'vite-plugin-html';
|
||||
|
||||
import pkg from '../../../package.json';
|
||||
import { GLOB_CONFIG_FILE_NAME } from '../../constant';
|
||||
|
||||
@ -22,7 +20,7 @@ export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
|
||||
minify: isBuild,
|
||||
inject: {
|
||||
// Inject data into ejs template
|
||||
injectData: {
|
||||
data: {
|
||||
title: VITE_GLOB_APP_TITLE,
|
||||
},
|
||||
// Embed the generated app.config.js file
|
||||
|
@ -1,6 +1,5 @@
|
||||
// Image resource files used to compress the output of the production environment
|
||||
// https://github.com/anncwb/vite-plugin-imagemin
|
||||
|
||||
import viteImagemin from 'vite-plugin-imagemin';
|
||||
|
||||
export function configImageminPlugin() {
|
||||
|
@ -1,11 +1,10 @@
|
||||
import type { Plugin } from 'vite';
|
||||
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx';
|
||||
import legacy from '@vitejs/plugin-legacy';
|
||||
|
||||
import purgeIcons from 'vite-plugin-purge-icons';
|
||||
import windiCSS from 'vite-plugin-windicss';
|
||||
import vueSetupExtend from 'vite-plugin-vue-setup-extend';
|
||||
import { configHtmlPlugin } from './html';
|
||||
import { configPwaConfig } from './pwa';
|
||||
import { configMockPlugin } from './mock';
|
||||
@ -31,6 +30,8 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
|
||||
vue(),
|
||||
// have to
|
||||
vueJsx(),
|
||||
// support name
|
||||
vueSetupExtend(),
|
||||
];
|
||||
|
||||
// vite-plugin-windicss
|
||||
@ -70,7 +71,7 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
|
||||
|
||||
// rollup-plugin-gzip
|
||||
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
|
||||
|
@ -2,7 +2,6 @@
|
||||
* Zero-config PWA for Vite
|
||||
* https://github.com/antfu/vite-plugin-pwa
|
||||
*/
|
||||
|
||||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
|
||||
export function configPwaConfig(env: ViteEnv) {
|
||||
|
@ -2,11 +2,12 @@
|
||||
* Introduces component library styles on demand.
|
||||
* https://github.com/anncwb/vite-plugin-style-import
|
||||
*/
|
||||
|
||||
import styleImport from 'vite-plugin-style-import';
|
||||
|
||||
export function configStyleImportPlugin(isBuild: boolean) {
|
||||
if (!isBuild) return [];
|
||||
if (!isBuild) {
|
||||
return [];
|
||||
}
|
||||
const styleImportPlugin = styleImport({
|
||||
libs: [
|
||||
{
|
||||
|
@ -66,7 +66,7 @@ export function configThemePlugin(isBuild: boolean): Plugin[] {
|
||||
'border-color-base': '#303030',
|
||||
// 'border-color-split': '#30363d',
|
||||
'item-active-bg': '#111b26',
|
||||
'app-content-background': 'rgb(255 255 255 / 4%)',
|
||||
'app-content-background': '#1e1e1e',
|
||||
'tree-node-selected-bg': '#11263c',
|
||||
|
||||
'alert-success-border-color': '#274916',
|
||||
|
@ -13,7 +13,7 @@ export function resultPageSuccess<T = any>(
|
||||
page: number,
|
||||
pageSize: number,
|
||||
list: T[],
|
||||
{ message = 'ok' } = {}
|
||||
{ message = 'ok' } = {},
|
||||
) {
|
||||
const pageData = pagination(page, pageSize, list);
|
||||
|
||||
|
@ -52,7 +52,7 @@ export default [
|
||||
response: ({ body }) => {
|
||||
const { username, password } = body;
|
||||
const checkUser = createFakeUserList().find(
|
||||
(item) => item.username === username && password === item.password
|
||||
(item) => item.username === username && password === item.password,
|
||||
);
|
||||
if (!checkUser) {
|
||||
return resultError('Incorrect account or password!');
|
||||
|
89
package.json
89
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vben-admin",
|
||||
"version": "2.7.1",
|
||||
"version": "2.7.2",
|
||||
"author": {
|
||||
"name": "vben",
|
||||
"email": "anncwb@126.com",
|
||||
@ -34,55 +34,53 @@
|
||||
"gen:icon": "esno ./build/generate/icon/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify/iconify": "^2.0.3",
|
||||
"@logicflow/core": "^0.6.9",
|
||||
"@logicflow/extension": "^0.6.9",
|
||||
"@vueuse/core": "^5.3.0",
|
||||
"@iconify/iconify": "^2.0.4",
|
||||
"@vueuse/core": "^6.3.3",
|
||||
"@zxcvbn-ts/core": "^1.0.0-beta.0",
|
||||
"ant-design-vue": "2.2.6",
|
||||
"axios": "^0.21.1",
|
||||
"ant-design-vue": "2.2.7",
|
||||
"axios": "^0.21.4",
|
||||
"crypto-js": "^4.1.1",
|
||||
"echarts": "^5.1.2",
|
||||
"echarts": "^5.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mockjs": "^1.1.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"path-to-regexp": "^6.2.0",
|
||||
"pinia": "2.0.0-rc.4",
|
||||
"print-js": "^1.6.0",
|
||||
"pinia": "2.0.0-rc.9",
|
||||
"qrcode": "^1.4.4",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"sortablejs": "^1.14.0",
|
||||
"vue": "3.2.2",
|
||||
"vue": "3.2.11",
|
||||
"vue-i18n": "9.1.7",
|
||||
"vue-router": "^4.0.11",
|
||||
"vue-types": "^4.0.3"
|
||||
"vue-types": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^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",
|
||||
"@types/codemirror": "^5.60.2",
|
||||
"@types/crypto-js": "^4.0.2",
|
||||
"@types/fs-extra": "^9.0.12",
|
||||
"@types/inquirer": "^7.3.3",
|
||||
"@types/inquirer": "^8.1.1",
|
||||
"@types/intro.js": "^3.0.2",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/lodash-es": "^4.17.4",
|
||||
"@types/lodash-es": "^4.17.5",
|
||||
"@types/mockjs": "^1.0.4",
|
||||
"@types/node": "^16.6.1",
|
||||
"@types/node": "^16.9.1",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/qrcode": "^1.4.1",
|
||||
"@types/qs": "^6.9.7",
|
||||
"@types/showdown": "^1.9.4",
|
||||
"@types/sortablejs": "^1.10.7",
|
||||
"@typescript-eslint/eslint-plugin": "^4.29.1",
|
||||
"@typescript-eslint/parser": "^4.29.1",
|
||||
"@vitejs/plugin-legacy": "^1.5.1",
|
||||
"@vitejs/plugin-vue": "^1.4.0",
|
||||
"@vitejs/plugin-vue-jsx": "^1.1.7",
|
||||
"@vue/compiler-sfc": "3.2.2",
|
||||
"@vue/test-utils": "^2.0.0-rc.12",
|
||||
"autoprefixer": "^10.3.1",
|
||||
"@typescript-eslint/eslint-plugin": "^4.31.0",
|
||||
"@typescript-eslint/parser": "^4.31.0",
|
||||
"@vitejs/plugin-legacy": "^1.5.3",
|
||||
"@vitejs/plugin-vue": "^1.6.2",
|
||||
"@vitejs/plugin-vue-jsx": "^1.1.8",
|
||||
"@vue/compiler-sfc": "3.2.11",
|
||||
"@vue/test-utils": "^2.0.0-rc.14",
|
||||
"autoprefixer": "^10.3.4",
|
||||
"commitizen": "^4.2.4",
|
||||
"conventional-changelog-cli": "^2.1.1",
|
||||
"cross-env": "^7.0.3",
|
||||
@ -91,20 +89,20 @@
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-define-config": "^1.0.9",
|
||||
"eslint-plugin-jest": "^24.4.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-vue": "^7.16.0",
|
||||
"esno": "^0.8.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-vue": "^7.17.0",
|
||||
"esno": "^0.9.1",
|
||||
"fs-extra": "^10.0.0",
|
||||
"http-server": "^13.0.0",
|
||||
"husky": "^7.0.1",
|
||||
"http-server": "^13.0.1",
|
||||
"husky": "^7.0.2",
|
||||
"inquirer": "^8.1.2",
|
||||
"is-ci": "^3.0.0",
|
||||
"jest": "^27.0.6",
|
||||
"jest": "^27.2.0",
|
||||
"less": "^4.1.1",
|
||||
"lint-staged": "^11.1.2",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.3.6",
|
||||
"prettier": "^2.3.2",
|
||||
"prettier": "^2.4.0",
|
||||
"pretty-quick": "^3.1.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup-plugin-visualizer": "5.5.2",
|
||||
@ -112,27 +110,28 @@
|
||||
"stylelint-config-prettier": "^8.0.2",
|
||||
"stylelint-config-standard": "^22.0.0",
|
||||
"stylelint-order": "^4.1.0",
|
||||
"ts-jest": "^27.0.4",
|
||||
"ts-node": "^10.2.0",
|
||||
"typescript": "4.3.5",
|
||||
"vite": "2.5.0",
|
||||
"vite-plugin-compression": "^0.3.3",
|
||||
"vite-plugin-html": "^2.0.7",
|
||||
"vite-plugin-imagemin": "^0.4.3",
|
||||
"vite-plugin-mock": "^2.9.4",
|
||||
"ts-jest": "^27.0.5",
|
||||
"ts-node": "^10.2.1",
|
||||
"typescript": "4.4.3",
|
||||
"vite": "2.5.7",
|
||||
"vite-plugin-compression": "^0.3.5",
|
||||
"vite-plugin-html": "^2.1.0",
|
||||
"vite-plugin-imagemin": "^0.4.5",
|
||||
"vite-plugin-mock": "^2.9.6",
|
||||
"vite-plugin-purge-icons": "^0.7.0",
|
||||
"vite-plugin-pwa": "^0.10.0",
|
||||
"vite-plugin-style-import": "^1.1.1",
|
||||
"vite-plugin-pwa": "^0.11.2",
|
||||
"vite-plugin-style-import": "^1.2.1",
|
||||
"vite-plugin-svg-icons": "^1.0.4",
|
||||
"vite-plugin-theme": "^0.8.1",
|
||||
"vite-plugin-windicss": "^1.2.7",
|
||||
"vue-eslint-parser": "^7.10.0",
|
||||
"vue-tsc": "^0.2.3"
|
||||
"vite-plugin-vue-setup-extend": "^0.1.0",
|
||||
"vite-plugin-windicss": "^1.4.2",
|
||||
"vue-eslint-parser": "^7.11.0",
|
||||
"vue-tsc": "^0.3.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"//": "Used to install imagemin dependencies, because imagemin may not be installed in China. If it is abroad, you can delete it",
|
||||
"bin-wrapper": "npm:bin-wrapper-china",
|
||||
"rollup": "^2.56.2"
|
||||
"rollup": "^2.56.3"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -7,8 +7,7 @@ module.exports = {
|
||||
singleQuote: true,
|
||||
quoteProps: 'as-needed',
|
||||
bracketSpacing: true,
|
||||
trailingComma: 'es5',
|
||||
jsxBracketSameLine: false,
|
||||
trailingComma: 'all',
|
||||
jsxSingleQuote: false,
|
||||
arrowParens: 'always',
|
||||
insertPragma: false,
|
||||
@ -16,5 +15,4 @@ module.exports = {
|
||||
proseWrap: 'never',
|
||||
htmlWhitespaceSensitivity: 'strict',
|
||||
endOfLine: 'auto',
|
||||
rangeStart: 0,
|
||||
};
|
||||
|
@ -15,5 +15,6 @@
|
||||
// support Multi-language
|
||||
const { getAntdLocale } = useLocale();
|
||||
|
||||
// Listening to page changes and dynamically changing site titles
|
||||
useTitle();
|
||||
</script>
|
||||
|
@ -10,13 +10,13 @@ const { uploadUrl = '' } = useGlobSetting();
|
||||
*/
|
||||
export function uploadApi(
|
||||
params: UploadFileParams,
|
||||
onUploadProgress: (progressEvent: ProgressEvent) => void
|
||||
onUploadProgress: (progressEvent: ProgressEvent) => void,
|
||||
) {
|
||||
return defHttp.uploadFile<UploadApiResult>(
|
||||
{
|
||||
url: uploadUrl,
|
||||
onUploadProgress,
|
||||
},
|
||||
params
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ export function loginApi(params: LoginParams, mode: ErrorMessageMode = 'modal')
|
||||
},
|
||||
{
|
||||
errorMessageMode: mode,
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ export function loginApi(params: LoginParams, mode: ErrorMessageMode = 'modal')
|
||||
* @description: getUserInfo
|
||||
*/
|
||||
export function getUserInfo() {
|
||||
return defHttp.get<GetUserInfoModel>({ url: Api.GetUserInfo });
|
||||
return defHttp.get<GetUserInfoModel>({ url: Api.GetUserInfo }, { errorMessageMode: 'none' });
|
||||
}
|
||||
|
||||
export function getPermCode() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<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="moon" />
|
||||
</div>
|
||||
|
@ -62,6 +62,7 @@
|
||||
import { SearchOutlined } from '@ant-design/icons-vue';
|
||||
import AppSearchFooter from './AppSearchFooter.vue';
|
||||
import Icon from '/@/components/Icon';
|
||||
// @ts-ignore
|
||||
import vClickOutside from '/@/directives/clickOutside';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { useRefs } from '/@/hooks/core/useRefs';
|
||||
@ -104,7 +105,7 @@
|
||||
nextTick(() => {
|
||||
unref(inputRef)?.focus();
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
function handleClose() {
|
||||
|
@ -47,7 +47,7 @@
|
||||
const { prefixCls } = useDesign('basic-help');
|
||||
|
||||
const getTooltipStyle = computed(
|
||||
(): CSSProperties => ({ color: props.color, fontSize: props.fontSize })
|
||||
(): CSSProperties => ({ color: props.color, fontSize: props.fontSize }),
|
||||
);
|
||||
|
||||
const getOverlayStyle = computed((): CSSProperties => ({ maxWidth: props.maxWidth }));
|
||||
|
@ -9,10 +9,11 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
import { defineComponent } from 'vue';
|
||||
export default defineComponent({
|
||||
name: 'AButton',
|
||||
inheritAttrs: false,
|
||||
};
|
||||
});
|
||||
</script>
|
||||
<script lang="ts" setup>
|
||||
import { computed, unref } from 'vue';
|
||||
|
@ -33,7 +33,7 @@
|
||||
okText: t('common.okText'),
|
||||
cancelText: t('common.cancelText'),
|
||||
},
|
||||
{ ...props, ...unref(attrs) }
|
||||
{ ...props, ...unref(attrs) },
|
||||
);
|
||||
});
|
||||
|
||||
|
4
src/components/CardList/index.ts
Normal file
4
src/components/CardList/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { withInstall } from '/@/utils';
|
||||
import cardList from './src/CardList.vue';
|
||||
|
||||
export const CardList = withInstall(cardList);
|
178
src/components/CardList/src/CardList.vue
Normal file
178
src/components/CardList/src/CardList.vue
Normal 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>
|
25
src/components/CardList/src/data.ts
Normal file
25
src/components/CardList/src/data.ts
Normal 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,
|
||||
};
|
||||
};
|
@ -10,7 +10,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
const MODE = {
|
||||
export const MODE = {
|
||||
JSON: 'application/json',
|
||||
html: 'htmlmixed',
|
||||
js: 'javascript',
|
||||
@ -25,18 +25,26 @@
|
||||
value: { type: [Object, String] as PropType<Record<string, any> | string> },
|
||||
mode: { type: String, default: MODE.JSON },
|
||||
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 { value, mode } = props;
|
||||
if (mode !== MODE.JSON) {
|
||||
const { value, mode, autoFormat } = props;
|
||||
if (!autoFormat || mode !== MODE.JSON) {
|
||||
return value as string;
|
||||
}
|
||||
return isString(value)
|
||||
? JSON.stringify(JSON.parse(value), null, 2)
|
||||
: JSON.stringify(value, null, 2);
|
||||
let result = value;
|
||||
if (isString(value)) {
|
||||
try {
|
||||
result = JSON.parse(value);
|
||||
} catch (e) {
|
||||
emit('format-error', value);
|
||||
return value as string;
|
||||
}
|
||||
}
|
||||
return JSON.stringify(result, null, 2);
|
||||
});
|
||||
|
||||
function handleValueChange(v) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
<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>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@ -40,7 +40,7 @@
|
||||
editor?.setValue(value ? value : '');
|
||||
}
|
||||
},
|
||||
{ flush: 'post' }
|
||||
{ flush: 'post' },
|
||||
);
|
||||
|
||||
watchEffect(() => {
|
||||
@ -54,13 +54,13 @@
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
function setTheme() {
|
||||
unref(editor)?.setOption(
|
||||
'theme',
|
||||
appStore.getDarkMode === 'light' ? 'idea' : 'material-palenight'
|
||||
appStore.getDarkMode === 'light' ? 'idea' : 'material-palenight',
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@
|
||||
return;
|
||||
}
|
||||
nextTick(() => {
|
||||
const wrap = unref(scrollbar.wrap);
|
||||
const wrap = unref(scrollbar.wrap) as any;
|
||||
if (!wrap) {
|
||||
return;
|
||||
}
|
||||
|
@ -46,7 +46,7 @@
|
||||
name: 'ContextMenu',
|
||||
props,
|
||||
setup(props) {
|
||||
const wrapRef = ref<ElRef>(null);
|
||||
const wrapRef = ref(null);
|
||||
const showRef = ref(false);
|
||||
|
||||
const getStyle = computed((): CSSProperties => {
|
||||
|
@ -77,7 +77,7 @@
|
||||
const getStyle = computed((): CSSProperties => ({ width: unref(getWidth) }));
|
||||
|
||||
const getImageWrapperStyle = computed(
|
||||
(): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) })
|
||||
(): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) }),
|
||||
);
|
||||
|
||||
watchEffect(() => {
|
||||
@ -88,7 +88,7 @@
|
||||
() => sourceValue.value,
|
||||
(v: string) => {
|
||||
emit('update:value', v);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
function handleUploadSuccess({ source }) {
|
||||
@ -103,7 +103,7 @@
|
||||
t,
|
||||
prefixCls,
|
||||
register,
|
||||
openModal,
|
||||
openModal: openModal as any,
|
||||
getIconWidth,
|
||||
sourceValue,
|
||||
getClass,
|
||||
|
@ -14,7 +14,7 @@ export interface DescItem {
|
||||
// render
|
||||
render?: (
|
||||
val: any,
|
||||
data: Recordable
|
||||
data: Recordable,
|
||||
) => VNode | undefined | JSX.Element | Element | string | number;
|
||||
}
|
||||
|
||||
|
@ -139,7 +139,7 @@
|
||||
(newVal, oldVal) => {
|
||||
if (newVal !== oldVal) visibleRef.value = newVal;
|
||||
},
|
||||
{ deep: true }
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
@ -149,7 +149,7 @@
|
||||
emit('visible-change', visible);
|
||||
instance && drawerInstance.emitVisible?.(visible, instance.uid);
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Cancel event
|
||||
@ -181,9 +181,9 @@
|
||||
onClose,
|
||||
t,
|
||||
prefixCls,
|
||||
getMergeProps,
|
||||
getMergeProps: getMergeProps as any,
|
||||
getScrollContentStyle,
|
||||
getProps,
|
||||
getProps: getProps as any,
|
||||
getLoading,
|
||||
getBindValues,
|
||||
getFooterHeight,
|
||||
|
@ -79,7 +79,7 @@
|
||||
() => props.data,
|
||||
() => {
|
||||
onRender();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// TODO
|
||||
@ -94,7 +94,7 @@
|
||||
() => unref(getFlowOptions),
|
||||
(options) => {
|
||||
unref(lfInstance)?.updateEditConfig(options);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// init logicFlow
|
||||
|
@ -24,12 +24,12 @@
|
||||
</FormItem>
|
||||
</template>
|
||||
|
||||
<FormAction v-bind="{ ...getProps, ...advanceState }" @toggle-advanced="handleToggleAdvanced">
|
||||
<FormAction v-bind="getFormActionBindProps" @toggle-advanced="handleToggleAdvanced">
|
||||
<template
|
||||
#[item]="data"
|
||||
v-for="item in ['resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']"
|
||||
>
|
||||
<slot :name="item" v-bind="data"></slot>
|
||||
<slot :name="item" v-bind="data || {}"></slot>
|
||||
</template>
|
||||
</FormAction>
|
||||
<slot name="formFooter"></slot>
|
||||
@ -62,8 +62,6 @@
|
||||
import { basicProps } from './props';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
import type { RowProps } from 'ant-design-vue/lib/grid/Row';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BasicForm',
|
||||
components: { FormItem, Form, Row, FormAction },
|
||||
@ -103,7 +101,7 @@
|
||||
});
|
||||
|
||||
// Get uniform row style and Row configuration for the entire form
|
||||
const getRow = computed((): RowProps => {
|
||||
const getRow = computed((): Recordable => {
|
||||
const { baseRowStyle = {}, rowProps } = unref(getProps);
|
||||
return {
|
||||
style: baseRowStyle,
|
||||
@ -112,7 +110,7 @@
|
||||
});
|
||||
|
||||
const getBindValue = computed(
|
||||
() => ({ ...attrs, ...props, ...unref(getProps) } as Recordable)
|
||||
() => ({ ...attrs, ...props, ...unref(getProps) } as Recordable),
|
||||
);
|
||||
|
||||
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({
|
||||
@ -196,14 +198,14 @@
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => unref(getProps).schemas,
|
||||
(schemas) => {
|
||||
resetSchema(schemas ?? []);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
@ -220,7 +222,7 @@
|
||||
initDefault();
|
||||
isInitedDefaultRef.value = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
async function setProps(formProps: Partial<FormProps>): Promise<void> {
|
||||
@ -278,10 +280,12 @@
|
||||
getProps,
|
||||
formElRef,
|
||||
getSchema,
|
||||
formActionType,
|
||||
formActionType: formActionType as any,
|
||||
setFormModel,
|
||||
prefixCls,
|
||||
getFormClass,
|
||||
getFormActionBindProps: computed(
|
||||
(): Recordable => ({ ...getProps.value, ...advanceState }),
|
||||
),
|
||||
...formActionType,
|
||||
};
|
||||
},
|
||||
|
@ -18,6 +18,7 @@ import {
|
||||
TreeSelect,
|
||||
Slider,
|
||||
Rate,
|
||||
Divider,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
import RadioButtonGroup from './components/RadioButtonGroup.vue';
|
||||
@ -61,6 +62,7 @@ componentMap.set('IconPicker', IconPicker);
|
||||
componentMap.set('InputCountDown', CountdownInput);
|
||||
|
||||
componentMap.set('Upload', BasicUpload);
|
||||
componentMap.set('Divider', Divider);
|
||||
|
||||
export function add(compName: ComponentType, component: Component) {
|
||||
componentMap.set(compName, component);
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<Select
|
||||
@dropdownVisibleChange="handleFetch"
|
||||
v-bind="attrs"
|
||||
v-bind="$attrs"
|
||||
@change="handleChange"
|
||||
:options="getOptions"
|
||||
v-model:value="state"
|
||||
@ -41,12 +41,7 @@
|
||||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
value: propTypes.oneOfType([
|
||||
propTypes.object,
|
||||
propTypes.number,
|
||||
propTypes.string,
|
||||
propTypes.array,
|
||||
]),
|
||||
value: [Array, Object, String, Number],
|
||||
numberToString: propTypes.bool,
|
||||
api: {
|
||||
type: Function as PropType<(arg?: Recordable) => Promise<OptionsItem[]>>,
|
||||
@ -100,7 +95,7 @@
|
||||
() => {
|
||||
!unref(isFirstLoad) && fetch();
|
||||
},
|
||||
{ deep: true }
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
async function fetch() {
|
||||
|
@ -46,14 +46,14 @@
|
||||
() => {
|
||||
isFirstLoaded.value && fetch();
|
||||
},
|
||||
{ deep: true }
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.immediate,
|
||||
(v) => {
|
||||
v && !isFirstLoaded.value && fetch();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -105,7 +105,7 @@
|
||||
{
|
||||
text: t('common.resetText'),
|
||||
},
|
||||
props.resetButtonOptions
|
||||
props.resetButtonOptions,
|
||||
);
|
||||
});
|
||||
|
||||
@ -114,7 +114,7 @@
|
||||
{
|
||||
text: t('common.queryText'),
|
||||
},
|
||||
props.submitButtonOptions
|
||||
props.submitButtonOptions,
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
import type { ValidationRule } from 'ant-design-vue/lib/form/Form';
|
||||
import type { TableActionType } from '/@/components/Table';
|
||||
import { defineComponent, computed, unref, toRefs } from 'vue';
|
||||
import { Form, Col } from 'ant-design-vue';
|
||||
import { Form, Col, Divider } from 'ant-design-vue';
|
||||
import { componentMap } from '../componentMap';
|
||||
import { BasicHelp } from '/@/components/Basic';
|
||||
import { isBoolean, isFunction, isNull } from '/@/utils/is';
|
||||
@ -73,11 +73,17 @@
|
||||
|
||||
const getComponentsProps = computed(() => {
|
||||
const { schema, tableAction, formModel, formActionType } = props;
|
||||
const { componentProps = {} } = schema;
|
||||
if (!isFunction(componentProps)) {
|
||||
return componentProps;
|
||||
let { componentProps = {} } = schema;
|
||||
if (isFunction(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(() => {
|
||||
@ -177,7 +183,7 @@
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -300,38 +306,46 @@
|
||||
}
|
||||
|
||||
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 { colon } = props.formProps;
|
||||
|
||||
const getContent = () => {
|
||||
return slot
|
||||
? getSlot(slots, slot, unref(getValues))
|
||||
: render
|
||||
? render(unref(getValues))
|
||||
: renderComponent();
|
||||
};
|
||||
if (component === 'Divider') {
|
||||
return (
|
||||
<Col span={24}>
|
||||
<Divider {...unref(getComponentsProps)}>{renderLabelHelpMessage()}</Divider>
|
||||
</Col>
|
||||
);
|
||||
} else {
|
||||
const getContent = () => {
|
||||
return slot
|
||||
? getSlot(slots, slot, unref(getValues))
|
||||
: render
|
||||
? render(unref(getValues))
|
||||
: renderComponent();
|
||||
};
|
||||
|
||||
const showSuffix = !!suffix;
|
||||
const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix;
|
||||
const showSuffix = !!suffix;
|
||||
const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix;
|
||||
|
||||
return (
|
||||
<Form.Item
|
||||
name={field}
|
||||
colon={colon}
|
||||
class={{ 'suffix-item': showSuffix }}
|
||||
{...(itemProps as Recordable)}
|
||||
label={renderLabelHelpMessage()}
|
||||
rules={handleRules()}
|
||||
labelCol={labelCol}
|
||||
wrapperCol={wrapperCol}
|
||||
>
|
||||
<div style="display:flex">
|
||||
<div style="flex:1">{getContent()}</div>
|
||||
{showSuffix && <span class="suffix">{getSuffix}</span>}
|
||||
</div>
|
||||
</Form.Item>
|
||||
);
|
||||
return (
|
||||
<Form.Item
|
||||
name={field}
|
||||
colon={colon}
|
||||
class={{ 'suffix-item': showSuffix }}
|
||||
{...(itemProps as Recordable)}
|
||||
label={renderLabelHelpMessage()}
|
||||
rules={handleRules()}
|
||||
labelCol={labelCol}
|
||||
wrapperCol={wrapperCol}
|
||||
>
|
||||
<div style="display:flex">
|
||||
<div style="flex:1">{getContent()}</div>
|
||||
{showSuffix && <span class="suffix">{getSuffix}</span>}
|
||||
</div>
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
|
@ -38,7 +38,7 @@ function genType() {
|
||||
export function setComponentRuleType(
|
||||
rule: ValidationRule,
|
||||
component: ComponentType,
|
||||
valueFormat: string
|
||||
valueFormat: string,
|
||||
) {
|
||||
if (['DatePicker', 'MonthPicker', 'WeekPicker', 'TimePicker'].includes(component)) {
|
||||
rule.type = valueFormat ? 'string' : 'object';
|
||||
|
@ -58,7 +58,7 @@ export default function ({
|
||||
debounceUpdateAdvanced();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
function getAdvanced(itemCol: Partial<ColEx>, itemColSum = 0, isLastAction = false) {
|
||||
@ -139,7 +139,7 @@ export default function ({
|
||||
if (isShow && (colProps || baseColProps)) {
|
||||
const { itemColSum: sum, isAdvanced } = getAdvanced(
|
||||
{ ...baseColProps, ...colProps },
|
||||
itemColSum
|
||||
itemColSum,
|
||||
);
|
||||
|
||||
itemColSum = sum || 0;
|
||||
|
@ -18,7 +18,7 @@ export function useForm(props?: Props): UseFormReturnType {
|
||||
const form = unref(formRef);
|
||||
if (!form) {
|
||||
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();
|
||||
@ -44,7 +44,7 @@ export function useForm(props?: Props): UseFormReturnType {
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -96,7 +96,7 @@ export function useForm(props?: Props): UseFormReturnType {
|
||||
appendSchemaByField: async (
|
||||
schema: FormSchema,
|
||||
prefixField: string | undefined,
|
||||
first: boolean
|
||||
first: boolean,
|
||||
) => {
|
||||
const form = await getForm();
|
||||
form.appendSchemaByField(schema, prefixField, first);
|
||||
|
@ -149,11 +149,13 @@ export function useFormEvents({
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
@ -169,11 +171,13 @@ export function useFormEvents({
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ export interface FormActionType {
|
||||
appendSchemaByField: (
|
||||
schema: FormSchema,
|
||||
prefixField: string | undefined,
|
||||
first?: boolean | undefined
|
||||
first?: boolean | undefined,
|
||||
) => Promise<void>;
|
||||
validateFields: (nameList?: NamePath[]) => Promise<any>;
|
||||
validate: (nameList?: NamePath[]) => Promise<any>;
|
||||
|
@ -109,4 +109,5 @@ export type ComponentType =
|
||||
| 'IconPicker'
|
||||
| 'Render'
|
||||
| 'Slider'
|
||||
| 'Rate';
|
||||
| 'Rate'
|
||||
| 'Divider';
|
||||
|
@ -63,7 +63,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<template v-else
|
||||
><div class="p-5"> <Empty /></div>
|
||||
><div class="p-5"><Empty /></div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@ -139,7 +139,7 @@
|
||||
|
||||
const { getPaginationList, getTotal, setCurrentPage } = usePagination(
|
||||
currentList,
|
||||
props.pageSize
|
||||
props.pageSize,
|
||||
);
|
||||
|
||||
watchEffect(() => {
|
||||
@ -151,7 +151,7 @@
|
||||
(v) => {
|
||||
emit('update:value', v);
|
||||
return emit('change', v);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
function handlePageChange(page: number) {
|
||||
|
@ -4,7 +4,7 @@ import type { LoadingProps } from './typing';
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
export interface UseLoadingOptions {
|
||||
target?: HTMLElement | Ref<ElRef>;
|
||||
target?: any;
|
||||
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<LoadingProps> | Partial<UseLoadingOptions>
|
||||
opt: Partial<LoadingProps> | Partial<UseLoadingOptions>,
|
||||
): [Fn, Fn, (string) => void] {
|
||||
let props: Partial<LoadingProps>;
|
||||
let target: HTMLElement | Ref<ElRef> = document.body;
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { withInstall } from '/@/utils';
|
||||
import markDown from './src/Markdown.vue';
|
||||
import markDownViewer from './src/MarkdownViewer.vue';
|
||||
|
||||
export const MarkDown = withInstall(markDown);
|
||||
export const MarkdownViewer = withInstall(markDownViewer);
|
||||
export * from './src/typing';
|
||||
|
@ -2,6 +2,7 @@
|
||||
<div ref="wrapRef"></div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { Ref } from 'vue';
|
||||
import {
|
||||
defineComponent,
|
||||
ref,
|
||||
@ -30,14 +31,14 @@
|
||||
emits: ['change', 'get', 'update:value'],
|
||||
setup(props, { attrs, emit }) {
|
||||
const wrapRef = ref<ElRef>(null);
|
||||
const vditorRef = ref<Nullable<Vditor>>(null);
|
||||
const vditorRef = ref(null) as Ref<Nullable<Vditor>>;
|
||||
const initedRef = ref(false);
|
||||
|
||||
const modalFn = useModalContext();
|
||||
|
||||
const { getLocale } = useLocale();
|
||||
const { getDarkMode } = useRootSetting();
|
||||
const valueRef = ref('');
|
||||
const valueRef = ref(props.value || '');
|
||||
|
||||
watch(
|
||||
[() => getDarkMode.value, () => initedRef.value],
|
||||
@ -51,7 +52,7 @@
|
||||
{
|
||||
immediate: true,
|
||||
flush: 'post',
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
@ -61,7 +62,7 @@
|
||||
instance.getVditor()?.setValue(v);
|
||||
}
|
||||
valueRef.value = v;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const getCurrentLang = computed((): 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' => {
|
||||
@ -89,6 +90,9 @@
|
||||
theme: getDarkMode.value === 'dark' ? 'dark' : 'classic',
|
||||
lang: unref(getCurrentLang),
|
||||
mode: 'sv',
|
||||
fullscreen: {
|
||||
index: 520,
|
||||
},
|
||||
preview: {
|
||||
actions: [],
|
||||
},
|
||||
|
22
src/components/Markdown/src/MarkdownViewer.vue
Normal file
22
src/components/Markdown/src/MarkdownViewer.vue
Normal 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>
|
@ -56,15 +56,15 @@
|
||||
const { prefixCls } = useDesign('basic-menu');
|
||||
const { items, mode, accordion } = toRefs(props);
|
||||
|
||||
const { getCollapsed, getIsHorizontal, getTopMenuAlign, getSplit } = useMenuSetting();
|
||||
const { getCollapsed, getTopMenuAlign, getSplit } = useMenuSetting();
|
||||
|
||||
const { currentRoute } = useRouter();
|
||||
|
||||
const { handleOpenChange, setOpenKeys, getOpenKeys } = useOpenKeys(
|
||||
menuState,
|
||||
items,
|
||||
mode,
|
||||
accordion
|
||||
mode as any,
|
||||
accordion,
|
||||
);
|
||||
|
||||
const getIsTopMenu = computed(() => {
|
||||
@ -114,7 +114,7 @@
|
||||
() => props.items,
|
||||
() => {
|
||||
handleMenuChange();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
async function handleMenuClick({ key }: { key: string; keyPath: string[] }) {
|
||||
@ -126,9 +126,6 @@
|
||||
emit('menuClick', key);
|
||||
|
||||
isClickGo.value = true;
|
||||
// const parentPath = await getCurrentParentPath(key);
|
||||
|
||||
// menuState.openKeys = [parentPath];
|
||||
menuState.selectedKeys = [key];
|
||||
}
|
||||
|
||||
@ -150,8 +147,6 @@
|
||||
}
|
||||
|
||||
return {
|
||||
prefixCls,
|
||||
getIsHorizontal,
|
||||
handleMenuClick,
|
||||
getInlineCollapseOptions,
|
||||
getMenuClass,
|
||||
|
@ -1,13 +1,11 @@
|
||||
<template>
|
||||
<MenuItem :key="item.path">
|
||||
<!-- <MenuItem :class="getLevelClass"> -->
|
||||
<MenuItemContent v-bind="$props" :item="item" />
|
||||
</MenuItem>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { Menu } from 'ant-design-vue';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { itemProps } from '../props';
|
||||
|
||||
import MenuItemContent from './MenuItemContent.vue';
|
||||
@ -15,20 +13,8 @@
|
||||
name: 'BasicMenuItem',
|
||||
components: { MenuItem: Menu.Item, MenuItemContent },
|
||||
props: itemProps,
|
||||
setup() // props
|
||||
{
|
||||
const { prefixCls } = useDesign('basic-menu-item');
|
||||
|
||||
// const getLevelClass = computed(() => {
|
||||
// const { level, theme } = props;
|
||||
|
||||
// const levelCls = [`${prefixCls}__level${level}`, theme];
|
||||
// return levelCls;
|
||||
// });
|
||||
return {
|
||||
prefixCls,
|
||||
// getLevelClass,
|
||||
};
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -4,6 +4,8 @@ import type { PropType } from 'vue';
|
||||
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
|
||||
import { ThemeEnum } from '/@/enums/appEnum';
|
||||
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 = {
|
||||
items: {
|
||||
type: Array as PropType<Menu[]>,
|
||||
@ -14,7 +16,7 @@ export const basicProps = {
|
||||
inlineIndent: propTypes.number.def(20),
|
||||
// 菜单组件的mode属性
|
||||
mode: {
|
||||
type: String as PropType<MenuModeEnum>,
|
||||
type: String as PropType<MenuMode>,
|
||||
default: MenuModeEnum.INLINE,
|
||||
},
|
||||
|
||||
@ -22,7 +24,10 @@ export const basicProps = {
|
||||
type: String as PropType<MenuTypeEnum>,
|
||||
default: MenuTypeEnum.MIX,
|
||||
},
|
||||
theme: propTypes.string.def(ThemeEnum.DARK),
|
||||
theme: {
|
||||
type: String as PropType<MenuTheme>,
|
||||
default: ThemeEnum.DARK,
|
||||
},
|
||||
inlineCollapsed: propTypes.bool,
|
||||
mixSider: propTypes.bool,
|
||||
|
||||
|
@ -14,7 +14,7 @@ export function useOpenKeys(
|
||||
menuState: MenuState,
|
||||
menus: Ref<MenuType[]>,
|
||||
mode: Ref<MenuModeEnum>,
|
||||
accordion: Ref<boolean>
|
||||
accordion: Ref<boolean>,
|
||||
) {
|
||||
const { getCollapsed, getIsMixSidebar } = useMenuSetting();
|
||||
|
||||
@ -37,7 +37,7 @@ export function useOpenKeys(
|
||||
}
|
||||
},
|
||||
16,
|
||||
!native
|
||||
!native,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Modal v-bind="getBindValue" @cancel="handleCancel">
|
||||
<Modal v-bind="getBindValue">
|
||||
<template #closeIcon v-if="!$slots.closeIcon">
|
||||
<ModalClose
|
||||
:canFullscreen="getProps.canFullscreen"
|
||||
@ -44,7 +44,7 @@
|
||||
</ModalWrapper>
|
||||
|
||||
<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>
|
||||
</Modal>
|
||||
</template>
|
||||
@ -104,7 +104,7 @@
|
||||
}
|
||||
|
||||
// Custom title component: get title
|
||||
const getMergeProps = computed((): ModalProps => {
|
||||
const getMergeProps = computed((): Recordable => {
|
||||
return {
|
||||
...props,
|
||||
...(unref(propsRef) as any),
|
||||
@ -118,7 +118,7 @@
|
||||
});
|
||||
|
||||
// modal component does not need title and origin buttons
|
||||
const getProps = computed((): ModalProps => {
|
||||
const getProps = computed((): Recordable => {
|
||||
const opt = {
|
||||
...unref(getMergeProps),
|
||||
visible: unref(visibleRef),
|
||||
@ -169,7 +169,7 @@
|
||||
},
|
||||
{
|
||||
immediate: false,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// 取消事件
|
||||
@ -212,7 +212,7 @@
|
||||
extHeightRef.value = height;
|
||||
}
|
||||
|
||||
function handleTitleDbClick(e: ChangeEvent) {
|
||||
function handleTitleDbClick(e) {
|
||||
if (!props.canFullscreen) return;
|
||||
e.stopPropagation();
|
||||
handleFullScreen(e);
|
||||
|
@ -62,7 +62,7 @@
|
||||
{
|
||||
attributes: true,
|
||||
subtree: true,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
createModalContext({
|
||||
@ -89,7 +89,7 @@
|
||||
} else {
|
||||
minRealHeightRef.value = realHeightRef.value;
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
@ -125,7 +125,7 @@
|
||||
const modalDom = bodyDom.parentElement && bodyDom.parentElement.parentElement;
|
||||
if (!modalDom) return;
|
||||
|
||||
const modalRect = getComputedStyle(modalDom).top;
|
||||
const modalRect = getComputedStyle(modalDom as Element).top;
|
||||
const modalTop = Number.parseInt(modalRect);
|
||||
let maxHeight =
|
||||
window.innerHeight -
|
||||
|
@ -48,6 +48,7 @@ export function useModal(): UseModalReturnType {
|
||||
if (unref(loaded) && isProdMode() && modalMethod === unref(modal)) return;
|
||||
|
||||
modal.value = modalMethod;
|
||||
loaded.value = true;
|
||||
modalMethod.emitVisible = (visible: boolean, uid: number) => {
|
||||
visibleData[uid] = visible;
|
||||
};
|
||||
|
@ -19,7 +19,7 @@
|
||||
width: 520px;
|
||||
padding-bottom: 0;
|
||||
|
||||
.scrollbar {
|
||||
.ant-modal-body > .scrollbar {
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,7 @@
|
||||
contentFullHeight: propTypes.bool,
|
||||
contentClass: propTypes.string,
|
||||
fixedHeight: propTypes.bool,
|
||||
upwardSpace: propTypes.oneOfType([propTypes.number, propTypes.string]).def(0),
|
||||
},
|
||||
setup(props, { slots, attrs }) {
|
||||
const wrapperRef = ref(null);
|
||||
@ -71,18 +72,20 @@
|
||||
|
||||
provide(
|
||||
PageWrapperFixedHeightKey,
|
||||
computed(() => props.fixedHeight)
|
||||
computed(() => props.fixedHeight),
|
||||
);
|
||||
|
||||
const getIsContentFullHeight = computed(() => {
|
||||
return props.contentFullHeight;
|
||||
});
|
||||
|
||||
const getUpwardSpace = computed(() => props.upwardSpace);
|
||||
const { redoHeight, setCompensation, contentHeight } = useContentHeight(
|
||||
getIsContentFullHeight,
|
||||
wrapperRef,
|
||||
[headerRef, footerRef],
|
||||
[contentRef]
|
||||
[contentRef],
|
||||
getUpwardSpace,
|
||||
);
|
||||
setCompensation({ useLayoutFooter: true, elements: [footerRef] });
|
||||
|
||||
@ -135,7 +138,7 @@
|
||||
{
|
||||
flush: 'post',
|
||||
immediate: true,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -56,7 +56,7 @@
|
||||
setup(props) {
|
||||
const { prefixCls } = useDesign('image-preview');
|
||||
|
||||
const getImageList = computed(() => {
|
||||
const getImageList = computed((): any[] => {
|
||||
const { imageList } = props;
|
||||
if (!imageList) {
|
||||
return [];
|
||||
|
@ -103,7 +103,7 @@
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return { wrapRef, download };
|
||||
|
@ -44,7 +44,7 @@ export default defineComponent({
|
||||
|
||||
const clickTrackHandler = (e: any) => {
|
||||
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 thumbPositionPercentage =
|
||||
@ -104,7 +104,7 @@ export default defineComponent({
|
||||
move: props.move,
|
||||
bar: bar.value,
|
||||
}),
|
||||
})
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
@ -75,7 +75,7 @@
|
||||
items,
|
||||
accordion,
|
||||
mixSider,
|
||||
collapse
|
||||
collapse,
|
||||
);
|
||||
|
||||
const getBindValues = computed(() => ({ ...attrs, ...props }));
|
||||
@ -89,7 +89,7 @@
|
||||
setOpenKeys(currentRoute.value.path);
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
@ -100,7 +100,7 @@
|
||||
}
|
||||
setOpenKeys(currentRoute.value.path);
|
||||
},
|
||||
{ flush: 'post' }
|
||||
{ flush: 'post' },
|
||||
);
|
||||
|
||||
listenerRouteChange((route) => {
|
||||
|
@ -87,7 +87,7 @@
|
||||
nextTick(() => {
|
||||
updateOpened();
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
function updateOpened() {
|
||||
@ -124,7 +124,7 @@
|
||||
isRemoveAllPopup,
|
||||
sliceIndex,
|
||||
level: 0,
|
||||
props,
|
||||
props: props as any,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -98,7 +98,7 @@
|
||||
active.value = false;
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
return { getClass, prefixCls, getItemStyle, getCollapse, handleClickItem, showTooptip };
|
||||
|
@ -286,7 +286,7 @@
|
||||
if (props.name && Array.isArray(data)) {
|
||||
state.opened = (data as (string | number)[]).includes(props.name);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
rootMenuEmitter.on('on-update-active-name:submenu', (data: number[]) => {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import type { InjectionKey, Ref } from 'vue';
|
||||
import type { Emitter } from '/@/utils/mitt';
|
||||
import { createContext, useContext } from '/@/hooks/core/useContext';
|
||||
import mitt from '/@/utils/mitt';
|
||||
|
||||
export interface SimpleRootMenuContextProps {
|
||||
rootMenuEmitter: typeof mitt;
|
||||
rootMenuEmitter: Emitter;
|
||||
activeName: Ref<string | number>;
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ export function useOpenKeys(
|
||||
menus: Ref<MenuType[]>,
|
||||
accordion: Ref<boolean>,
|
||||
mixSider: Ref<boolean>,
|
||||
collapse: Ref<boolean>
|
||||
collapse: Ref<boolean>,
|
||||
) {
|
||||
const debounceSetOpenKeys = useDebounceFn(setOpenKeys, 50);
|
||||
async function setOpenKeys(path: string) {
|
||||
@ -38,7 +38,7 @@ export function useOpenKeys(
|
||||
menuState.activeSubMenuNames = menuState.openNames;
|
||||
},
|
||||
30,
|
||||
native
|
||||
native,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@
|
||||
() => unref(innerValueRef),
|
||||
(val) => {
|
||||
emit('change', val);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -10,7 +10,7 @@
|
||||
@advanced-change="redoHeight"
|
||||
>
|
||||
<template #[replaceFormSlotKey(item)]="data" v-for="item in getFormSlotKeys">
|
||||
<slot :name="item" v-bind="data"></slot>
|
||||
<slot :name="item" v-bind="data || {}"></slot>
|
||||
</template>
|
||||
</BasicForm>
|
||||
|
||||
@ -111,7 +111,7 @@
|
||||
unref(isFixedHeightPage) &&
|
||||
props.canResize &&
|
||||
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,
|
||||
setTableData,
|
||||
updateTableDataRecord,
|
||||
deleteTableDataRecord,
|
||||
insertTableDataRecord,
|
||||
findTableDataRecord,
|
||||
fetch,
|
||||
getRowKey,
|
||||
@ -157,7 +159,7 @@
|
||||
getFieldsValue: formActions.getFieldsValue,
|
||||
clearSelectedRowKeys,
|
||||
},
|
||||
emit
|
||||
emit,
|
||||
);
|
||||
|
||||
function handleTableChange(...args) {
|
||||
@ -182,7 +184,7 @@
|
||||
tableElRef,
|
||||
getColumnsRef,
|
||||
getRowSelectionRef,
|
||||
getDataSourceRef
|
||||
getDataSourceRef,
|
||||
);
|
||||
|
||||
const { customRow } = useCustomRow(getProps, {
|
||||
@ -211,7 +213,7 @@
|
||||
getProps,
|
||||
getScrollRef,
|
||||
tableElRef,
|
||||
getDataSourceRef
|
||||
getDataSourceRef,
|
||||
);
|
||||
|
||||
const { getFormProps, replaceFormSlotKey, getFormSlotKeys, handleSearchInfoChange } =
|
||||
@ -279,6 +281,8 @@
|
||||
setPagination,
|
||||
setTableData,
|
||||
updateTableDataRecord,
|
||||
deleteTableDataRecord,
|
||||
insertTableDataRecord,
|
||||
findTableDataRecord,
|
||||
redoHeight,
|
||||
setSelectedRowKeys,
|
||||
@ -320,7 +324,7 @@
|
||||
wrapRef,
|
||||
tableAction,
|
||||
redoHeight,
|
||||
getFormProps,
|
||||
getFormProps: getFormProps as any,
|
||||
replaceFormSlotKey,
|
||||
getFormSlotKeys,
|
||||
getWrapperClass,
|
||||
@ -334,6 +338,13 @@
|
||||
|
||||
@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} {
|
||||
max-width: 100%;
|
||||
|
||||
@ -413,7 +424,7 @@
|
||||
|
||||
.ant-table-body {
|
||||
overflow-x: hidden !important;
|
||||
overflow-y: scroll !important;
|
||||
// overflow-y: scroll !important;
|
||||
}
|
||||
|
||||
td {
|
||||
|
@ -14,11 +14,7 @@
|
||||
<Divider
|
||||
type="vertical"
|
||||
class="action-divider"
|
||||
v-if="
|
||||
divider &&
|
||||
index < getActions.length - (dropDownActions ? 0 : 1) &&
|
||||
getDropdownList.length > 0
|
||||
"
|
||||
v-if="divider && index < getActions.length - 1"
|
||||
/>
|
||||
</template>
|
||||
<Dropdown
|
||||
@ -95,7 +91,7 @@
|
||||
.map((action) => {
|
||||
const { popConfirm } = action;
|
||||
return {
|
||||
getPopupContainer: () => unref(table?.wrapRef.value) ?? document.body,
|
||||
getPopupContainer: () => unref((table as any)?.wrapRef.value) ?? document.body,
|
||||
type: 'link',
|
||||
size: 'small',
|
||||
...action,
|
||||
@ -107,7 +103,7 @@
|
||||
});
|
||||
});
|
||||
|
||||
const getDropdownList = computed(() => {
|
||||
const getDropdownList = computed((): any[] => {
|
||||
return (toRaw(props.dropDownActions) || [])
|
||||
.filter((action) => {
|
||||
return hasPermission(action.auth) && isIfShow(action);
|
||||
@ -133,7 +129,7 @@
|
||||
|
||||
function getTooltip(data: string | TooltipProps): TooltipProps {
|
||||
return {
|
||||
getPopupContainer: () => unref(table?.wrapRef.value) ?? document.body,
|
||||
getPopupContainer: () => unref((table as any)?.wrapRef.value) ?? document.body,
|
||||
placement: 'bottom',
|
||||
...(isString(data) ? { title: data } : data),
|
||||
};
|
||||
|
@ -24,7 +24,7 @@
|
||||
<template v-for="(img, index) in imgList" :key="img">
|
||||
<Image
|
||||
:width="size"
|
||||
:style="{ 'margin-left': index === 0 ? 0 : margin }"
|
||||
:style="{ marginLeft: index === 0 ? 0 : margin }"
|
||||
:src="srcPrefix + img"
|
||||
/>
|
||||
</template>
|
||||
|
@ -21,7 +21,7 @@ export const CellComponent: FunctionalComponent = (
|
||||
popoverVisible,
|
||||
getPopupContainer,
|
||||
}: ComponentProps,
|
||||
{ attrs }
|
||||
{ attrs },
|
||||
) => {
|
||||
const Comp = componentMap.get(component) as typeof defineComponent;
|
||||
|
||||
@ -39,6 +39,6 @@ export const CellComponent: FunctionalComponent = (
|
||||
{
|
||||
default: () => DefaultComp,
|
||||
content: () => ruleMessage,
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
@ -5,31 +5,33 @@
|
||||
:class="{ [`${prefixCls}__normal`]: true, 'ellipsis-cell': column.ellipsis }"
|
||||
@click="handleEdit"
|
||||
>
|
||||
<div class="cell-content" :title="column.ellipsis ? getValues ?? '' : ''">{{
|
||||
getValues ?? ' '
|
||||
}}</div>
|
||||
<div class="cell-content" :title="column.ellipsis ? getValues ?? '' : ''">
|
||||
{{ getValues ? getValues : ' ' }}
|
||||
</div>
|
||||
<FormOutlined :class="`${prefixCls}__normal-icon`" v-if="!column.editRow" />
|
||||
</div>
|
||||
|
||||
<div v-if="isEdit" :class="`${prefixCls}__wrapper`" v-click-outside="onClickOutside">
|
||||
<CellComponent
|
||||
v-bind="getComponentProps"
|
||||
:component="getComponent"
|
||||
:style="getWrapperStyle"
|
||||
:popoverVisible="getRuleVisible"
|
||||
:rule="getRule"
|
||||
:ruleMessage="ruleMessage"
|
||||
:class="getWrapperClass"
|
||||
ref="elRef"
|
||||
@change="handleChange"
|
||||
@options-change="handleOptionsChange"
|
||||
@pressEnter="handleEnter"
|
||||
/>
|
||||
<div :class="`${prefixCls}__action`" v-if="!getRowEditable">
|
||||
<CheckOutlined :class="[`${prefixCls}__icon`, 'mx-2']" @click="handleSubmitClick" />
|
||||
<CloseOutlined :class="`${prefixCls}__icon `" @click="handleCancel" />
|
||||
<a-spin v-if="isEdit" :spinning="spinning">
|
||||
<div :class="`${prefixCls}__wrapper`" v-click-outside="onClickOutside">
|
||||
<CellComponent
|
||||
v-bind="getComponentProps"
|
||||
:component="getComponent"
|
||||
:style="getWrapperStyle"
|
||||
:popoverVisible="getRuleVisible"
|
||||
:rule="getRule"
|
||||
:ruleMessage="ruleMessage"
|
||||
:class="getWrapperClass"
|
||||
ref="elRef"
|
||||
@change="handleChange"
|
||||
@options-change="handleOptionsChange"
|
||||
@pressEnter="handleEnter"
|
||||
/>
|
||||
<div :class="`${prefixCls}__action`" v-if="!getRowEditable">
|
||||
<CheckOutlined :class="[`${prefixCls}__icon`, 'mx-2']" @click="handleSubmitClick" />
|
||||
<CloseOutlined :class="`${prefixCls}__icon `" @click="handleCancel" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
@ -48,12 +50,13 @@
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { isArray, isBoolean, isFunction, isNumber, isString } from '/@/utils/is';
|
||||
import { createPlaceholderMessage } from './helper';
|
||||
import { omit, set } from 'lodash-es';
|
||||
import { omit, pick, set } from 'lodash-es';
|
||||
import { treeToList } from '/@/utils/helper/treeHelper';
|
||||
import { Spin } from 'ant-design-vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'EditableCell',
|
||||
components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent },
|
||||
components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent, ASpin: Spin },
|
||||
directives: {
|
||||
clickOutside,
|
||||
},
|
||||
@ -80,6 +83,7 @@
|
||||
const optionsRef = ref<LabelValueOptions>([]);
|
||||
const currentValueRef = ref<any>(props.value);
|
||||
const defaultValueRef = ref<any>(props.value);
|
||||
const spinning = ref<boolean>(false);
|
||||
|
||||
const { prefixCls } = useDesign('editable-cell');
|
||||
|
||||
@ -246,6 +250,35 @@
|
||||
|
||||
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);
|
||||
//const record = await table.updateTableData(index, dataKey, value);
|
||||
needEmit && table.emit?.('edit-end', { record, index, key, value });
|
||||
@ -331,13 +364,7 @@
|
||||
/* eslint-disable */
|
||||
props.record.onSubmitEdit = async () => {
|
||||
if (isArray(props.record?.submitCbs)) {
|
||||
const validFns = (props.record?.validCbs || []).map((fn) => fn());
|
||||
|
||||
const res = await Promise.all(validFns);
|
||||
|
||||
const pass = res.every((item) => !!item);
|
||||
|
||||
if (!pass) return;
|
||||
if (!props.record?.onValid?.()) return;
|
||||
const submitFns = props.record?.submitCbs || [];
|
||||
submitFns.forEach((fn) => fn(false, false));
|
||||
table.emit?.('edit-row-end');
|
||||
@ -368,6 +395,7 @@
|
||||
getValues,
|
||||
handleEnter,
|
||||
handleSubmitClick,
|
||||
spinning,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@ -3,6 +3,7 @@ import type { BasicColumn } from '/@/components/Table/src/types/table';
|
||||
import { h, Ref } from 'vue';
|
||||
|
||||
import EditableCell from './EditableCell.vue';
|
||||
import { isArray } from '/@/utils/is';
|
||||
|
||||
interface Params {
|
||||
text: string;
|
||||
@ -12,12 +13,23 @@ interface Params {
|
||||
|
||||
export function renderEditCell(column: BasicColumn) {
|
||||
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) => {
|
||||
if (!submit) {
|
||||
record.editable = edit;
|
||||
}
|
||||
|
||||
if (!edit && submit) {
|
||||
if (!(await record.onValid())) return false;
|
||||
const res = await record.onSubmitEdit?.();
|
||||
if (res) {
|
||||
record.editable = false;
|
||||
@ -44,6 +56,7 @@ export function renderEditCell(column: BasicColumn) {
|
||||
export type EditRecordRow<T = Recordable> = Partial<
|
||||
{
|
||||
onEdit: (editable: boolean, submit?: boolean) => Promise<boolean>;
|
||||
onValid: () => Promise<boolean>;
|
||||
editable: boolean;
|
||||
onCancel: Fn;
|
||||
onSubmit: Fn;
|
||||
|
@ -42,7 +42,7 @@
|
||||
<ScrollContainer>
|
||||
<CheckboxGroup v-model:value="checkedList" @change="onChange" ref="columnListRef">
|
||||
<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" />
|
||||
<Checkbox :value="item.value">
|
||||
{{ item.label }}
|
||||
@ -351,7 +351,7 @@
|
||||
const visible =
|
||||
columns.findIndex(
|
||||
(c: BasicColumn | string) =>
|
||||
c === col.value || (typeof c !== 'string' && c.dataIndex === col.value)
|
||||
c === col.value || (typeof c !== 'string' && c.dataIndex === col.value),
|
||||
) !== -1;
|
||||
return { dataIndex: col.value, fixed: col.fixed, visible };
|
||||
});
|
||||
|
@ -40,7 +40,7 @@ function handleChildren(children: BasicColumn[] | undefined, ellipsis: boolean)
|
||||
function handleIndexColumn(
|
||||
propsRef: ComputedRef<BasicTableProps>,
|
||||
getPaginationRef: ComputedRef<boolean | PaginationProps>,
|
||||
columns: BasicColumn[]
|
||||
columns: BasicColumn[],
|
||||
) {
|
||||
const { t } = useI18n();
|
||||
|
||||
@ -102,7 +102,7 @@ function handleActionColumn(propsRef: ComputedRef<BasicTableProps>, columns: Bas
|
||||
|
||||
export function useColumns(
|
||||
propsRef: ComputedRef<BasicTableProps>,
|
||||
getPaginationRef: ComputedRef<boolean | PaginationProps>
|
||||
getPaginationRef: ComputedRef<boolean | PaginationProps>,
|
||||
) {
|
||||
const columnsRef = ref(unref(propsRef).columns) as unknown as Ref<BasicColumn[]>;
|
||||
let cacheColumns = unref(propsRef).columns;
|
||||
@ -122,7 +122,7 @@ export function useColumns(
|
||||
|
||||
handleItem(
|
||||
item,
|
||||
Reflect.has(item, 'ellipsis') ? !!item.ellipsis : !!ellipsis && !customRender && !slots
|
||||
Reflect.has(item, 'ellipsis') ? !!item.ellipsis : !!ellipsis && !customRender && !slots,
|
||||
);
|
||||
});
|
||||
return columns;
|
||||
@ -179,7 +179,7 @@ export function useColumns(
|
||||
(columns) => {
|
||||
columnsRef.value = columns;
|
||||
cacheColumns = columns?.filter((item) => !item.flag) ?? [];
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
function setCacheColumnsByField(dataIndex: string | undefined, value: Partial<BasicColumn>) {
|
||||
@ -288,7 +288,7 @@ function sortFixedColumn(columns: BasicColumn[]) {
|
||||
defColumns.push(column);
|
||||
}
|
||||
return [...fixedLeftColumns, ...defColumns, ...fixedRightColumns].filter(
|
||||
(item) => !item.defaultHidden
|
||||
(item) => !item.defaultHidden,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ interface Options {
|
||||
function getKey(
|
||||
record: Recordable,
|
||||
rowKey: string | ((record: Record<string, any>) => string) | undefined,
|
||||
autoCreateKey?: boolean
|
||||
autoCreateKey?: boolean,
|
||||
) {
|
||||
if (!rowKey || autoCreateKey) {
|
||||
return record[ROW_KEY];
|
||||
@ -31,7 +31,7 @@ function getKey(
|
||||
|
||||
export function useCustomRow(
|
||||
propsRef: ComputedRef<BasicTableProps>,
|
||||
{ setSelectedRowKeys, getSelectRowKeys, getAutoCreateKey, clearSelectedRowKeys, emit }: Options
|
||||
{ setSelectedRowKeys, getSelectRowKeys, getAutoCreateKey, clearSelectedRowKeys, emit }: Options,
|
||||
) {
|
||||
const customRow = (record: Recordable, index: number) => {
|
||||
return {
|
||||
|
@ -40,7 +40,7 @@ export function useDataSource(
|
||||
clearSelectedRowKeys,
|
||||
tableData,
|
||||
}: ActionType,
|
||||
emit: EmitType
|
||||
emit: EmitType,
|
||||
) {
|
||||
const searchState = reactive<SearchState>({
|
||||
sortInfo: {},
|
||||
@ -61,13 +61,13 @@ export function useDataSource(
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
function handleTableChange(
|
||||
pagination: PaginationProps,
|
||||
filters: Partial<Recordable<string[]>>,
|
||||
sorter: SorterResult
|
||||
sorter: SorterResult,
|
||||
) {
|
||||
const { clearSelectOnPageChange, sortFn, filterFn } = unref(propsRef);
|
||||
if (clearSelectOnPageChange) {
|
||||
@ -148,7 +148,7 @@ export function useDataSource(
|
||||
|
||||
function updateTableDataRecord(
|
||||
rowKey: string | number,
|
||||
record: Recordable
|
||||
record: Recordable,
|
||||
): Recordable | undefined {
|
||||
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) {
|
||||
if (!dataSourceRef.value || dataSourceRef.value.length == 0) return;
|
||||
|
||||
@ -206,7 +231,7 @@ export function useDataSource(
|
||||
const { pageField, sizeField, listField, totalField } = Object.assign(
|
||||
{},
|
||||
FETCH_SETTING,
|
||||
fetchSetting
|
||||
fetchSetting,
|
||||
);
|
||||
let pageParams: Recordable = {};
|
||||
|
||||
@ -314,6 +339,8 @@ export function useDataSource(
|
||||
reload,
|
||||
updateTableData,
|
||||
updateTableDataRecord,
|
||||
deleteTableDataRecord,
|
||||
insertTableDataRecord,
|
||||
findTableDataRecord,
|
||||
handleTableChange,
|
||||
};
|
||||
|
@ -8,7 +8,7 @@ export function useLoading(props: ComputedRef<BasicTableProps>) {
|
||||
() => unref(props).loading,
|
||||
(loading) => {
|
||||
loadingRef.value = loading;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const getLoading = computed(() => unref(loadingRef));
|
||||
|
@ -8,7 +8,7 @@ import { findNodeAll } from '/@/utils/helper/treeHelper';
|
||||
export function useRowSelection(
|
||||
propsRef: ComputedRef<BasicTableProps>,
|
||||
tableData: Ref<Recordable[]>,
|
||||
emit: EmitType
|
||||
emit: EmitType,
|
||||
) {
|
||||
const selectedRowKeysRef = ref<string[]>([]);
|
||||
const selectedRowRef = ref<Recordable[]>([]);
|
||||
@ -35,7 +35,7 @@ export function useRowSelection(
|
||||
() => unref(propsRef).rowSelection?.selectedRowKeys,
|
||||
(v: string[]) => {
|
||||
setSelectedRowKeys(v);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
@ -53,7 +53,7 @@ export function useRowSelection(
|
||||
});
|
||||
});
|
||||
},
|
||||
{ deep: true }
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
const getAutoCreateKey = computed(() => {
|
||||
@ -72,7 +72,7 @@ export function useRowSelection(
|
||||
(item) => rowKeys.includes(item[unref(getRowKey) as string]),
|
||||
{
|
||||
children: propsRef.value.childrenColumnName ?? 'children',
|
||||
}
|
||||
},
|
||||
);
|
||||
const trueSelectedRows: any[] = [];
|
||||
rowKeys.forEach((key: string) => {
|
||||
|
@ -18,7 +18,7 @@ export function useTable(tableProps?: Props): [
|
||||
(instance: TableActionType, formInstance: UseTableMethod) => void,
|
||||
TableActionType & {
|
||||
getForm: () => FormActionType;
|
||||
}
|
||||
},
|
||||
] {
|
||||
const tableRef = ref<Nullable<TableActionType>>(null);
|
||||
const loadedRef = ref<Nullable<boolean>>(false);
|
||||
@ -50,7 +50,7 @@ export function useTable(tableProps?: Props): [
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@ export function useTable(tableProps?: Props): [
|
||||
const table = unref(tableRef);
|
||||
if (!table) {
|
||||
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;
|
||||
@ -122,6 +122,12 @@ export function useTable(tableProps?: Props): [
|
||||
updateTableData: (index: number, key: string, value: any) => {
|
||||
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) => {
|
||||
return getTableInstance().updateTableDataRecord(rowKey, record);
|
||||
},
|
||||
|
@ -6,7 +6,7 @@ import { ROW_KEY } from '../const';
|
||||
export function useTableExpand(
|
||||
propsRef: ComputedRef<BasicTableProps>,
|
||||
tableData: Ref<Recordable[]>,
|
||||
emit: EmitType
|
||||
emit: EmitType,
|
||||
) {
|
||||
const expandedRowKeys = ref<string[]>([]);
|
||||
|
||||
|
@ -12,7 +12,7 @@ export function useTableFooter(
|
||||
scrollToFirstRowOnChange: boolean;
|
||||
}>,
|
||||
tableElRef: Ref<ComponentRef>,
|
||||
getDataSourceRef: ComputedRef<Recordable>
|
||||
getDataSourceRef: ComputedRef<Recordable>,
|
||||
) {
|
||||
const getIsEmptyData = computed(() => {
|
||||
return (unref(getDataSourceRef) || []).length === 0;
|
||||
@ -43,7 +43,7 @@ export function useTableFooter(
|
||||
name: 'scroll',
|
||||
listener: () => {
|
||||
const footerBodyDom = tableEl.$el.querySelector(
|
||||
'.ant-table-footer .ant-table-body'
|
||||
'.ant-table-footer .ant-table-body',
|
||||
) as HTMLDivElement;
|
||||
if (!footerBodyDom || !bodyDom) return;
|
||||
footerBodyDom.scrollLeft = bodyDom.scrollLeft;
|
||||
|
@ -8,7 +8,7 @@ export function useTableForm(
|
||||
propsRef: ComputedRef<BasicTableProps>,
|
||||
slots: Slots,
|
||||
fetch: (opt?: FetchParams | undefined) => Promise<void>,
|
||||
getLoading: ComputedRef<boolean | undefined>
|
||||
getLoading: ComputedRef<boolean | undefined>,
|
||||
) {
|
||||
const getFormProps = computed((): Partial<FormProps> => {
|
||||
const { formConfig } = unref(propsRef);
|
||||
|
@ -8,7 +8,7 @@ import { getSlot } from '/@/utils/helper/tsxHelper';
|
||||
export function useTableHeader(
|
||||
propsRef: ComputedRef<BasicTableProps>,
|
||||
slots: Slots,
|
||||
handlers: InnerHandlers
|
||||
handlers: InnerHandlers,
|
||||
) {
|
||||
const getHeaderProps = computed((): Recordable => {
|
||||
const { title, showTableSetting, titleHelpMessage, tableSetting } = unref(propsRef);
|
||||
@ -46,7 +46,7 @@ export function useTableHeader(
|
||||
headerTop: () => getSlot(slots, 'headerTop'),
|
||||
}
|
||||
: {}),
|
||||
}
|
||||
},
|
||||
),
|
||||
};
|
||||
});
|
||||
|
@ -13,7 +13,7 @@ export function useTableScroll(
|
||||
tableElRef: Ref<ComponentRef>,
|
||||
columnsRef: ComputedRef<BasicColumn[]>,
|
||||
rowSelectionRef: ComputedRef<TableRowSelection<any> | null>,
|
||||
getDataSourceRef: ComputedRef<Recordable[]>
|
||||
getDataSourceRef: ComputedRef<Recordable[]>,
|
||||
) {
|
||||
const tableHeightRef: Ref<Nullable<number>> = ref(null);
|
||||
|
||||
@ -34,7 +34,7 @@ export function useTableScroll(
|
||||
},
|
||||
{
|
||||
flush: 'post',
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
function redoHeight() {
|
||||
|
@ -6,11 +6,14 @@ import { isFunction } from '/@/utils/is';
|
||||
export function useTableStyle(propsRef: ComputedRef<BasicTableProps>, prefixCls: string) {
|
||||
function getRowClassName(record: TableCustomRecord, index: number) {
|
||||
const { striped, rowClassName } = unref(propsRef);
|
||||
if (!striped) return;
|
||||
if (rowClassName && isFunction(rowClassName)) {
|
||||
return rowClassName(record);
|
||||
const classNames: string[] = [];
|
||||
if (striped) {
|
||||
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 };
|
||||
|
@ -126,4 +126,14 @@ export const basicProps = {
|
||||
type: Object as PropType<{ x: number | true; y: number }>,
|
||||
default: null,
|
||||
},
|
||||
beforeEditSubmit: {
|
||||
type: Function as PropType<
|
||||
(data: {
|
||||
record: Recordable;
|
||||
index: number;
|
||||
key: string | number;
|
||||
value: any;
|
||||
}) => Promise<any>
|
||||
>,
|
||||
},
|
||||
};
|
||||
|
@ -25,7 +25,7 @@ export interface TableRowSelection<T = any> extends ITableRowSelection {
|
||||
|
||||
/**
|
||||
* Callback executed when select/deselect one row
|
||||
* @type FunctionT
|
||||
* @type Function
|
||||
*/
|
||||
onSelect?: (record: T, selected: boolean, selectedRows: Object[], nativeEvent: Event) => any;
|
||||
|
||||
@ -95,6 +95,8 @@ export interface TableActionType {
|
||||
setPagination: (info: Partial<PaginationProps>) => void;
|
||||
setTableData: <T = Recordable>(values: T[]) => 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;
|
||||
getColumns: (opt?: GetColumnsParams) => BasicColumn[];
|
||||
setColumns: (columns: BasicColumn[] | string[]) => void;
|
||||
@ -289,7 +291,7 @@ export interface BasicTableProps<T = any> {
|
||||
* Row's className
|
||||
* @type Function
|
||||
*/
|
||||
rowClassName?: (record: TableCustomRecord<T>) => string;
|
||||
rowClassName?: (record: TableCustomRecord<T>, index: number) => string;
|
||||
|
||||
/**
|
||||
* Row selection config
|
||||
@ -362,6 +364,18 @@ export interface BasicTableProps<T = any> {
|
||||
*/
|
||||
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
|
||||
* @param pagination
|
||||
|
@ -34,7 +34,7 @@
|
||||
() => {
|
||||
setTime();
|
||||
},
|
||||
{ immediate: true }
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
function getTime() {
|
||||
|
@ -8,12 +8,18 @@
|
||||
v-show="editorRef"
|
||||
: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>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import type { RawEditorSettings } from 'tinymce';
|
||||
import type { Editor, RawEditorSettings } from 'tinymce';
|
||||
import tinymce from 'tinymce/tinymce';
|
||||
import 'tinymce/themes/silver';
|
||||
import 'tinymce/icons/default/icons';
|
||||
@ -54,8 +60,8 @@
|
||||
ref,
|
||||
unref,
|
||||
watch,
|
||||
onUnmounted,
|
||||
onDeactivated,
|
||||
onBeforeUnmount,
|
||||
} from 'vue';
|
||||
import ImgUpload from './ImgUpload.vue';
|
||||
import { toolbar, plugins } from './tinymce';
|
||||
@ -108,9 +114,9 @@
|
||||
components: { ImgUpload },
|
||||
inheritAttrs: false,
|
||||
props: tinymceProps,
|
||||
emits: ['change', 'update:modelValue'],
|
||||
emits: ['change', 'update:modelValue', 'inited', 'init-error'],
|
||||
setup(props, { emit, attrs }) {
|
||||
const editorRef = ref();
|
||||
const editorRef = ref<Nullable<Editor>>(null);
|
||||
const fullscreen = ref(false);
|
||||
const tinymceId = ref<string>(buildShortUUID('tiny-vue'));
|
||||
const elRef = ref<Nullable<HTMLElement>>(null);
|
||||
@ -159,7 +165,7 @@
|
||||
content_css:
|
||||
publicPath + 'resource/tinymce/skins/ui/' + skinName.value + '/content.min.css',
|
||||
...options,
|
||||
setup: (editor) => {
|
||||
setup: (editor: Editor) => {
|
||||
editorRef.value = editor;
|
||||
editor.on('init', (e) => initSetup(e));
|
||||
},
|
||||
@ -184,11 +190,13 @@
|
||||
return;
|
||||
}
|
||||
editor.setMode(attrs.disabled ? 'readonly' : 'design');
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
onMountedOrActivated(() => {
|
||||
tinymceId.value = buildShortUUID('tiny-vue');
|
||||
if (!initOptions.value.inline) {
|
||||
tinymceId.value = buildShortUUID('tiny-vue');
|
||||
}
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
initEditor();
|
||||
@ -196,7 +204,7 @@
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
onBeforeUnmount(() => {
|
||||
destory();
|
||||
});
|
||||
|
||||
@ -206,7 +214,7 @@
|
||||
|
||||
function destory() {
|
||||
if (tinymce !== null) {
|
||||
tinymce?.remove?.(unref(editorRef));
|
||||
tinymce?.remove?.(unref(initOptions).selector!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,7 +223,14 @@
|
||||
if (el) {
|
||||
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) {
|
||||
@ -249,7 +264,7 @@
|
||||
() => props.modelValue,
|
||||
(val: string, prevVal: string) => {
|
||||
setValue(editor, val, prevVal);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
@ -259,7 +274,7 @@
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
editor.on(normalizedEvents ? normalizedEvents : 'change keyup undo redo', () => {
|
||||
|
@ -18,10 +18,10 @@ export const ScaleRotateTransition = createSimpleTransition('scale-rotate-transi
|
||||
|
||||
export const ExpandXTransition = createJavascriptTransition(
|
||||
'expand-x-transition',
|
||||
ExpandTransitionGenerator('', true)
|
||||
ExpandTransitionGenerator('', true),
|
||||
);
|
||||
|
||||
export const ExpandTransition = createJavascriptTransition(
|
||||
'expand-transition',
|
||||
ExpandTransitionGenerator('')
|
||||
ExpandTransitionGenerator(''),
|
||||
);
|
||||
|
@ -41,7 +41,7 @@ export function createSimpleTransition(name: string, origin = 'top center 0', mo
|
||||
export function createJavascriptTransition(
|
||||
name: string,
|
||||
functions: Recordable,
|
||||
mode: Mode = 'in-out'
|
||||
mode: Mode = 'in-out',
|
||||
) {
|
||||
return defineComponent({
|
||||
name,
|
||||
|
@ -19,9 +19,9 @@
|
||||
import { ScrollContainer } from '/@/components/Container';
|
||||
|
||||
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 { filter } from '/@/utils/helper/treeHelper';
|
||||
import { filter, treeToList } from '/@/utils/helper/treeHelper';
|
||||
|
||||
import { useTree } from './useTree';
|
||||
import { useContextMenu } from '/@/hooks/web/useContextMenu';
|
||||
@ -60,6 +60,7 @@
|
||||
|
||||
const searchState = reactive({
|
||||
startSearch: false,
|
||||
searchText: '',
|
||||
searchData: [] as TreeItem[],
|
||||
});
|
||||
|
||||
@ -119,7 +120,7 @@
|
||||
});
|
||||
|
||||
const getTreeData = computed((): TreeItem[] =>
|
||||
searchState.startSearch ? searchState.searchData : unref(treeDataRef)
|
||||
searchState.startSearch ? searchState.searchData : unref(treeDataRef),
|
||||
);
|
||||
|
||||
const getNotFound = computed((): boolean => {
|
||||
@ -199,28 +200,70 @@
|
||||
state.checkStrictly = strictly;
|
||||
}
|
||||
|
||||
const searchText = ref('');
|
||||
watchEffect(() => {
|
||||
if (props.searchValue !== searchText.value) searchText.value = props.searchValue;
|
||||
});
|
||||
watch(
|
||||
() => 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) {
|
||||
if (searchValue !== searchText.value) searchText.value = searchValue;
|
||||
if (searchValue !== searchState.searchText) searchState.searchText = searchValue;
|
||||
emit('update:searchValue', searchValue);
|
||||
if (!searchValue) {
|
||||
searchState.startSearch = false;
|
||||
return;
|
||||
}
|
||||
const { filterFn, checkable, expandOnSearch, checkOnSearch, selectedOnSearch } =
|
||||
unref(props);
|
||||
searchState.startSearch = true;
|
||||
const { title: titleField } = unref(getReplaceFields);
|
||||
const { title: titleField, key: keyField } = unref(getReplaceFields);
|
||||
|
||||
const matchedKeys: string[] = [];
|
||||
searchState.searchData = filter(
|
||||
unref(treeDataRef),
|
||||
(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[]) {
|
||||
@ -266,7 +309,7 @@
|
||||
() => props.value,
|
||||
() => {
|
||||
state.checkedKeys = toRaw(props.value || []);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
@ -275,7 +318,7 @@
|
||||
const v = toRaw(state.checkedKeys);
|
||||
emit('update:value', v);
|
||||
emit('change', v);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// watchEffect(() => {
|
||||
@ -311,7 +354,7 @@
|
||||
handleSearch(value);
|
||||
},
|
||||
getSearchValue: () => {
|
||||
return searchText.value;
|
||||
return searchState.searchText;
|
||||
},
|
||||
};
|
||||
|
||||
@ -342,6 +385,8 @@
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
const searchText = searchState.searchText;
|
||||
const { highlight } = unref(props);
|
||||
return data.map((item) => {
|
||||
const {
|
||||
title: titleField,
|
||||
@ -352,6 +397,23 @@
|
||||
const propsData = omit(item, 'title');
|
||||
const icon = getIcon({ ...item, level }, item.icon);
|
||||
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 (
|
||||
<Tree.TreeNode {...propsData} node={toRaw(item)} key={get(item, keyField)}>
|
||||
{{
|
||||
@ -365,11 +427,8 @@
|
||||
) : (
|
||||
<>
|
||||
{icon && <TreeIcon icon={icon} />}
|
||||
<span
|
||||
class={unref(getBindValues)?.blockNode ? `${prefixCls}__content` : ''}
|
||||
>
|
||||
{get(item, titleField)}
|
||||
</span>
|
||||
{titleDom}
|
||||
{/*{get(item, titleField)}*/}
|
||||
<span class={`${prefixCls}__actions`}>
|
||||
{renderAction({ ...item, level })}
|
||||
</span>
|
||||
@ -400,7 +459,7 @@
|
||||
helpMessage={helpMessage}
|
||||
onStrictlyChange={onStrictlyChange}
|
||||
onSearch={handleSearch}
|
||||
searchText={unref(searchText)}
|
||||
searchText={searchState.searchText}
|
||||
>
|
||||
{extendSlots(slots)}
|
||||
</TreeHeader>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user