From 0082db07fe9441e14df23a27aeabf394daa2ab6d Mon Sep 17 00:00:00 2001 From: zhf <1204297681@qq.com> Date: Fri, 29 Aug 2025 20:45:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=A1=A5=E5=85=85=E5=AE=9E=E4=BD=93?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E8=A1=A8=E6=A0=BC=E6=8F=92=E4=BB=B6=E3=80=81?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=8F=92=E4=BB=B6=E4=BE=9D=E8=B5=96=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/ac-item-plugin/package.json | 2 +- packages/control-plugin/package.json | 2 +- packages/counter-plugin/package.json | 2 +- packages/de-action-plugin/package.json | 2 +- packages/editor-plugin/package.json | 2 +- packages/entity-field-grid/CHANGELOG.md | 3 + packages/entity-field-grid/README.md | 75 + packages/entity-field-grid/package.json | 103 ++ packages/entity-field-grid/scripts/link.sh | 6 + .../src/entity-field-grid.controller.ts | 52 + .../src/entity-field-grid.provider.ts | 20 + .../src/entity-field-grid.scss | 3 + .../src/entity-field-grid.tsx | 809 +++++++++++ packages/entity-field-grid/src/index.ts | 16 + .../src/utils/grid-control.util.ts | 1278 +++++++++++++++++ .../src/utils/use-pagination.ts | 70 + .../src/utils/use-row-edit-popover.tsx | 101 ++ packages/entity-field-grid/tsconfig.json | 19 + packages/entity-field-grid/tsconfig.node.json | 9 + packages/entity-field-grid/vite.config.ts | 57 + .../form-user-control-plugin/package.json | 2 +- packages/global-plugin/package.json | 2 +- packages/grid-column-plugin/package.json | 2 +- packages/panel-item-plugin/package.json | 2 +- packages/portlet-plugin/package.json | 2 +- packages/replace-default-demo/package.json | 2 +- packages/theme-plugin/package.json | 2 +- packages/toolbar-item-plugin/package.json | 2 +- packages/ui-action-plugin/package.json | 2 +- packages/ui-logic-node-plugin/package.json | 2 +- packages/view-plugin/package.json | 2 +- 31 files changed, 2637 insertions(+), 16 deletions(-) create mode 100644 packages/entity-field-grid/CHANGELOG.md create mode 100644 packages/entity-field-grid/README.md create mode 100644 packages/entity-field-grid/package.json create mode 100644 packages/entity-field-grid/scripts/link.sh create mode 100644 packages/entity-field-grid/src/entity-field-grid.controller.ts create mode 100644 packages/entity-field-grid/src/entity-field-grid.provider.ts create mode 100644 packages/entity-field-grid/src/entity-field-grid.scss create mode 100644 packages/entity-field-grid/src/entity-field-grid.tsx create mode 100644 packages/entity-field-grid/src/index.ts create mode 100644 packages/entity-field-grid/src/utils/grid-control.util.ts create mode 100644 packages/entity-field-grid/src/utils/use-pagination.ts create mode 100644 packages/entity-field-grid/src/utils/use-row-edit-popover.tsx create mode 100644 packages/entity-field-grid/tsconfig.json create mode 100644 packages/entity-field-grid/tsconfig.node.json create mode 100644 packages/entity-field-grid/vite.config.ts diff --git a/packages/ac-item-plugin/package.json b/packages/ac-item-plugin/package.json index e88f816..c5f3b58 100644 --- a/packages/ac-item-plugin/package.json +++ b/packages/ac-item-plugin/package.json @@ -51,7 +51,7 @@ "eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-vue": "^9.18.1", "husky": "^8.0.3", - "lerna": "^8.0.0", + "lerna": "^7.4.2", "lint-staged": "^15.1.0", "lodash-es": "^4.17.21", "pluralize": "^8.0.0", diff --git a/packages/control-plugin/package.json b/packages/control-plugin/package.json index 4712108..9554724 100644 --- a/packages/control-plugin/package.json +++ b/packages/control-plugin/package.json @@ -51,7 +51,7 @@ "eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-vue": "^9.18.1", "husky": "^8.0.3", - "lerna": "^8.0.0", + "lerna": "^7.4.2", "lint-staged": "^15.1.0", "lodash-es": "^4.17.21", "pluralize": "^8.0.0", diff --git a/packages/counter-plugin/package.json b/packages/counter-plugin/package.json index e805c7b..01886a7 100644 --- a/packages/counter-plugin/package.json +++ b/packages/counter-plugin/package.json @@ -48,7 +48,7 @@ "eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-vue": "^9.18.1", "husky": "^8.0.3", - "lerna": "^8.0.0", + "lerna": "^7.4.2", "lint-staged": "^15.1.0", "lodash-es": "^4.17.21", "pluralize": "^8.0.0", diff --git a/packages/de-action-plugin/package.json b/packages/de-action-plugin/package.json index e09e2c9..75b89e4 100644 --- a/packages/de-action-plugin/package.json +++ b/packages/de-action-plugin/package.json @@ -48,7 +48,7 @@ "eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-vue": "^9.18.1", "husky": "^8.0.3", - "lerna": "^8.0.0", + "lerna": "^7.4.2", "lint-staged": "^15.1.0", "lodash-es": "^4.17.21", "pluralize": "^8.0.0", diff --git a/packages/editor-plugin/package.json b/packages/editor-plugin/package.json index a6e0b04..3c7406e 100644 --- a/packages/editor-plugin/package.json +++ b/packages/editor-plugin/package.json @@ -51,7 +51,7 @@ "eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-vue": "^9.18.1", "husky": "^8.0.3", - "lerna": "^8.0.0", + "lerna": "^7.4.2", "lint-staged": "^15.1.0", "lodash-es": "^4.17.21", "pluralize": "^8.0.0", diff --git a/packages/entity-field-grid/CHANGELOG.md b/packages/entity-field-grid/CHANGELOG.md new file mode 100644 index 0000000..8ac8e7f --- /dev/null +++ b/packages/entity-field-grid/CHANGELOG.md @@ -0,0 +1,3 @@ +# 版本变更日志 + +添加变更日志 \ No newline at end of file diff --git a/packages/entity-field-grid/README.md b/packages/entity-field-grid/README.md new file mode 100644 index 0000000..a68757c --- /dev/null +++ b/packages/entity-field-grid/README.md @@ -0,0 +1,75 @@ +# 表格插件 + +表格插件可自定义表格内容的绘制,当标准逻辑无法满足表格绘制要求时,可以通过表格插件来实现。modeling建模平台创建表格插件可参见 [插件开发](https://open.ibizlab.cn/apphub/zh/guide/plugin-dev.html) + +## 插件结构 + +``` +|─ ─ entity-field-grid 表格插件顶层目录 + |─ ─ src 表格插件源代码目录 + |─ ─ utils 表格插件工具目录 + |─ ─ grid-control.util.ts 表格插件工具 + |─ ─ use-pagination.ts 表格插件分页工具 + |─ ─ use-row-edit-popover.tsx 表格插件行编辑弹窗工具 +​ |─ ─ entity-field-grid.controller.ts 表格插件控制器 +​ |─ ─ entity-field-grid.provider.ts 表格插件适配器 +​ |─ ─ entity-field-grid.scss 表格插件样式 +​ |─ ─ entity-field-grid.tsx 表格插件组件 +​ |─ ─ index.ts 表格插件入口文件 +``` + +- 表格插件入口文件 + +表格插件入口文件会全局注册表格插件适配器和表格插件组件,供外部使用。 + +- 表格插件组件 + +表格插件组件使用tsx的书写方式,承载表格绘制的内容。 + +- 表格插件适配器 + +表格插件适配器主要通过component属性指定表格实际要绘制的组件,并且通过createController方法返回传递给表格的控制器。 + +- 表格插件控制器 + +表格插件控制器承载表格的逻辑控制。 + +## 本地开发 + +1. 安装依赖并link至全局 + +```sh +// 安装依赖 +pnpm i + +// link底包 +./scripts/link.sh + +// 启动 +pnpm dev + +// link到全局 +pnpm link --global +``` + +2. 主项目包中引用插件 + +```sh +// link插件 +pnpm link --global '@ibiz-plugin-example/entity-field-grid' +``` + +3. 主项目包中注册插件 + +```ts +import { App } from 'vue'; +import EntityFieldGrid from '@ibiz-plugin-example/entity-field-grid'; + +export default { + install(app: App): void { + app.use(EntityFieldGrid); + // 设置本地开发需要忽略加载的插件,可填写正则或全匹配字符串。匹配插件在modeling建模平台配置的[运行时插件仓库配置]内容 + ibiz.plugin.setDevIgnore(/^@ibiz-plugin-example\/entity-field-grid/); + }, +}; +``` \ No newline at end of file diff --git a/packages/entity-field-grid/package.json b/packages/entity-field-grid/package.json new file mode 100644 index 0000000..7fbbcc3 --- /dev/null +++ b/packages/entity-field-grid/package.json @@ -0,0 +1,103 @@ +{ + "name": "@ibiz-plugin-example/entity-field-grid", + "version": "0.0.1", + "description": "实体属性表格", + "author": "iBiz", + "license": "MIT", + "type": "module", + "main": "dist/index.es.js", + "module": "dist/index.es.js", + "types": "dist/types/index.d.ts", + "system": "dist/index.legacy.js", + "styles": [ + "dist/style.css" + ], + "files": [ + "dist", + "public", + "CHANGELOG.md", + "README.md" + ], + "scripts": { + "dev": "vite build --watch", + "build": "vue-tsc --noEmit && vite build", + "preview": "vite preview" + }, + "devDependencies": { + "@floating-ui/dom": "^1.5.3", + "@ibiz-template/core": "^0.7.40-alpha.7", + "@ibiz-template/model-helper": "^0.7.40-alpha.8", + "@ibiz-template/runtime": "^0.7.40-alpha.8", + "@ibiz-template/theme": "^0.7.39", + "@ibiz-template/vue3-util": "^0.7.40-alpha.8", + "@ibiz/model-core": "^0.1.72", + "@qx-chitanda/vite-plugin-lib-legacy": "^4.1.1", + "@types/lodash-es": "^4.17.12", + "@types/ramda": "^0.29.6", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "@typescript-eslint/parser": "^6.11.0", + "@vitejs/plugin-vue": "^4.4.1", + "@vitejs/plugin-vue-jsx": "^3.0.2", + "async-validator": "^4.2.5", + "axios": "^1.6.2", + "dayjs": "^1.11.10", + "element-plus": "^2.4.2", + "eslint": "^8.53.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-import": "^2.29.0", + "eslint-plugin-prettier": "^5.0.1", + "eslint-plugin-unused-imports": "^3.0.0", + "eslint-plugin-vue": "^9.18.1", + "husky": "^8.0.3", + "lerna": "^7.4.2", + "lint-staged": "^15.1.0", + "lodash-es": "^4.17.21", + "pluralize": "^8.0.0", + "postcss": "^8.4.31", + "prettier": "^3.1.0", + "qs": "^6.11.2", + "qx-util": "^0.4.8", + "ramda": "^0.29.1", + "rollup-plugin-visualizer": "^5.9.2", + "sass": "1.69.5", + "stylelint": "15.11.0", + "stylelint-config-prettier": "^9.0.5", + "stylelint-config-recess-order": "^4.3.0", + "stylelint-config-standard": "^34.0.0", + "stylelint-config-standard-scss": "^11.1.0", + "stylelint-scss": "5.3.1", + "terser": "^5.24.0", + "typescript": "5.2.2", + "vite": "^4.5.0", + "vite-plugin-dts": "^3.6.3", + "vite-plugin-eslint": "^1.8.1", + "vite-plugin-libcss": "^1.1.1", + "vue": "^3.3.8", + "vue-eslint-parser": "^9.3.2", + "vue-router": "^4.2.5", + "vue-tsc": "1.8.22", + "vuedraggable": "^4.1.0" + }, + "peerDependencies": { + "@floating-ui/dom": "^1.5.1", + "@ibiz-template/core": "^0.7.40-alpha.7", + "@ibiz-template/model-helper": "^0.7.40-alpha.8", + "@ibiz-template/runtime": "^0.7.40-alpha.8", + "@ibiz-template/theme": "^0.7.39", + "@ibiz-template/vue3-util": "^0.7.40-alpha.8", + "@ibiz/model-core": "^0.1.72", + "async-validator": "^4.2.5", + "axios": "^1.6.2", + "dayjs": "^1.11.10", + "element-plus": "^2.4.2", + "lodash-es": "^4.17.21", + "pluralize": "^8.0.0", + "qs": "^6.11.2", + "qx-util": "^0.4.8", + "ramda": "^0.29.1", + "vue": "^3.3.8", + "vue-router": "^4.2.5", + "vuedraggable": "^4.1.0" + } +} diff --git a/packages/entity-field-grid/scripts/link.sh b/packages/entity-field-grid/scripts/link.sh new file mode 100644 index 0000000..d4edc15 --- /dev/null +++ b/packages/entity-field-grid/scripts/link.sh @@ -0,0 +1,6 @@ +pnpm link --global "@ibiz-template/vue3-util" +pnpm link --global "@ibiz-template/model-helper" +pnpm link --global "@ibiz-template/runtime" +pnpm link --global "@ibiz-template/core" +pnpm link --global "@ibiz-template/theme" +pnpm link --global "@ibiz-template/vue3-components" diff --git a/packages/entity-field-grid/src/entity-field-grid.controller.ts b/packages/entity-field-grid/src/entity-field-grid.controller.ts new file mode 100644 index 0000000..3fca891 --- /dev/null +++ b/packages/entity-field-grid/src/entity-field-grid.controller.ts @@ -0,0 +1,52 @@ +import { GridController } from '@ibiz-template/runtime'; +import { clone } from 'ramda'; + +export class EntityFieldGridController extends GridController { + protected async onCreated(): Promise { + if (this.model.appDataEntityId) { + (this as IData).model = clone(this.model); + const entity = await ibiz.hub.getAppDataEntity( + this.model.appDataEntityId!, + this.model.appId, + ); + if (entity) { + const degridColumns = entity.appDEFields?.map(field => { + return { + dataItemName: field.id, + appDEFieldId: field.id, + valueType: 'SIMPLE', + align: 'LEFT', + capLanguageRes: field.lnlanguageRes, + caption: field.logicName, + codeName: field.id, + columnType: 'DEFGRIDCOLUMN', + width: 200, + widthUnit: 'PX', + enableSort: true, + id: field.id, + appId: field.appId, + }; + }); + const degridDataItems = entity.appDEFields?.map(field => { + return { + appDEFieldId: field.id, + valueType: 'SIMPLE', + dataType: field.stdDataType, + id: field.id, + appId: field.appId, + }; + }); + this.model.degridColumns = degridColumns; + this.model.degridDataItems = degridDataItems; + if (this.controlParams.showcolumns) { + const showColumns = JSON.parse(this.controlParams.showcolumns); + const set = new Set(showColumns); + this.model.degridColumns = this.model.degridColumns?.filter(column => + set.has(column.id), + ); + } + } + } + return super.onCreated(); + } +} diff --git a/packages/entity-field-grid/src/entity-field-grid.provider.ts b/packages/entity-field-grid/src/entity-field-grid.provider.ts new file mode 100644 index 0000000..d03713d --- /dev/null +++ b/packages/entity-field-grid/src/entity-field-grid.provider.ts @@ -0,0 +1,20 @@ +import { + CTX, + IControlController, + IControlProvider, +} from '@ibiz-template/runtime'; +import { IControl } from '@ibiz/model-core'; +import { EntityFieldGridController } from './entity-field-grid.controller'; + +export class EntityFieldGridProvider implements IControlProvider { + component: string = 'IBizEntityFieldGrid'; + + createController( + model: IControl, + context: IContext, + params: IParams, + ctx: CTX, + ): IControlController { + return new EntityFieldGridController(model, context, params, ctx); + } +} diff --git a/packages/entity-field-grid/src/entity-field-grid.scss b/packages/entity-field-grid/src/entity-field-grid.scss new file mode 100644 index 0000000..6636b3f --- /dev/null +++ b/packages/entity-field-grid/src/entity-field-grid.scss @@ -0,0 +1,3 @@ +@include b(entity-field-grid) { + // 样式 +} \ No newline at end of file diff --git a/packages/entity-field-grid/src/entity-field-grid.tsx b/packages/entity-field-grid/src/entity-field-grid.tsx new file mode 100644 index 0000000..3f52c9e --- /dev/null +++ b/packages/entity-field-grid/src/entity-field-grid.tsx @@ -0,0 +1,809 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { + useNamespace, + IBizCustomRender, + useControlController, + hasEmptyPanelRenderer, + useControlPopoverzIndex, +} from '@ibiz-template/vue3-util'; +import { + h, + ref, + watch, + VNode, + PropType, + computed, + renderSlot, + onUnmounted, + defineComponent, + resolveComponent, + VNodeArrayChildren, +} from 'vue'; +import { + IDEGrid, + IDEGridColumn, + IDEGridGroupColumn, + IUIActionGroupDetail, +} from '@ibiz/model-core'; +import { + GridRowState, + ScriptFactory, + GridController, + IControlProvider, + GridFieldColumnController, +} from '@ibiz-template/runtime'; +import { NOOP, showTitle } from '@ibiz-template/core'; +import { createUUID } from 'qx-util'; +import { isNotNil } from 'ramda'; +import { + IGridProps, + useAppGridBase, + useITableEvent, + useGridDraggable, + useGridHeaderStyle, +} from './utils/grid-control.util'; +import { useRowEditPopover } from './utils/use-row-edit-popover'; +import { usePagination } from './utils/use-pagination'; +import { EntityFieldGridController } from './entity-field-grid.controller'; +import './entity-field-grid.scss'; + +/** + * 绘制成员的attrs + * @author lxm + * @date 2024-03-19 03:48:00 + * @param {IDEFormDetail} model + * @return {*} {IParams} + */ +function renderAttrs(model: IDEGridColumn | IDEGrid, params: IParams): IParams { + const attrs: IParams = {}; + model.controlAttributes?.forEach(item => { + // 表格合并行列应该排除由表格内置合并单元格方法处理 + if (item.attrName !== 'span-method') { + if (item.attrName && item.attrValue) { + attrs[item.attrName!] = ScriptFactory.execSingleLine(item.attrValue!, { + ...params, + }); + } + } + }); + return attrs; +} + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export function renderFieldColumn( + c: GridFieldColumnController, + row: GridRowState, +) { + const ns = useNamespace('grid-field-column'); + const fieldValue = row.data[c.fieldName]; + let actionToolbar; + if (c.model.deuiactionGroup) { + const onActionClick = ( + detail: IUIActionGroupDetail, + event: MouseEvent, + ): Promise => { + return c.onActionClick(detail, row, event); + }; + const zIndex = c.grid.state.zIndex; + actionToolbar = ( + + ); + } else { + actionToolbar = null; + } + + let content = null; + const { controlRenders = [] } = c.model; + const panel = controlRenders.find( + renderItem => renderItem.renderType === 'LAYOUTPANEL', + )?.layoutPanel; + const columnType = c.model.userParam?.columntype; + if (panel) { + content = ( + + ); + // 特殊处理附件列 + } else if (columnType === 'attachment') { + content = ( + + ); + } else { + const showValue = c.formatValue(fieldValue); + const tooltip = computed(() => { + if ( + c.grid.overflowMode === 'ellipsis' && + isNotNil(fieldValue) && + fieldValue !== '' + ) { + return showValue + (c.model.unitName || ''); + } + return undefined; + }); + const hiddenEmpty = computed(() => { + if (fieldValue) { + if (c.grid.emptyHiddenUnit) { + if (showValue) { + return true; + } + return false; + } + return true; + } + return false; + }); + const percent = computed(() => { + const { grid, fieldName } = c; + if (!grid.percentkeys.includes(fieldName)) { + return ''; + } + const percentValue = Number(fieldValue); + if (!Number.isNaN(percentValue)) { + const { totalResult = {} } = grid.state; + const total = totalResult[fieldName]; + if (total && !Number.isNaN(total)) { + return ibiz.util.text.format(`${percentValue / total}`, '0.##%'); + } + } + return ''; + }); + const onTextClick = (event: MouseEvent): void => { + // 阻止触发行点击 + if (c.isLinkColumn) { + event.stopPropagation(); + c.openLinkView(row, event); + } + }; + content = ( + + {showValue} + {hiddenEmpty.value && c.model.unitName} + {percent.value && `(${percent.value})`} + + ); + } + const onCellClick = (event: MouseEvent): void => { + if (c.hasAction) { + event.stopPropagation(); + // 阻止触发行点击 + c.triggerAction(row, event); + } + }; + + return ( +
+ {c.model.deuiactionGroup + ? [ +
{content}
, +
{actionToolbar}
, + ] + : content} +
+ ); +} + +// 绘制除分组列之外的表格列 +export function renderColumn( + c: GridController, + model: IDEGridColumn, + renderColumns: IDEGridColumn[], + index: number, +): VNode | null { + const { codeName: columnName, width } = model; + + // 查缓存,有缓存用缓存,没缓存的用模型 + const columnC = c.columns[columnName!]; + const columnState = c.state.columnStates.find( + item => item.key === columnName, + )!; + + // 如果没有配置自适应列,则最后一列变为自适应列 + const widthFlexGrow = + columnC.isAdaptiveColumn || + (!c.hasAdaptiveColumn && index === renderColumns.length - 1); + + const widthName = widthFlexGrow ? 'min-width' : 'width'; + + const tempWidth = columnState?.columnWidth || width; + // 表格列自定义 + return ( + { + const fieldName = model.id!.toLowerCase(); + if (a[fieldName] < b[fieldName] || !a[fieldName]) return -1; + if (a[fieldName] > b[fieldName] || !b[fieldName]) return 1; + return 0; + }} + align={model.align?.toLowerCase() || 'center'} + > + {{ + header: ({ column }: IData) => { + return ( + + ); + }, + default: ({ row }: IData): VNode | null => { + let elRow = row; // element表格数据 + if (elRow.isGroupRow) + return ( +
+ {row.caption} +
+ ); + if (row.isGroupData) elRow = row.first; + const rowState = c.findRowState(elRow); + if (rowState) { + // 常规非业务单元格由表格绘制(性能优化) + if ( + model.columnType === 'DEFGRIDCOLUMN' || + model.columnType === 'DEFTREEGRIDCOLUMN' + ) { + if ( + c.providers[columnName!].component === 'IBizGridFieldColumn' && + !columnC.isCustomCode && + !(columnC as GridFieldColumnController).codeList + ) { + return renderFieldColumn( + columnC as GridFieldColumnController, + rowState, + ); + } + } + const comp = resolveComponent(c.providers[columnName!].component); + return h(comp, { + controller: columnC, + row: rowState, + key: elRow.tempsrfkey + columnName, + attrs: renderAttrs(model, { + ...c.getEventArgs(), + data: rowState.data, + }), + }); + } + return null; + }, + }} +
+ ); +} + +// 绘制表格列 +export function renderChildColumn( + c: GridController, + model: IDEGridColumn, + renderColumns: IDEGridColumn[], + index: number, +): VNode | null { + if (model.columnType === 'GROUPGRIDCOLUMN') { + const childColumns = + (model as IDEGridGroupColumn).degridColumns?.filter( + item => !item.hideDefault && !item.hiddenDataItem, + ) || []; + const { width, codeName } = model; + const columnC = c.columns[codeName!]; + const align = model.align?.toLowerCase() || 'center'; + return ( + + {{ + header: ({ column }: IData) => { + return ( + + ); + }, + default: (): VNodeArrayChildren => { + return childColumns.map((column, index2) => { + return renderChildColumn(c, column, renderColumns, index2); + }); + }, + }} + + ); + } + return renderColumn(c, model, renderColumns, index); +} + +export const EntityFieldGrid = defineComponent({ + name: 'IBizEntityFieldGrid', + props: { + /** + * @description 表格模型数据 + */ + modelData: { type: Object as PropType, required: true }, + /** + * @description 应用上下文对象 + */ + context: { type: Object as PropType, required: true }, + /** + * @description 视图参数对象 + * @default {} + */ + params: { type: Object as PropType, default: () => ({}) }, + /** + * @description 部件适配器 + */ + provider: { type: Object as PropType }, + /** + * @description 部件行数据默认激活模式,值为0:不激活,值为1:单击激活,值为2:双击激活 + */ + mdctrlActiveMode: { type: Number, default: undefined }, + /** + * @description 是否单选 + */ + singleSelect: { type: Boolean, default: undefined }, + /** + * @description 是否启用行编辑 + */ + rowEditOpen: { type: Boolean, default: undefined }, + /** + * @description 是否是简单模式,即直接传入数据,不加载数据 + */ + isSimple: { type: Boolean, required: false }, + /** + * @description 简单模式下传入的数据 + */ + data: { type: Array, required: false }, + /** + * @description 是否默认加载数据 + * @default true + */ + loadDefault: { type: Boolean, default: true }, + }, + setup(props, { slots }) { + const c: EntityFieldGridController = + useControlController( + (...args) => new EntityFieldGridController(...args), + ); + const ns = useNamespace(`control-${c.model.controlType!.toLowerCase()}`); + const ns2 = useNamespace('entity-field-grid'); + + useControlPopoverzIndex(c); + + const { sysCss } = c.model; + const sysCssName = sysCss?.cssName; + + const { + tableRef, + onRowClick, + onDbRowClick, + onSelectionChange, + onSortChange, + handleRowClassName, + handleHeaderCellClassName, + cleanClick = NOOP, + cleanEnter = NOOP, + cleanTab = NOOP, + } = useITableEvent(c); + const { onPageChange, onPageRefresh, onPageSizeChange } = usePagination(c); + + const { headerCssVars } = useGridHeaderStyle(tableRef, ns); + const { cleanup = NOOP, setDragEvent } = useGridDraggable(tableRef, ns, c); + + c.evt.on('onLoadSuccess', () => { + if (setDragEvent) { + setDragEvent(); + } + }); + + const renderNoData = (): VNode | null => { + // 未加载不显示无数据 + const { isLoaded } = c.state; + if (isLoaded) { + const quickToolbar = c.model.controls?.find( + item => item.name === `${c.model.name}_quicktoolbar`, + ); + if (quickToolbar) { + return ( + + ); + } + const noDataSlots: IParams = {}; + if (hasEmptyPanelRenderer(c)) { + Object.assign(noDataSlots, { + customRender: () => ( + + ), + }); + } + return ( + + {noDataSlots} + + ); + } + // 给null 表格会绘制默认的无数据 + return
; + }; + const { + tableData, + renderColumns, + defaultSort, + summaryMethod, + spanMethod, + headerDragend, + } = useAppGridBase(c, props as IGridProps, tableRef); + + const { renderPopover } = useRowEditPopover(tableRef, c); + + // 绘制批操作工具栏 + const renderBatchToolBar = (): VNode | undefined => { + const batchToolbar = c.model.controls?.find(item => { + return item.name === `${c.model.name!}_batchtoolbar`; + }); + if (!batchToolbar || c.state.singleSelect) { + return; + } + return ( +
0), + ]} + > +
+
+ {ibiz.i18n.t('control.common.itemsSelected', { + length: c.state.selectedData.length, + })} +
+
|
+ +
+
+ ); + }; + + // 绘制表格列 + const renderTableColumn = ( + model: IDEGridColumn, + index: number, + ): VNode | null => { + if (slots[model.id!]) { + return renderSlot(slots, model.id!, { + model, + data: c.state.items, + }); + } + return renderChildColumn(c, model, renderColumns.value, index); + }; + + // 绘制拖动图标列 + const renderDragIconColumn = () => { + return ( + + {{ + default: () => { + return ( + + + + + + + + ); + }, + }} + + ); + }; + + /** + * 绘制行明细 + * + * @return {*} + */ + const renderRowDetail = () => { + const { navAppViewId, navViewHeight } = c.model; + if (navAppViewId && c.state.showRowDetail) + return ( + + {{ + default: ({ row }: IData): VNode | null => { + const { context, params } = c.calcNavParams(row); + const style = { + height: navViewHeight ? `${navViewHeight}px` : 'auto', + }; + return h(resolveComponent('IBizViewShell'), { + style, + params, + context, + viewId: navAppViewId, + class: ns.b('row-detail-view'), + }); + }, + }} + + ); + }; + + onUnmounted(() => { + if (cleanup !== NOOP) cleanup(); + if (cleanClick !== NOOP) cleanClick(); + if (cleanEnter !== NOOP) cleanEnter(); + if (cleanTab !== NOOP) cleanTab(); + }); + + // 是否可以加载更多 + const isLodeMoreDisabled = computed(() => { + if (c.model.pagingMode !== 2) { + return true; + } + return ( + c.state.items.length >= c.state.total || + c.state.isLoading || + c.state.total <= c.state.size + ); + }); + + // 无限滚动元素 + const infiniteScroll = ref(); + // 无限滚动元素标识 + const infiniteScrollKey = ref(createUUID()); + + watch( + () => c.state.curPage, + () => { + if ( + c.state.curPage === 1 && + (c.model.pagingMode === 2 || c.model.pagingMode === 3) + ) { + infiniteScrollKey.value = createUUID(); + const containerEl = + infiniteScroll.value?.ElInfiniteScroll?.containerEl; + if (containerEl) { + containerEl.lastScrollTop = 0; + containerEl.scrollTop = 0; + } + } + }, + ); + + return { + c, + ns, + ns2, + tableRef, + tableData, + sysCssName, + defaultSort, + renderColumns, + headerCssVars, + infiniteScroll, + infiniteScrollKey, + isLodeMoreDisabled, + onRowClick, + spanMethod, + onSortChange, + onDbRowClick, + onPageChange, + renderNoData, + summaryMethod, + headerDragend, + renderPopover, + onPageRefresh, + renderRowDetail, + onPageSizeChange, + renderTableColumn, + onSelectionChange, + handleRowClassName, + renderBatchToolBar, + renderDragIconColumn, + handleHeaderCellClassName, + }; + }, + render() { + if (!this.c.state.isCreated) { + return; + } + const state = this.c.state; + const defaultExpandAll = this.c.controlParams.defaultexpandall === 'true'; + + return ( + + + { + + this.c.expandChange(row, expanded) + } + {...this.$attrs} + {...renderAttrs(this.c.model, { + ...this.c.getEventArgs(), + })} + > + {{ + empty: this.renderNoData, + default: (): VNodeArrayChildren => { + return [ + this.c.enableRowEditOrder && this.renderDragIconColumn(), + !state.singleSelect && ( + + ), + this.renderRowDetail(), + this.renderColumns.map((model, index) => { + return this.renderTableColumn(model, index); + }), + ]; + }, + append: () => { + return [ +
this.c.loadMore()} + infinite-scroll-distance={10} + infinite-scroll-disabled={this.isLodeMoreDisabled} + >
, + this.c.model.pagingMode === 3 && + !( + this.c.state.items.length >= this.c.state.total || + this.c.state.isLoading || + this.c.state.total <= this.c.state.size + ) && ( +
+ this.c.loadMore()}> + {ibiz.i18n.t('control.common.loadMore')} + +
+ ), + this.c.state.isAutoGrid ? ( + this.c.newRow()} + > + + {ibiz.i18n.t('app.add')} + + ) : ( + this.renderPopover() + ), + ]; + }, + }} +
+ } + {this.c.state.enablePagingBar && ( + + )} + {this.c.model.enableCustomized && !this.c.state.hideHeader && ( +
+ +
+ )} + {this.renderBatchToolBar()} +
+
+ ); + }, +}); diff --git a/packages/entity-field-grid/src/index.ts b/packages/entity-field-grid/src/index.ts new file mode 100644 index 0000000..0b8057c --- /dev/null +++ b/packages/entity-field-grid/src/index.ts @@ -0,0 +1,16 @@ +import { App } from 'vue'; +import { registerControlProvider } from '@ibiz-template/runtime'; +import { EntityFieldGrid } from './entity-field-grid'; +import { EntityFieldGridProvider } from './entity-field-grid.provider'; + +export default { + install(app: App): void { + // 全局注册表格插件组件 + app.component(EntityFieldGrid.name!, EntityFieldGrid); + // 全局注册表格插件适配器,GRID_RENDER是插件类型,ENTITY_FIELD_GRID是插件标识 + registerControlProvider( + 'GRID_RENDER_ENTITY_FIELD_GRID', + () => new EntityFieldGridProvider(), + ); + }, +}; diff --git a/packages/entity-field-grid/src/utils/grid-control.util.ts b/packages/entity-field-grid/src/utils/grid-control.util.ts new file mode 100644 index 0000000..4d2fe26 --- /dev/null +++ b/packages/entity-field-grid/src/utils/grid-control.util.ts @@ -0,0 +1,1278 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable no-restricted-syntax */ +/* eslint-disable no-param-reassign */ +import { + NOOP, + Namespace, + eventPath, + listenJSEvent, + recursiveIterate, +} from '@ibiz-template/core'; +import { + Srfuf, + ControlVO, + GridRowState, + IColumnState, + IGridRowState, + ScriptFactory, + GridController, + IControlProvider, +} from '@ibiz-template/runtime'; +import { + IDEGrid, + IDEGridColumn, + IDEGridFieldColumn, + IDEGridGroupColumn, +} from '@ibiz/model-core'; +import { TableColumnCtx } from 'element-plus'; +import { chunk, orderBy } from 'lodash-es'; +import { + Ref, + ref, + watch, + computed, + nextTick, + onUnmounted, + watchEffect, +} from 'vue'; + +/** + * 通过回车聚焦元素 + * + * @export + * @param {(HTMLElement | Document)} [target=document] 聚焦元素范围 默认document + * @param {string[]} [querySelects=[ + * 'a', + * 'input', + * 'button', + * 'textarea', + * 'select', + * '[tabindex]:not([tabindex="-1"])', + * ]] 可聚焦元素选择器 + * @param {() => void} [callback] 最后一个可聚焦元素回车事件回调 + * @return {*} {{ + * cleanup: () => void; + * }} + */ +function useFocusByEnter( + target: HTMLElement | Document = document, + querySelects: string[] = [ + 'a', + 'input', + 'button', + 'textarea', + 'select', + '[tabindex]:not([tabindex="-1"])', + ], + callback?: () => void, +): { + cleanup: () => void; +} { + const querySelector = querySelects.join(','); + const listener = (event: KeyboardEvent): void => { + if (event.key === 'Enter') { + // 阻止默认行为 + event.preventDefault(); + const currentElement = document.activeElement as HTMLElement; + // 获取所有可聚焦元素 + const focusableElements = Array.from( + target.querySelectorAll(querySelector), + ) as HTMLElement[]; + const currentIndex = focusableElements.indexOf(currentElement); + const nextElement = focusableElements[currentIndex + 1]; + if (nextElement) { + // 聚焦到下一个元素 + nextElement.focus(); + } else { + callback?.(); + } + } + }; + const cleanup = listenJSEvent(window, 'keydown', listener, { + capture: true, + }); + return { cleanup }; +} + +/** + * 表格部件props接口 + * + * @author zk + * @date 2023-09-26 06:09:51 + * @export + * @interface IGridProps + */ +export interface IGridProps { + // 模型 + modelData: IDEGrid; + // 上下文 + context: IContext; + // 视图参数 + params: IParams; + // 适配器 + provider: IControlProvider; + // 部件行数据默认激活模式 + mdctrlActiveMode?: number; + // 是否单选 + singleSelect?: boolean; + // 是否开启行编辑 + rowEditOpen?: boolean; + // 是否是本地数据模式 + isSimple: boolean; + // 本地数据模式data + data: Array; + // 默认加载 + loadDefault: boolean; +} + +/** + * 排序合并数据 + * + * @param {string[]} rowSpanKeys + * @param {IData[]} items + * @return {*} + */ +function sortMergeData(rowSpanKeys: string[], items: IData[]) { + const otherItems: IData[] = []; + const firstKey: string = rowSpanKeys[0] || ''; + const sortedItems = items.filter((item: IData) => { + if (!item[firstKey]) { + otherItems.push(item); + } + return item[firstKey]; + }); + sortedItems.sort((a: IData, b: IData) => { + for (const key of rowSpanKeys) { + if (a[key] !== b[key]) { + return a[key] > b[key] ? 1 : -1; + } + } + return 0; + }); + sortedItems.push(...otherItems); + return sortedItems; +} + +/** + * 适配element的table的事件 + * + * @author lxm + * @date 2022-09-05 21:09:42 + * @export + * @param {GridController} c + * @returns {*} + */ +export function useITableEvent(c: GridController): { + tableRef: Ref; + onRowClick: ( + data: ControlVO, + _column: IData, + event: MouseEvent, + ) => Promise; + onDbRowClick: (data: ControlVO) => void; + onSelectionChange: (selection: ControlVO[]) => void; + onSortChange: (opts: { + _column: IData; + prop: string; + order: 'ascending' | 'descending'; + }) => void; + handleRowClassName: ({ row }: { row: IData }) => string; + handleHeaderCellClassName: ({ + _row, + column, + _rowIndex, + _columnIndex, + }: { + _row: IData; + column: IData; + _rowIndex: number; + _columnIndex: number; + }) => string; + cleanClick: () => void; + cleanEnter: () => void; + cleanTab: () => void; +} { + const tableRef = ref(); + let forbidChange = false; + + // 是否正在设置elementPlus表格排序回显效果 + let isGridUISort = false; + + let cleanClick = NOOP; + let cleanEnter = NOOP; + let cleanTab = NOOP; + + if (c.state.isAutoGrid) { + // 行编辑模式才监听 + if (c.editShowMode === 'row') { + cleanClick = listenJSEvent(window, 'click', async event => { + const classList: string[] = []; + eventPath(event).forEach(e => { + if (e && (e as IData).classList) { + classList.push(...(e as IData).classList); + } + }); + // 排除 popper,行,滚动条点击的情况 + if ( + classList.includes('el-popper') || + classList.includes('el-scrollbar') || + classList.includes('el-table__row') + ) + return; + const editingRow = c.state.rows.find(item => item.showRowEdit); + if (editingRow) await c.switchRowEdit(editingRow); + }); + } + + const timer = setInterval(() => { + const tableEl = tableRef.value?.$el; + if (tableEl) { + clearInterval(timer); + const querySelect: string[] = [ + 'tbody select', + 'tbody textarea', + 'tbody input:not([type="checkbox"])', + ]; + const callback = async () => { + if (c.editShowMode === 'row') { + const editingRow = c.state.rows.find(item => item.showRowEdit); + if (editingRow) await c.switchRowEdit(editingRow); + } + }; + const { cleanup } = useFocusByEnter(tableEl, querySelect, callback); + cleanEnter = cleanup; + } + }, 300); + } + + // 所有表格列 + const allGridColumns: IDEGridColumn[] = []; + recursiveIterate( + c.model, + (column: IDEGridColumn) => { + allGridColumns.push(column); + }, + { childrenFields: ['degridColumns'] }, + ); + + c.evt.on('onToggleRowExpansion', event => { + const { row, expand } = event as IData; + if (tableRef.value) { + if (tableRef.value.lazy) { + const { store } = tableRef.value; + const { treeData } = tableRef.value.store.states; + const data = treeData.value[row.srfkey]; + if (data && data.expanded !== expand) { + store.loadOrToggle(row); + } + } else { + tableRef.value.toggleRowExpansion(row, expand); + } + } + }); + + // 可编辑属性列 + const editColumn = computed(() => { + const columns: IDEGridFieldColumn[] = []; + recursiveIterate({ children: c.state.columnStates }, columnState => { + if (!columnState.uaColumn && !columnState.hidden) { + const model = c.columns[columnState.key].model; + if (model.enableRowEdit) columns.push(model); + } + }); + return columns; + }); + + const tabListener = (event: KeyboardEvent): void => { + const tableEl = tableRef.value!.$el; + const currentElement = document.activeElement as HTMLElement; + // 焦点在当前表格,表格启用编辑时才启用 + if (event.key === 'Tab' && tableEl.contains(currentElement)) { + // 阻止默认 + event.preventDefault(); + // 单元格编辑模式 + const classList: string[] = []; + eventPath(event as any).forEach(e => { + if (e && (e as IData).classList) { + classList.push(...(e as IData).classList); + } + }); + // 获取主键 + const key = classList + .filter(className => className.startsWith('id-'))[0] + ?.substring(3); + const columnName = classList + .filter(className => className.startsWith('defgridcolumn-'))[0] + ?.substring(14); + const columnIndex = editColumn.value.findIndex( + column => column.id!.toLowerCase() === columnName?.toLowerCase(), + ); + // 不存在主键为新建行 + const rowIndex = c.state.rows.findIndex( + item => key && item.data.srfkey === key, + ); + const row = rowIndex !== -1 ? c.state.rows[rowIndex] : undefined; + // 如果当前行为新建行,则下一行默认为第一个非新建行 + const nextRow = + rowIndex !== -1 + ? c.state.rows[rowIndex + 1] + : c.state.rows.find(_row => _row.data.srfuf === Srfuf.UPDATE); + if (row) { + // 有下一个可编辑单元格或下一行 先关闭之前的编辑单元格 + if (nextRow || columnIndex < editColumn.value.length - 1) { + Object.keys(row.editColStates).forEach(fieldName => { + row.editColStates[fieldName].editable = false; + }); + } + // 打开下一个可编辑单元格 + if (columnIndex < editColumn.value.length - 1) { + row.editColStates[ + editColumn.value[columnIndex + 1].id!.toLowerCase() + ].editable = true; + } else if (nextRow) { + // 如果是这一行最后一个单元格则打开下一行第一个单元格 + nextRow.editColStates[ + editColumn.value[0].id!.toLowerCase() + ].editable = true; + } + } else { + // 新建行直接聚焦元素即可 + // 获取当前表格所有可聚焦元素 + const focusableElements = Array.from( + tableEl.querySelectorAll( + 'tbody select, tbody textarea, tbody input:not([type="checkbox"])', + ), + ) as HTMLElement[]; + const currentIndex = focusableElements.indexOf(currentElement); + // 下一个可聚焦元素 + const nextElement = focusableElements[currentIndex + 1]; + if (nextElement) { + nextElement.focus(); + } else if (nextRow) { + // 没有可聚焦元素时,下一行非新建行的首项启用编辑态 + nextRow.editColStates[ + editColumn.value[0].id!.toLowerCase() + ].editable = true; + } + } + } + }; + + watch( + () => tableRef.value, + table => { + const { enableRowEdit } = c.model; + if (table && enableRowEdit && c.editShowMode === 'cell') { + cleanTab = listenJSEvent(window, 'keydown', tabListener, { + capture: true, + }); + } + }, + ); + + /** + * 处理ctrl+click选中 + * + * @param {ControlVO} data + */ + function handleCtrlSelect(data: ControlVO): void { + const selection = [...c.state.selectedData]; + const index = selection.findIndex(x => x.srfkey === data.srfkey); + if (index !== -1) { + selection.splice(index, 1); + c.setSelection(selection); + } else { + c.setSelection([...selection, data]); + } + } + + // 上一次选中数据索引 + let lastSelectedIndex: number | null = null; + + /** + * 处理shift+click选中 + * + * @param {ControlVO} data + */ + function handleShiftSelect(data: ControlVO): void { + // 清除shift的默认选中样式 + window.getSelection()?.removeAllRanges(); + const index = c.findRowStateIndex(data); + const selection = [...c.state.selectedData]; + const isSelected = selection.includes(data); + if (lastSelectedIndex !== null) { + // Shift+点击,执行范围选择 + const start = Math.min(lastSelectedIndex, index); + const end = Math.max(lastSelectedIndex, index); + + if (isSelected) { + // 如果点击的是已选中项,则取消该范围内的选中 + for (let i = start; i <= end; i++) { + const itemIndex = selection.indexOf(c.state.items[i]); + if (itemIndex !== -1) { + selection.splice(itemIndex, 1); + } + } + } else { + // 否则选中该范围内的所有项 + for (let i = start; i <= end; i++) { + if (!selection.includes(c.state.items[i])) { + selection.push(c.state.items[i]); + } + } + } + c.setSelection(selection); + } else { + c.setSelection([data]); + } + lastSelectedIndex = index; + } + + async function onRowClickDynamic( + data: ControlVO, + _column: IData, + event: MouseEvent, + ): Promise { + // 新建行拦截行点击事件 + if (data.srfuf === Srfuf.CREATE) { + if (c.editShowMode === 'row' && !data.isGroupRow) { + const row = c.findRowState(data); + // 新建行值被修改过就保存,否则取消 + if (row) await c.switchRowEdit(row); + } + return; + } + if (event.ctrlKey && !c.state.singleSelect) return handleCtrlSelect(data); + if (event.shiftKey && !c.state.singleSelect) return handleShiftSelect(data); + // 单行编辑模式下,行点击会触发 + if (c.editShowMode === 'row' && c.allowRowEdit) { + const row = c.findRowState(data); + if (row) { + // 切换行编辑 + await c.switchRowEdit(row); + } + } else { + await c.onRowClick(data as ControlVO); + } + } + + let forbidClick: boolean = false; + + async function onRowClick( + data: ControlVO, + _column: IData, + event: MouseEvent, + ): Promise { + if (data.isGroupRow) { + tableRef.value?.store.loadOrToggle(data); + return; + } + // 非shift点击时需标记选中数据 + if (!event.shiftKey) { + const index = c.findRowStateIndex(data); + const isSelected = c.state.selectedData.includes(data); + lastSelectedIndex = isSelected ? null : index; + } + if (c.state.isAutoGrid) { + await onRowClickDynamic(data, _column, event); + return; + } + // 新建行拦截行点击事件 + if (data.srfuf === Srfuf.CREATE || forbidClick) return; + if (event.ctrlKey && !c.state.singleSelect) return handleCtrlSelect(data); + if (event.shiftKey && !c.state.singleSelect) return handleShiftSelect(data); + const target = event.target as HTMLElement; + const { classList } = target.parentElement || {}; + if (classList && classList.contains('el-table-column--selection')) { + tableRef.value!.toggleRowSelection(data); + return; + } + // 单行编辑模式下,行点击会触发 + if (c.editShowMode === 'row' && c.model.enableRowEdit) { + const row = c.findRowState(data); + if (row && row.showRowEdit !== true) { + // 开启行编辑 + await c.switchRowEdit(row, true); + } + } else { + await c.onRowClick(data as ControlVO); + } + forbidClick = true; + setTimeout(() => { + forbidClick = false; + }, 200); + } + + function onDbRowClick(data: ControlVO): void { + // 新建行拦截行双击事件 + if (data.srfuf === Srfuf.CREATE || data.isGroupRow) return; + c.onDbRowClick(data); + } + + function onSelectionChange(selection: ControlVO[]): void { + // 选中数据在回显的时候屏蔽值变更事件,否则会递归。 + if (!forbidChange && !c.state.isLoading) c.setSelection(selection); + } + + // element表格选中数据 + const elSelection = computed(() => { + const items: IData[] = []; + const keys = c.state.selectedData.map(selected => selected.srfkey); + if (tableRef.value) + recursiveIterate( + { children: tableRef.value.store.states.data.value }, + item => { + if (keys.includes(item.srfkey)) items.push(item); + }, + ); + return items; + }); + + // 监听选中数据,操作表格来界面回显选中效果。 + watch( + [ + () => tableRef.value, + (): boolean => c.state.isLoaded, + (): IData[] => c.state.selectedData, + ], + ([table, isLoaded, _newVal]) => { + if (!isLoaded || !table) return; + forbidChange = true; + setTimeout(() => { + if (c.state.singleSelect) { + table.setCurrentRow(elSelection.value[0]); + } else { + // 修复选中值中有父值时导致子全选中问题 + table.store.states.selection.value = elSelection.value; + } + forbidChange = false; + }); + }, + ); + + // 排序变更回调。 + function onSortChange(opts: { + _column: IData; + prop: string; + order: 'ascending' | 'descending'; + }): void { + if (isGridUISort) { + isGridUISort = false; + return; + } + const { prop, order } = opts; + const fieldName = c.fieldColumns[prop].model.appDEFieldId; + let order1: 'asc' | 'desc' | undefined; + if (order === 'ascending') { + order1 = 'asc'; + } else if (order === 'descending') { + order1 = 'desc'; + } + // 如果排序条件相同,不触发查询。 + const sortQuery = `${fieldName},${order1}`; + if (sortQuery === c.state.sortQuery) { + return; + } + if (c.runMode === 'DESIGN') { + c.state.items = orderBy( + c.state.items, + [item => item?.[fieldName || '']?.length || 0], + [order1 || 'asc'], + ); + c.state.rows = c.state.items.map(item => { + const row = new GridRowState(new ControlVO(item), c); + return row; + }); + return; + } + c.setSort(fieldName, order1); + // 非本地模式才走远程 + if (c.model.sortMode !== 'LOCAL') + c.load({ + isInitialLoad: c.model.pagingMode === 2 || c.model.pagingMode === 3, + }); + } + + // todo 用自己的ns类名去压制,把element原来的样式清除 + function handleRowClassName({ row }: { row: IData }): string { + let activeClassName = ''; + if (c.state.selectedData.length > 0) { + c.state.selectedData.forEach((data: IData) => { + // current-row用于多选激活样式与单选保持一致,有背景色 + if (data === row || data.srfkey === row.srfkey) + activeClassName = 'current-row'; + }); + } + const rowState = c.findRowState(row); + if (rowState?.showRowEdit) { + activeClassName += ' editing-row'; + } + if (row.srfkey) { + activeClassName += ` id-${row.srfkey}`; + } + if (c.enableRowEditOrder) { + activeClassName += ` enable-order`; + } + return activeClassName; + } + + // 表头单元格的 className 的回调方法 + function handleHeaderCellClassName({ + _row, + column, + _rowIndex, + _columnIndex, + }: { + _row: IData; + column: IData; + _rowIndex: number; + _columnIndex: number; + }): string { + const columnModel = allGridColumns.find(gridColumn => { + return gridColumn.codeName === column.property; + }); + if ( + columnModel && + columnModel.headerSysCss && + columnModel.headerSysCss.cssName + ) { + return columnModel.headerSysCss.cssName; + } + return ''; + } + + watch( + () => c.state.sortQuery, + newVal => { + // 监听排序查询条件,手动触发element-plus表格的排序以回显样式 + if (newVal) { + const prop = c.state.sortQuery.split(',')[0]; + const sortDir = c.state.sortQuery.split(',')[1]; + const column = c.model.degridColumns?.find(_column => { + return _column.codeName?.toLowerCase() === prop.toLowerCase(); + }); + if (column && sortDir) { + const order = sortDir === 'desc' ? 'descending' : 'ascending'; + const sortTable = () => { + if (tableRef.value) { + nextTick(() => { + if (newVal !== c.state.sortQuery) return; + isGridUISort = true; + tableRef.value!.sort(column.codeName, order); + }); + } else { + setTimeout(sortTable, 500); + } + }; + sortTable(); + } + } else { + tableRef.value?.clearSort(); + } + }, + ); + + return { + tableRef, + onRowClick, + onDbRowClick, + onSelectionChange, + onSortChange, + handleRowClassName, + handleHeaderCellClassName, + cleanClick, + cleanEnter, + cleanTab, + }; +} + +/** + * 使用表格分页组件 + * + * @author lxm + * @date 2022-09-06 17:09:09 + * @export + * @param {GridController} c + * @returns {*} + */ +export function useAppGridPagination(c: GridController): { + onPageChange: (page: number) => void; + onPageSizeChange: (size: number) => void; + onPageRefresh: () => void; +} { + function onPageChange(page: number): void { + if (!page || page === c.state.curPage) { + return; + } + c.state.curPage = page; + c.load(); + } + + function onPageSizeChange(size: number): void { + if (!size || size === c.state.size) { + return; + } + c.state.size = size; + + // 当page为第一页的时候切换size不会触发pageChange,需要自己触发加载 + if (c.state.curPage === 1) { + c.load(); + } + } + + function onPageRefresh(): void { + c.load(); + } + return { onPageChange, onPageSizeChange, onPageRefresh }; +} + +export function useAppGridBase( + c: GridController, + props: IGridProps, + tableRef: Ref, +): { + tableData: Ref; + renderColumns: Ref; + defaultSort: Ref; + summaryMethod: ({ + columns, + }: { + columns: TableColumnCtx[]; + data: IData[]; + }) => string[]; + spanMethod: ({ + row, + column, + rowIndex, + columnIndex, + }: { + row: IData; + column: IData; + rowIndex: number; + columnIndex: number; + }) => IData | void; + headerDragend: (newWidth: number, oldWidth: number, column: IData) => void; +} { + const initSimpleData = (): void => { + if (!props.data) return; + const items = (props.data as IData[]).map(item => new ControlVO(item)); + c.state.items = items; + if (c.runMode === 'DESIGN') { + c.state.simpleData = items; + c.state.total = props.data.length; + c.state.curPage = 1; + c.state.items = + chunk(c.state.simpleData, c.state.size)[c.state.curPage - 1] || []; + } + c.afterLoad({ isInitialLoad: true }, c.state.items as ControlVO[]); + }; + + const defaultSort = computed(() => { + const fieldColumn = Object.values(c.fieldColumns).find( + item => item.model.appDEFieldId === c.model.minorSortAppDEFieldId, + ); + return { + prop: fieldColumn?.model.codeName, + order: + c.model.minorSortDir?.toLowerCase() === 'desc' + ? 'descending' + : 'ascending', + }; + }); + + // 选中数据回显 + c.evt.on('onCreated', async () => { + if (props.isSimple) { + initSimpleData(); + c.state.isLoaded = true; + } + }); + + watch( + () => props.data, + () => { + if (props.isSimple) initSimpleData(); + }, + { + deep: true, + }, + ); + + // 表格数据,items和rows更新有时间差,用rows来获取items + const tableData = computed(() => { + const state = c.state; + if (c.state.enableGroup) { + const grouprowmode = c.controlParams.grouprowmode; + const result: IData[] = []; + state.groups.forEach(item => { + if (!item.children.length) return; + if (grouprowmode === 'NEWROW') { + result.push({ + ...item, + srfkey: item.key, + tempsrfkey: item.key, + isGroupRow: true, + }); + } else { + const children = [...item.children]; + const first = children.shift(); + result.push({ + tempsrfkey: first?.tempsrfkey || item.caption, + srfkey: first?.srfkey || item.caption, + isGroupData: true, + caption: item.caption, + first, + children, + }); + } + }); + return result; + } + const { rowspankeys = [] } = c.controlParams; + // 合并行单元格时计算合并数据,合并列则不用 + if (rowspankeys.length > 0) { + return sortMergeData( + rowspankeys, + state.rows.map(row => row.data), + ); + } + return state.rows.map(row => row.data); + }); + + /** + * @description 计算分组列状态 + * @param {IDEGridGroupColumn} groupColumn + * @returns {*} {IDEGridGroupColumn} + */ + const calcGroupColumns = ( + groupColumn: IDEGridGroupColumn, + ): IDEGridGroupColumn => { + const result: IDEGridGroupColumn = { ...groupColumn }; + const columns: IDEGridColumn[] = []; + const degridColumns = groupColumn.degridColumns || []; + degridColumns.forEach(column => { + if (column.columnType === 'GROUPGRIDCOLUMN') { + const children = calcGroupColumns(column as IDEGridGroupColumn); + if (children.degridColumns && children.degridColumns.length) { + columns.push(children); + } + } else { + const columnState = c.state.columnStates.find( + item => item.key === column.codeName, + ); + if (columnState && !columnState.hidden) { + const columnModel = + c.fieldColumns[columnState.key]?.model || + c.uaColumns[columnState.key]?.model; + if (columnModel) { + columns.push(columnModel); + } + } + } + }); + result.degridColumns = columns; + return result; + }; + + // 实际绘制的表格列 + const renderColumns = computed(() => { + const columns: IDEGridColumn[] = []; + if (c.isMultistageHeader) { + const degridColumns = c.model.degridColumns || []; + degridColumns.forEach((column: IDEGridColumn) => { + if (column.columnType === 'GROUPGRIDCOLUMN') { + const groupColumn = calcGroupColumns(column as IDEGridGroupColumn); + if (groupColumn.degridColumns && groupColumn.degridColumns.length) { + columns.push(groupColumn); + } + } else { + const columnState = c.state.columnStates.find( + item => item.key === column.codeName, + ); + if (columnState && !columnState.hidden) { + const columnModel = + c.fieldColumns[columnState.key]?.model || + c.uaColumns[columnState.key]?.model; + if (columnModel) { + columns.push(columnModel); + } + } + } + }); + return columns; + } + c.state.columnStates.forEach(item => { + if (item.hidden) { + return; + } + const columnModel = + c.fieldColumns[item.key]?.model || c.uaColumns[item.key]?.model; + if (columnModel) { + columns.push(columnModel); + } + }); + return columns; + }); + + /** + * 求和计算回调 + * @author lxm + * @date 2023-08-07 05:21:31 + * @return {*} {string[]} + */ + const summaryMethod = ({ + columns, + }: { + columns: TableColumnCtx[]; + data: IData[]; + }): string[] => { + return columns.map((item, index) => { + if (index === 0) { + return c.aggTitle; + } + return c.state.aggResult[item.property]; + }); + }; + + /** + * 表格合并单元格方法 + * + * @return {*} + */ + const spanMethod = ({ + row, + column, + rowIndex, + columnIndex, + }: { + row: IData; + column: IData; + rowIndex: number; + columnIndex: number; + }) => { + const spanMethodAttribute = c.model.controlAttributes?.find(item => { + return item.attrName === 'span-method' && item.attrValue; + }); + if (spanMethodAttribute) { + return ScriptFactory.execScriptFn( + { + ...c.getEventArgs(), + metadata: { + row, + column, + rowIndex, + columnIndex, + items: tableRef.value!.store.states.data.value, + }, + }, + spanMethodAttribute.attrValue!, + { isAsync: false }, + ) as IData; + } + const { property } = column; + const { rowspankeys = [], colspankeys = [] } = c.controlParams; + // 合并行 + if (rowspankeys.length > 0 && rowspankeys.includes(property)) { + // 只有第一列或有值时才和并 + const allow = columnIndex === 0 || row[property]; + // 第二行开始合并 + if ( + rowIndex > 0 && + allow && + row[property] === tableData.value[rowIndex - 1][property] + ) { + return { + rowspan: 0, + colspan: 0, + }; + } + // 第一行计算出合并长度 + let rowspan = 1; + for (let i = rowIndex + 1; i < tableData.value.length; i++) { + if (allow && tableData.value[i][property] === row[property]) { + rowspan += 1; + } else { + break; + } + } + return { + rowspan, + colspan: 1, + }; + } + // 合并列 + if (colspankeys.length > 0 && colspankeys.includes(property)) { + // 第二列开始合并 + const preProperty = renderColumns.value[columnIndex - 1].codeName; + if ( + columnIndex > 0 && + colspankeys.includes(preProperty) && + row[property] === row[preProperty!] + ) { + return { + rowspan: 0, + colspan: 0, + }; + } + // 第一列计算出合并长度 + let colspan = 1; + for (let i = columnIndex + 1; i < renderColumns.value.length; i++) { + const nextProperty = renderColumns.value[i].codeName!; + if ( + colspankeys.includes(nextProperty) && + row[nextProperty] === row[property] + ) { + colspan += 1; + } else { + break; + } + } + return { + rowspan: 1, + colspan, + }; + } + // 设置分组行的合并 + if (row.isGroupRow) { + const total = c.state.singleSelect + ? renderColumns.value.length + : renderColumns.value.length + 1; + const index = c.state.singleSelect ? 0 : 1; + if (columnIndex === index) return { rowspan: 1, colspan: total }; + return { + rowspan: 0, + colspan: 0, + }; + } + }; + + /** + * 表格列拖动 + * + * @return {*} + */ + const headerDragend = ( + newWidth: number, + oldWidth: number, + column: IData, + ): void => { + const { property } = column; + const columnC = c.columns[property!]; + const columnState = c.state.columnStates.find((item: IColumnState) => { + return item.key === property; + }); + if (columnC.isAdaptiveColumn) { + if (columnState) { + columnState.adaptive = false; + } + columnC.isAdaptiveColumn = false; + columnC.model.width = newWidth; + const index = renderColumns.value.findIndex(renderColumn => { + const renderColumnC = c.columns[renderColumn.codeName!]; + return renderColumnC.isAdaptiveColumn; + }); + c.hasAdaptiveColumn = index !== -1; + } + + // 列宽持久化 + + if (columnState) { + columnState.columnWidth = newWidth; + c.saveColumnStates(); + } + }; + + return { + tableData, + renderColumns, + defaultSort, + summaryMethod, + spanMethod, + headerDragend, + }; +} + +/** + * 监听表格头部高度变化,计算css变量 + */ +export function useGridHeaderStyle( + tableRef: IData, + ns: Namespace, +): { + headerCssVars: IData; +} { + // 浏览器ResizeObserver对象 + let resizeObserver: ResizeObserver | null = null; + + // 上次表格头高度 + let lastGridHeaderHeight = 0; + + // 样式变量 + const headerCssVars = ref({}); + + const calcGridHeaderHeight = () => { + if (window.ResizeObserver) { + const gridHeaderDom = tableRef.value!.$el.querySelector( + '.el-table__header-wrapper', + ); + if (gridHeaderDom) { + // 监听表格头高度变化动态去算css + resizeObserver = new ResizeObserver(entries => { + const height = entries[0].contentRect.height; + if (height !== lastGridHeaderHeight) { + const tempCssVars = { + 'now-header-height': `${height}px`, + }; + headerCssVars.value = ns.cssVarBlock(tempCssVars); + lastGridHeaderHeight = height; + } + }); + resizeObserver.observe(gridHeaderDom); + } + } + }; + + const stop = watchEffect(() => { + if (tableRef.value) { + calcGridHeaderHeight(); + } + }); + + onUnmounted(() => { + if (resizeObserver) { + resizeObserver.disconnect(); + } + stop(); + }); + + return { + headerCssVars, + }; +} + +export function useGridDraggable( + tableRef: IData, + ns: Namespace, + c: GridController, +): { + cleanup?: () => void; + setDragEvent?: () => void; +} { + // 表格不启用次序调整 + if (!c.enableRowEditOrder) { + return {}; + } + + // 拖拽下标 + let dragIndex = 0; + + // 目标下标 + let dropIndex = 0; + + // 拖拽数据 + let draggingData: IGridRowState | null = null; + + // 目标数据 + let dropData: IGridRowState | null = null; + + // eslint-disable-next-line @typescript-eslint/ban-types + const cleanups: Function[] = []; + + const calcSrfKeyByClass = (classList: DOMTokenList): string => { + let result = ''; + classList.forEach((className: string) => { + if (className.startsWith('id-')) { + result = className.replace('id-', ''); + } + }); + return result; + }; + + const setRowDragEvent = (item: HTMLTableRowElement) => { + item.setAttribute('draggable', 'true'); + const cleanDragStart = listenJSEvent( + item, + 'dragstart', + (event: DragEvent) => { + if (event.target) { + const draggingDom = event.target as HTMLElement; + event.dataTransfer!.effectAllowed = 'move'; + const draggingKey = calcSrfKeyByClass(draggingDom.classList); + dragIndex = c.state.rows.findIndex( + row => row.data.srfkey === draggingKey, + ); + draggingData = c.state.rows[dragIndex]; + } + }, + ); + const cleanDragEnter = listenJSEvent( + item, + 'dragenter', + (event: DragEvent) => { + event.preventDefault(); + const targetDom = event.currentTarget as HTMLElement; + const targetKey = calcSrfKeyByClass(targetDom.classList); + dropIndex = c.state.rows.findIndex( + row => row.data.srfkey === targetKey, + ); + if (draggingData?.data.srfkey === targetKey || dropIndex === -1) { + return; + } + dropData = c.state.rows[dropIndex]; + }, + ); + const cleanDragOver = listenJSEvent( + item, + 'dragover', + (event: DragEvent) => { + event.preventDefault(); + }, + ); + const cleanDragEnd = listenJSEvent(item, 'dragend', (event: DragEvent) => { + event.preventDefault(); + if (draggingData && dropData) { + c.onDragChange( + draggingData, + dropData, + dropIndex > dragIndex ? 'next' : 'prev', + ); + } + }); + cleanups.push(cleanDragStart); + cleanups.push(cleanDragEnter); + cleanups.push(cleanDragOver); + cleanups.push(cleanDragEnd); + }; + + const cleanup = () => { + cleanups.forEach(cleanupF => { + cleanupF(); + }); + }; + + const setDragEvent = () => { + cleanup(); + const grid = tableRef.value!.$el; + if (grid) { + const rows = grid.getElementsByClassName('el-table__row'); + rows.forEach((item: HTMLTableRowElement) => { + setRowDragEvent(item); + }); + } + }; + + watch( + [() => tableRef.value, (): boolean => c.state.isLoaded], + (table, isLoaded) => { + if (!isLoaded || !table) { + return; + } + setDragEvent(); + }, + ); + + return { + cleanup, + setDragEvent, + }; +} diff --git a/packages/entity-field-grid/src/utils/use-pagination.ts b/packages/entity-field-grid/src/utils/use-pagination.ts new file mode 100644 index 0000000..8e2d6b1 --- /dev/null +++ b/packages/entity-field-grid/src/utils/use-pagination.ts @@ -0,0 +1,70 @@ +import { + ControlVO, + GridController, + GridRowState, + IMDControlController, +} from '@ibiz-template/runtime'; +import { chunk } from 'lodash-es'; + +/** + * 使用分页组件 + * + * @author lxm + * @date 2022-09-06 17:09:09 + * @export + * @param {GridController} c + * @returns {*} + */ +export function usePagination(c: IMDControlController): { + onPageChange: (page: number) => void; + onPageSizeChange: (size: number) => void; + onPageRefresh: () => void; +} { + // 初始化表格项 + const initGridItems = () => { + const controller = c as GridController; + if (Array.isArray(controller.state.simpleData)) { + controller.state.items = + chunk(controller.state.simpleData, controller.state.size)[ + controller.state.curPage - 1 + ] || []; + controller.state.rows = controller.state.items.map(item => { + const row = new GridRowState(new ControlVO(item), controller); + return row; + }); + } + }; + + function onPageChange(page: number): void { + if (!page || page === c.state.curPage) { + return; + } + c.state.curPage = page; + if (c.runMode === 'DESIGN') { + initGridItems(); + return; + } + c.load(); + } + + function onPageSizeChange(size: number): void { + if (!size || size === c.state.size) { + return; + } + c.state.size = size; + if (c.runMode === 'DESIGN') { + initGridItems(); + return; + } + // 当page为第一页的时候切换size不会触发pageChange,需要自己触发加载 + if (c.state.curPage !== 1) { + c.state.curPage = 1; + } + c.load(); + } + + function onPageRefresh(): void { + c.load(); + } + return { onPageChange, onPageSizeChange, onPageRefresh }; +} diff --git a/packages/entity-field-grid/src/utils/use-row-edit-popover.tsx b/packages/entity-field-grid/src/utils/use-row-edit-popover.tsx new file mode 100644 index 0000000..b2d6fd3 --- /dev/null +++ b/packages/entity-field-grid/src/utils/use-row-edit-popover.tsx @@ -0,0 +1,101 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +import { computePosition } from '@floating-ui/dom'; +import { RuntimeError } from '@ibiz-template/core'; +import { GridController, GridRowState } from '@ibiz-template/runtime'; +import { ComponentPublicInstance, reactive, ref, Ref } from 'vue'; + +export function useRowEditPopover( + tableRef: Ref, + c: GridController, +) { + let popInstance: ComponentPublicInstance | undefined; + const showPop = ref(false); + const editingRow = ref(); + + const popStyle = reactive({}); + + const findTrEl = (row: GridRowState): HTMLElement => { + if (!tableRef.value) { + throw new RuntimeError(ibiz.i18n.t('control.common.citeErrMessage')); + } + const tableEl = tableRef.value.$el as HTMLElement; + + // 找到对应那一行的element + let selector = '.el-table__row'; + if (row.data.srfkey) { + selector += `[class*="id-${row.data.srfkey}"]`; + } + const trEl = tableEl.querySelector(selector); + if (!trEl) { + throw new RuntimeError(ibiz.i18n.t('control.common.noDomErrMessage')); + } + return trEl as HTMLElement; + }; + + const showRowEditPop = async (row: GridRowState) => { + const trEl = findTrEl(row); + if (!popInstance) { + throw new RuntimeError(ibiz.i18n.t('control.common.noPopErrMessage')); + } + const popEl = popInstance.$el as HTMLElement; + const { x, y } = await computePosition(trEl, popEl, { + placement: 'bottom', + }); + + Object.assign(popStyle, { + top: `${y}px`, + left: `${x}px`, + }); + editingRow.value = row; + showPop.value = true; + }; + + const onConfirm = async () => { + if (editingRow.value) { + c.switchRowEdit(editingRow.value, false, true); + } + }; + const onCancel = async () => { + if (editingRow.value) { + c.switchRowEdit(editingRow.value, false, false); + } + }; + + const renderPopover = () => { + // 显示气泡,且最后一行是开启编辑态的时候用占位撑开高度 + const showPlaceholder = + showPop.value && c.state.rows[c.state.rows.length - 1].showRowEdit; + return [ +
, + { + popInstance = ins; + }} + style={popStyle} + show={showPop.value} + onConfirm={onConfirm} + onCancel={onCancel} + >, + ]; + }; + + c.evt.on('onRowEditChange', event => { + if (event.row.showRowEdit && !c.state.isAutoGrid) { + setTimeout(() => { + showRowEditPop(event.row); + }, 0); + } else { + editingRow.value = undefined; + showPop.value = false; + Object.assign(popStyle, { + top: undefined, + left: undefined, + }); + } + }); + + return { renderPopover }; +} diff --git a/packages/entity-field-grid/tsconfig.json b/packages/entity-field-grid/tsconfig.json new file mode 100644 index 0000000..ba8f48b --- /dev/null +++ b/packages/entity-field-grid/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "moduleResolution": "Node", + "strict": true, + "jsx": "preserve", + "jsxImportSource": "vue", + "sourceMap": true, + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "lib": ["ESNext", "DOM"], + "skipLibCheck": true + }, + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/packages/entity-field-grid/tsconfig.node.json b/packages/entity-field-grid/tsconfig.node.json new file mode 100644 index 0000000..9d31e2a --- /dev/null +++ b/packages/entity-field-grid/tsconfig.node.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/packages/entity-field-grid/vite.config.ts b/packages/entity-field-grid/vite.config.ts new file mode 100644 index 0000000..fc3449a --- /dev/null +++ b/packages/entity-field-grid/vite.config.ts @@ -0,0 +1,57 @@ +import { defineConfig } from 'vite'; +import vue from '@vitejs/plugin-vue'; +import vueJsx from '@vitejs/plugin-vue-jsx'; +import libLegacy from '@qx-chitanda/vite-plugin-lib-legacy'; +import dts from 'vite-plugin-dts'; +import libCss from 'vite-plugin-libcss'; +// https://vitejs.dev/config/ +export default defineConfig({ + build: { + lib: { + entry: './src/index.ts', + fileName: format => `index.${format}.js`, + }, + rollupOptions: { + external: [ + '@ibiz-template/core', + '@ibiz-template/model-helper', + '@ibiz-template/runtime', + '@ibiz-template/theme', + '@ibiz-template/vue3-util', + '@ibiz/dynamic-model-api', + '@floating-ui/dom', + 'async-validator', + 'axios', + 'dayjs', + 'element-plus', + 'lodash-es', + 'pluralize', + 'qs', + 'qx-util', + 'ramda', + 'vue', + 'vue-router', + 'vuedraggable', + ], + }, + }, + css: { + preprocessorOptions: { + scss: { + additionalData: '@import "@ibiz-template/theme/style/global.scss";', + }, + }, + }, + plugins: [ + // eslint({ + // include: 'src/**/*.{ts,tsx,js,jsx}', + // }), + vue(), + vueJsx(), + libLegacy(), + libCss(), + dts({ + outDir: 'dist/types', + }), + ], +}); diff --git a/packages/form-user-control-plugin/package.json b/packages/form-user-control-plugin/package.json index ff019cc..c0bec22 100644 --- a/packages/form-user-control-plugin/package.json +++ b/packages/form-user-control-plugin/package.json @@ -51,7 +51,7 @@ "eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-vue": "^9.18.1", "husky": "^8.0.3", - "lerna": "^8.0.0", + "lerna": "^7.4.2", "lint-staged": "^15.1.0", "lodash-es": "^4.17.21", "pluralize": "^8.0.0", diff --git a/packages/global-plugin/package.json b/packages/global-plugin/package.json index 7e2bf3b..50f16fe 100644 --- a/packages/global-plugin/package.json +++ b/packages/global-plugin/package.json @@ -51,7 +51,7 @@ "eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-vue": "^9.18.1", "husky": "^8.0.3", - "lerna": "^8.0.0", + "lerna": "^7.4.2", "lint-staged": "^15.1.0", "lodash-es": "^4.17.21", "pluralize": "^8.0.0", diff --git a/packages/grid-column-plugin/package.json b/packages/grid-column-plugin/package.json index c70a409..94d01e9 100644 --- a/packages/grid-column-plugin/package.json +++ b/packages/grid-column-plugin/package.json @@ -51,7 +51,7 @@ "eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-vue": "^9.18.1", "husky": "^8.0.3", - "lerna": "^8.0.0", + "lerna": "^7.4.2", "lint-staged": "^15.1.0", "lodash-es": "^4.17.21", "pluralize": "^8.0.0", diff --git a/packages/panel-item-plugin/package.json b/packages/panel-item-plugin/package.json index 6fbc966..fe38234 100644 --- a/packages/panel-item-plugin/package.json +++ b/packages/panel-item-plugin/package.json @@ -51,7 +51,7 @@ "eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-vue": "^9.18.1", "husky": "^8.0.3", - "lerna": "^8.0.0", + "lerna": "^7.4.2", "lint-staged": "^15.1.0", "lodash-es": "^4.17.21", "pluralize": "^8.0.0", diff --git a/packages/portlet-plugin/package.json b/packages/portlet-plugin/package.json index 9bf468e..c2dff23 100644 --- a/packages/portlet-plugin/package.json +++ b/packages/portlet-plugin/package.json @@ -51,7 +51,7 @@ "eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-vue": "^9.18.1", "husky": "^8.0.3", - "lerna": "^8.0.0", + "lerna": "^7.4.2", "lint-staged": "^15.1.0", "lodash-es": "^4.17.21", "pluralize": "^8.0.0", diff --git a/packages/replace-default-demo/package.json b/packages/replace-default-demo/package.json index 8625ff7..2c31a1c 100644 --- a/packages/replace-default-demo/package.json +++ b/packages/replace-default-demo/package.json @@ -50,7 +50,7 @@ "eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-vue": "^9.18.1", "husky": "^8.0.3", - "lerna": "^8.0.0", + "lerna": "^7.4.2", "lint-staged": "^15.1.0", "lodash-es": "^4.17.21", "pluralize": "^8.0.0", diff --git a/packages/theme-plugin/package.json b/packages/theme-plugin/package.json index 0522ba2..b2665c1 100644 --- a/packages/theme-plugin/package.json +++ b/packages/theme-plugin/package.json @@ -51,7 +51,7 @@ "eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-vue": "^9.18.1", "husky": "^8.0.3", - "lerna": "^8.0.0", + "lerna": "^7.4.2", "lint-staged": "^15.1.0", "lodash-es": "^4.17.21", "pluralize": "^8.0.0", diff --git a/packages/toolbar-item-plugin/package.json b/packages/toolbar-item-plugin/package.json index 4188311..b8bb0cf 100644 --- a/packages/toolbar-item-plugin/package.json +++ b/packages/toolbar-item-plugin/package.json @@ -51,7 +51,7 @@ "eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-vue": "^9.18.1", "husky": "^8.0.3", - "lerna": "^8.0.0", + "lerna": "^7.4.2", "lint-staged": "^15.1.0", "lodash-es": "^4.17.21", "pluralize": "^8.0.0", diff --git a/packages/ui-action-plugin/package.json b/packages/ui-action-plugin/package.json index b48f8e0..d678884 100644 --- a/packages/ui-action-plugin/package.json +++ b/packages/ui-action-plugin/package.json @@ -48,7 +48,7 @@ "eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-vue": "^9.18.1", "husky": "^8.0.3", - "lerna": "^8.0.0", + "lerna": "^7.4.2", "lint-staged": "^15.1.0", "lodash-es": "^4.17.21", "pluralize": "^8.0.0", diff --git a/packages/ui-logic-node-plugin/package.json b/packages/ui-logic-node-plugin/package.json index 5136de6..21e0698 100644 --- a/packages/ui-logic-node-plugin/package.json +++ b/packages/ui-logic-node-plugin/package.json @@ -48,7 +48,7 @@ "eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-vue": "^9.18.1", "husky": "^8.0.3", - "lerna": "^8.0.0", + "lerna": "^7.4.2", "lint-staged": "^15.1.0", "lodash-es": "^4.17.21", "pluralize": "^8.0.0", diff --git a/packages/view-plugin/package.json b/packages/view-plugin/package.json index f15b5c1..8266c53 100644 --- a/packages/view-plugin/package.json +++ b/packages/view-plugin/package.json @@ -51,7 +51,7 @@ "eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-vue": "^9.18.1", "husky": "^8.0.3", - "lerna": "^8.0.0", + "lerna": "^7.4.2", "lint-staged": "^15.1.0", "lodash-es": "^4.17.21", "pluralize": "^8.0.0", -- Gitee