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

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

2
.vscode/launch.json vendored
View File

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

View File

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

View File

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

View File

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

View File

@ -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 的全开源平台
## 维护者

View File

@ -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}]`,
);
});
}

View File

@ -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}`;

View File

@ -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;

View File

@ -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;

View File

@ -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

View 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() {

View File

@ -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

View File

@ -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) {

View File

@ -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: [
{

View File

@ -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',

View File

@ -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);

View File

@ -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');

View File

@ -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",

View File

@ -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,
};

View File

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

View File

@ -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,
);
}

View File

@ -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() {

View File

@ -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>

View File

@ -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() {

View File

@ -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 }));

View File

@ -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';

View File

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

View File

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

View File

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

View File

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

View File

@ -10,7 +10,7 @@
</template>
<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) {

View File

@ -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',
);
}

View File

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

View File

@ -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 => {

View File

@ -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,

View File

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

View File

@ -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,

View File

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

View File

@ -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,
};
},

View File

@ -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);

View File

@ -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() {

View File

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

View File

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

View File

@ -5,7 +5,7 @@
import type { ValidationRule } from 'ant-design-vue/lib/form/Form';
import type { 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 () => {

View File

@ -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';

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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>;

View File

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

View File

@ -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) {

View File

@ -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;

View File

@ -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';

View File

@ -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: [],
},

View File

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

View File

@ -56,15 +56,15 @@
const { prefixCls } = useDesign('basic-menu');
const { 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,

View File

@ -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>

View File

@ -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,

View File

@ -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,
);
}

View File

@ -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);

View File

@ -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 -

View File

@ -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;
};

View File

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

View File

@ -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 {

View File

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

View File

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

View File

@ -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,
}),
})
}),
);
},
});

View File

@ -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) => {

View File

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

View File

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

View File

@ -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[]) => {

View File

@ -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>;
}

View File

@ -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,
);
}

View File

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

View File

@ -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 {

View File

@ -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),
};

View File

@ -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>

View File

@ -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,
}
},
);
};

View File

@ -5,31 +5,33 @@
:class="{ [`${prefixCls}__normal`]: true, 'ellipsis-cell': column.ellipsis }"
@click="handleEdit"
>
<div class="cell-content" :title="column.ellipsis ? getValues ?? '' : ''">{{
getValues ?? '&nbsp;'
}}</div>
<div class="cell-content" :title="column.ellipsis ? getValues ?? '' : ''">
{{ getValues ? getValues : '&nbsp;' }}
</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,
};
},
});

View File

@ -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;

View File

@ -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 };
});

View File

@ -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,
);
}

View File

@ -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 {

View File

@ -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,
};

View File

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

View File

@ -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) => {

View File

@ -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);
},

View File

@ -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[]>([]);

View File

@ -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;

View File

@ -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);

View File

@ -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'),
}
: {}),
}
},
),
};
});

View File

@ -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() {

View File

@ -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 };

View File

@ -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>
>,
},
};

View File

@ -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

View File

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

View File

@ -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', () => {

View File

@ -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(''),
);

View File

@ -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,

View File

@ -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