-
+
+
-
-
-
+
+
-
+
-
+
-
+
-
+
-
- {{ $t('tip.noData') }}
+
+ {{ $t(tools.table.store.noDataLabelI18nKey) }}
-
+
-
+
-
+
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/extra/ViewPanel.vue b/io.sc.platform.core.frontend/src/platform/components/grid/extra/ViewPanel.vue
new file mode 100644
index 00000000..dbbb57d0
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/extra/ViewPanel.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/extra/advanced-query/AdvancedQuery.vue b/io.sc.platform.core.frontend/src/platform/components/grid/extra/advanced-query/AdvancedQuery.vue
new file mode 100644
index 00000000..6ddf14ad
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/extra/advanced-query/AdvancedQuery.vue
@@ -0,0 +1,58 @@
+
+
+
+
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/extra/append/AppendContent.vue b/io.sc.platform.core.frontend/src/platform/components/grid/extra/append/AppendContent.vue
new file mode 100644
index 00000000..cb445290
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/extra/append/AppendContent.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/extra/append/AppendRow.vue b/io.sc.platform.core.frontend/src/platform/components/grid/extra/append/AppendRow.vue
new file mode 100644
index 00000000..755f3ddc
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/extra/append/AppendRow.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/AloneGroup.vue b/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/AloneGroup.vue
new file mode 100644
index 00000000..c061f6b5
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/AloneGroup.vue
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+ {{ displayColumnLabel[col] }}
+
+
+
+
+
+
+
+
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/CheckboxSelection.vue b/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/CheckboxSelection.vue
new file mode 100644
index 00000000..ab9ef3e4
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/CheckboxSelection.vue
@@ -0,0 +1,36 @@
+
+
+
+ 复选框
+
+
+
+
+
+
+
+
+
+
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/ConfigPanel.vue b/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/ConfigPanel.vue
new file mode 100644
index 00000000..d84bd484
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/ConfigPanel.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/Dense.vue b/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/Dense.vue
new file mode 100644
index 00000000..fd6a6b53
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/Dense.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+ 紧凑
+
+
+
+
+
+
+
+ 按钮栏紧凑
+
+
+
+
+
+
+
+ 列头紧凑
+
+
+
+
+
+
+
+ 内容紧凑
+
+
+
+
+
+
+
+ 分页栏紧凑
+
+
+
+
+
+
+
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/DisplayColumn.vue b/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/DisplayColumn.vue
new file mode 100644
index 00000000..2e47584e
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/DisplayColumn.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+ {
+ tools.opFM.resetStyleVariableValue(100);
+ }
+ "
+ />
+
+
+ {{ col.label || col.name }}
+
+
+
+
+
+
+
+
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/Fullscreen.vue b/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/Fullscreen.vue
new file mode 100644
index 00000000..05e98d5e
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/Fullscreen.vue
@@ -0,0 +1,35 @@
+
+ {
+ scope.toggleFullscreen();
+ tools.table.configStore.showConfigPanel = false;
+ }
+ "
+ >
+
+ 全屏
+
+
+
+
+
+
+
+
+
+
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/Separator.vue b/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/Separator.vue
new file mode 100644
index 00000000..3601e355
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/Separator.vue
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+ 水平
+
+
+
+
+
+
+
+ 垂直
+
+
+
+
+
+
+
+ 单元格
+
+
+
+
+
+
+
+ 无
+
+
+
+
+
+
+
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/SortNo.vue b/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/SortNo.vue
new file mode 100644
index 00000000..150ce5d8
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/SortNo.vue
@@ -0,0 +1,36 @@
+
+
+
+ 序号
+
+
+
+
+
+
+
+
+
+
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/StickyColumn.vue b/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/StickyColumn.vue
new file mode 100644
index 00000000..3fde5186
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/extra/config/StickyColumn.vue
@@ -0,0 +1,44 @@
+
+
+
+ 固定列
+
+
+ {
+ tools.opFM.resetStyleVariableValue(500);
+ }
+ "
+ >
+
+
+
+
+
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/extra/group/GroupTr.vue b/io.sc.platform.core.frontend/src/platform/components/grid/extra/group/GroupTr.vue
new file mode 100644
index 00000000..55f1379a
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/extra/group/GroupTr.vue
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+ {{ record['groupName'] }}
+
+
+
+
+
+
+
+
+
+
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/extra/inline-edit/CellEditor.vue b/io.sc.platform.core.frontend/src/platform/components/grid/extra/inline-edit/CellEditor.vue
new file mode 100644
index 00000000..8243d13d
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/extra/inline-edit/CellEditor.vue
@@ -0,0 +1,97 @@
+
+
+
+
+
+
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/extra/inline-edit/InlineEditComponent.vue b/io.sc.platform.core.frontend/src/platform/components/grid/extra/inline-edit/InlineEditComponent.vue
new file mode 100644
index 00000000..0e6661c9
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/extra/inline-edit/InlineEditComponent.vue
@@ -0,0 +1,71 @@
+
+
+ {
+ if (needExit()) {
+ tools.editFM.exitInlineEdit();
+ }
+ }
+ "
+ >
+
+
+ {
+ if (needExit()) {
+ tools.editFM.exitInlineEdit();
+ }
+ }
+ "
+ >
+
+
+
+
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/extra/inline-edit/InlineEditToolbar.vue b/io.sc.platform.core.frontend/src/platform/components/grid/extra/inline-edit/InlineEditToolbar.vue
new file mode 100644
index 00000000..038cb058
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/extra/inline-edit/InlineEditToolbar.vue
@@ -0,0 +1,249 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/Base.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/Base.ts
new file mode 100644
index 00000000..1be44b91
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/Base.ts
@@ -0,0 +1,48 @@
+import { Tools } from '@/platform';
+import { GridTools } from './GridTools';
+import { TableType } from './types/TableType';
+import type { PropsType } from './types/PropsType';
+
+/**
+ * w-grid 基类
+ */
+export class Base {
+ /**
+ * w-grid 配置属性
+ */
+ props: PropsType;
+ /**
+ * w-grid 表格全局变量管理类,可供所有子组件直接使用,避免通过组件属性反复传递。
+ */
+ table: TableType;
+ /**
+ * w-grid 组件实例
+ */
+ instance?: any;
+ /**
+ * w-grid 工具类
+ */
+ tools?: GridTools;
+
+ constructor(props: PropsType, table: TableType) {
+ this.props = props;
+ this.table = table;
+ }
+
+ instanceError() {
+ if (Tools.isEmpty(this.instance)) {
+ throw new Error('[w-grid] The `instance` variable of `FunctionTools` class is undefined.');
+ }
+ }
+ tableError() {
+ if (Tools.isEmpty(this.table)) {
+ throw new Error('[w-grid] The `table` variable of `FunctionTools` class is undefined.');
+ }
+ }
+
+ // 设置组件实例
+ setInstance(instance: any, tools?: GridTools) {
+ this.instance = instance;
+ this.tools = tools;
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/GridTools.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/GridTools.ts
new file mode 100644
index 00000000..fd5bd1e5
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/GridTools.ts
@@ -0,0 +1,328 @@
+import { reactive } from 'vue';
+import { Constant } from './constant/Constant';
+import { TableType } from './types/TableType';
+import type { PropsType } from './types/PropsType';
+import { Init } from './Init';
+import { getCssVar } from 'quasar';
+import { Environment, Tools } from '@/platform';
+
+import { ComputedManager } from './computed/ComputedManager';
+import { EventManager } from './event/EventManager';
+import { ExposeApiManager } from './expose-api/ExposeApiManager';
+import { Criteria } from './function/Criteria';
+import { DragAndDrop } from './function/DragAndDrop';
+import { InlineEdit } from './function/InlineEdit';
+import { Operator } from './function/Operator';
+import { RequestApi } from './function/RequestApi';
+import { RowData } from './function/RowData';
+import { ButtonManager } from './toolbar/ButtonManager';
+
+/**
+ * w-grid 工具类
+ */
+export class GridTools {
+ /**
+ * w-grid 配置属性
+ */
+ props: PropsType;
+ /**
+ * w-grid 表格全局变量管理类,可供所有子组件直接使用,避免通过组件属性反复传递。
+ */
+ table: TableType;
+ /**
+ * w-grid 组件实例
+ */
+ instance?: any;
+
+ /**
+ * 表格事件管理器
+ */
+ em: EventManager;
+ /**
+ * 计算属性管理器
+ */
+ cm: ComputedManager;
+ /**
+ * 表格内置按钮管理器
+ */
+ bm: ButtonManager;
+ /**
+ * 对外暴露api 函数管理器
+ */
+ apiFM: ExposeApiManager;
+ /**
+ * 行数据处理函数管理器
+ */
+ dataFM: RowData;
+ /**
+ * 表格操作函数管理器
+ */
+ opFM: Operator;
+ /**
+ * 内联编辑函数管理器
+ */
+ editFM: InlineEdit;
+ /**
+ * 拖拽排序函数管理器
+ */
+ dndFM: DragAndDrop;
+ /**
+ * 请求后端API函数管理器
+ */
+ reqApiFM: RequestApi;
+ /**
+ * criteria 查询函数管理器
+ */
+ criteriaFM: Criteria;
+
+ constructor(props: PropsType) {
+ this.props = props;
+
+ const darkCssVar = getCssVar('dark') || '';
+ const gc = Environment.getConfigure();
+
+ const dense = props.dense !== undefined ? props.dense : false;
+ const denseBottom = props.denseBottom !== undefined ? props.denseBottom : false;
+ let bodyCellHeight = 48;
+ if (denseBottom) {
+ bodyCellHeight = 24;
+ } else if (dense !== false) {
+ bodyCellHeight = 24;
+ }
+
+ let alongGroupByField: any = undefined;
+ if (this.props.groupMode === Constant.GROUP_MODE.ALONE) {
+ if (typeof this.props.groupByField === 'string') {
+ alongGroupByField = this.props.groupByField;
+ } else if (Array.isArray(this.props.groupByField)) {
+ alongGroupByField = this.props.groupByField[0];
+ }
+ }
+
+ this.table = reactive({
+ originalColumns: [...props.columns],
+ columns: Init.initColumn(props.columns, props),
+ rows: [],
+ queryCriteria: { ...props.queryCriteria },
+ moreQueryStatus: false,
+ advancedQueryStatus: false,
+ advancedQueryModelValue: undefined,
+ queryFormDisplayFields: [],
+ componentRef: {
+ getTableRef: () => {},
+ getTopRef: () => {},
+ getHeaderRef: () => {},
+ getBodyRef: () => {},
+ getEditorRef: () => {},
+ getCellEditorRef: () => {},
+ getViewRef: () => {},
+ },
+ color: {
+ darkBgColor: darkCssVar,
+ headBgColor: gc.theme.dark ? darkCssVar : gc.theme?.grid?.headBgColor || '#f5f7fa',
+ borderColor: gc.theme?.grid?.borderColor || 'rgba(0, 0, 0, 0.12)',
+ stickyBgColor: gc.theme.dark ? darkCssVar : gc.theme?.grid?.stickyBgColor || '#ffffff',
+ },
+ url: {
+ dataUrl: props.dataUrl,
+ fetchDataUrl: props.fetchDataUrl,
+ addDataUrl: props.addDataUrl,
+ editDataUrl: props.editDataUrl,
+ removeDataUrl: props.removeDataUrl,
+ },
+ configStore: {
+ showConfigPanel: false,
+ isFullscreen: false,
+ separator: props.separator,
+ stickyNum: props.stickyNum,
+ useCheckboxSelection: props.checkboxSelection,
+ showSortNo: props.sortNo,
+ dense: dense,
+ denseToolbar: props.denseToolbar !== undefined ? props.denseToolbar : false,
+ denseHeader: props.denseHeader !== undefined ? props.denseHeader : false,
+ denseBody: props.denseBody !== undefined ? props.denseBody : false,
+ denseBottom: denseBottom,
+ aloneGroupRecords: [],
+ aloneGroupByField: alongGroupByField,
+ },
+ store: {
+ expand: props.treeDefaultExpandAll,
+ expandDatas: [],
+ alreadyLoadAllData: false,
+ lazyloadNoChildrenDatas: [],
+ headerTicked: false,
+ dragRecords: [],
+ inlineEditStatus: Constant.EDIT_STATUS.NONE,
+ cellSelected: undefined,
+ mergeGroupRecords: {},
+ spaceHeight: 4,
+ noDataLabelI18nKey: 'tip.noData',
+ loading: false,
+ loadingLabelI18nKey: 'tip.dataLoading',
+ bodyCellHeight: bodyCellHeight,
+ resizeFlag: false,
+ location: {
+ yLocation: 0,
+ noDataTrYLocation: 0,
+ topHeight: 0,
+ bottomHeight: 0,
+ middleWidth: 0,
+ middleScrollWidth: 0,
+ middleHeight: 0,
+ columnHeadHeight: 0,
+ titleTotalHeight: 0,
+ hideHeaderNoDataHeight: 0,
+ tableInFullscreenY: 0,
+ noDataTrInFullscreenY: 0,
+ },
+ pagination: {
+ config: {
+ boundaryLinks: true,
+ boundaryNumbers: false,
+ directionLinks: false,
+ ellipses: false,
+ maxPages: 5,
+ },
+ sortBy: '',
+ descending: false,
+ reqPageStart: props.pagination['reqPageStart'] || 0,
+ page: 1,
+ rowsPerPage: props.pagination['rowsPerPage'] || 10,
+ rowsNumber: 10,
+ rowsPerPageOptions: [5, 10, 20, 50, 100],
+ },
+ },
+ });
+
+ this.em = new EventManager(props, this.table);
+ this.cm = new ComputedManager(props, this.table);
+ this.bm = new ButtonManager(props, this.table);
+ this.apiFM = new ExposeApiManager(props, this.table);
+ this.dataFM = new RowData(props, this.table);
+ this.opFM = new Operator(props, this.table);
+ this.editFM = new InlineEdit(props, this.table);
+ this.dndFM = new DragAndDrop(props, this.table);
+ this.reqApiFM = new RequestApi(props, this.table);
+ this.criteriaFM = new Criteria(props, this.table);
+ }
+
+ // 设置组件实例
+ setInstance(instance: any) {
+ const this_ = this;
+ this.instance = instance;
+ this.em.setInstance(instance, this_);
+ this.cm.setInstance(instance, this_);
+ this.apiFM.setInstance(instance, this_);
+ this.bm.setInstance(instance, this_);
+ this.dataFM.setInstance(instance, this_);
+ this.opFM.setInstance(instance, this_);
+ this.editFM.setInstance(instance, this_);
+ this.dndFM.setInstance(instance, this_);
+ this.reqApiFM.setInstance(instance, this_);
+ this.criteriaFM.setInstance(instance, this_);
+ }
+
+ /**
+ * 移除表格数据中附加的字段
+ * @param target
+ */
+ static removeExtraFields(target: any) {
+ if (!target) {
+ console.error('[w-grid] target is undefined.');
+ return;
+ }
+ if (Array.isArray(target)) {
+ const tmp = [...target];
+ tmp.forEach((item) => {
+ GridTools.removeFields(item);
+ });
+ return tmp;
+ } else {
+ const tmp = { ...target };
+ GridTools.removeFields(tmp);
+ return tmp;
+ }
+ }
+
+ private static removeFields(target: any) {
+ if (Tools.hasOwnProperty(target, Constant.FIELD_NAMES.ROW_KEY)) {
+ delete target[Constant.FIELD_NAMES.ROW_KEY];
+ }
+ if (Tools.hasOwnProperty(target, Constant.FIELD_NAMES.EXPAND)) {
+ delete target[Constant.FIELD_NAMES.EXPAND];
+ }
+ if (Tools.hasOwnProperty(target, Constant.FIELD_NAMES.TICKED_COUNT)) {
+ delete target[Constant.FIELD_NAMES.TICKED_COUNT];
+ }
+ if (Tools.hasOwnProperty(target, Constant.FIELD_NAMES.CHILDREN_TICKED_COUNT)) {
+ delete target[Constant.FIELD_NAMES.CHILDREN_TICKED_COUNT];
+ }
+ if (Tools.hasOwnProperty(target, Constant.FIELD_NAMES.ROW_OLD_VALUE)) {
+ delete target[Constant.FIELD_NAMES.ROW_OLD_VALUE];
+ }
+ if (Tools.hasOwnProperty(target, Constant.FIELD_NAMES.ROW_INDEX)) {
+ delete target[Constant.FIELD_NAMES.ROW_INDEX];
+ }
+ if (Tools.hasOwnProperty(target, Constant.FIELD_NAMES.LAZYLOAD_NO_CHILDREN)) {
+ delete target[Constant.FIELD_NAMES.LAZYLOAD_NO_CHILDREN];
+ }
+ }
+
+ /**
+ * 对象数组转对象
+ * 说明:[{id: 1, value: '张三'}, {id: 2, value: '李四'}] ==> { 1: { id: 1, value: '张三' }, 2: { id: 2, value: '李四'} }
+ * @param array 对象数组
+ * @param propertyName 属性名
+ * @returns 转换结果
+ */
+ static arrayToObject(array: Array
, propertyName: string) {
+ const result = array.reduce((tmp, item) => {
+ tmp[item[propertyName]] = item;
+ return tmp;
+ }, {});
+ return result;
+ }
+
+ /**
+ * 构建孩子行所需的 scope 属性
+ * @param currentScope 当前scope
+ * @param child 孩子行数据
+ * @param childIndex 孩子行下标
+ * @param selectedFieldName 选中配置的字段名
+ * @returns
+ */
+ static buildChildrenScope(currentScope: any, child: any, childIndex: number, selectedFieldName: string, tickedFieldName: string) {
+ const cols = GridTools.buildChildrenCols(currentScope, child);
+ const childScope = {
+ ...currentScope,
+ cols: cols,
+ key: child[Constant.FIELD_NAMES.ROW_KEY],
+ [Constant.FIELD_NAMES.EXPAND]: child[Constant.FIELD_NAMES.EXPAND],
+ pageIndex: childIndex,
+ rowIndex: childIndex,
+ row: child,
+ [selectedFieldName]: child[selectedFieldName],
+ [tickedFieldName]: child[tickedFieldName],
+ };
+ return childScope;
+ }
+ // 构建孩子行 scope 中 cols 属性
+ private static buildChildrenCols(currentScope: any, child: any) {
+ const cols = [];
+ currentScope.cols.forEach((col) => {
+ cols.push({ ...col, value: GridTools.formatValue(child, col) });
+ });
+ return cols;
+ }
+ // 格式化值
+ private static formatValue(child: any, col: any) {
+ if (col.format && typeof col.format === 'function') {
+ try {
+ return col.format(child[col.name], child);
+ } catch (error) {
+ console.error('[w-grid]column `' + col.name + '` format error:', error);
+ }
+ }
+ return child[col.name];
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/Init.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/Init.ts
new file mode 100644
index 00000000..b1f43958
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/Init.ts
@@ -0,0 +1,226 @@
+import { Tools, $t } from '@/platform';
+import { Constant } from './index';
+
+/**
+ * 初始化工具类
+ */
+export class Init {
+ /**
+ * 创建 props 并设置默认值
+ * @returns
+ */
+ public static createProps() {
+ return {
+ height: { type: Number, default: 0 }, // 表格高度
+ title: { type: String, default: '' }, // 表格标题
+ autoFetchData: { type: Boolean, default: true }, // 自动加载数据
+ localMode: { type: Boolean, default: false }, // 本地模式,启用该模式后,除了查询数据依旧通过 autoFetchData 与 url 控制外,其他所有与后端交互的请求都失效,同时分页属性也失效
+ selectMode: { type: String, default: Constant.SELECT_MODE.ROW }, // 选择模式
+ dndMode: { type: String, default: undefined }, // 拖拽数据行排序的模式,默认不可拖拽,字符串`local`为本地排序,只根据配置的字段进行本地排序,不会将数据发送至后端,字符串`server`为远程排序,拖拽完成后会将数据发送至后端。
+ dndOrderBy: { type: String, default: 'order' }, // 拖拽排序时根据哪个字段进行排序,默认为`order`字段
+ pageable: { type: Boolean, default: true }, // 是否需要分页,当启用树形表格模式时该属性失效,树形表格不支持分页。
+ configButton: { type: Boolean, default: true }, // 是否显示表格配置按钮
+ dataUrl: { type: String, default: '' }, // 表格数据操作请求的URL前缀
+ fetchDataUrl: { type: String, default: '' }, // 获取数据URL
+ addDataUrl: { type: String, default: '' }, // 新增数据url
+ editDataUrl: { type: String, default: '' }, // 修改数据url
+ removeDataUrl: { type: String, default: '' }, // 删除数据url
+ dense: { type: Boolean, default: undefined }, // 表格整体紧凑模式
+ denseHeader: { type: Boolean, default: undefined }, // 表格列标题紧凑模式
+ denseBody: { type: Boolean, default: undefined }, // 表格数据行紧凑模式
+ denseBottom: { type: Boolean, default: undefined }, // 表格底部分页栏紧凑模式
+ denseToolbar: { type: Boolean, default: true }, // 表格toolbar按钮栏紧凑模式
+ sortNo: { type: Boolean, default: false }, // 是否显示排序号
+ stickyNum: { type: Number, default: 0 }, // 从左侧开始冻结列数,复选框与排序列不计算在内,支持1-10列。
+ checkboxSelection: { type: Boolean, default: true }, // checkbox选择模式,默认启用
+ tickedField: { type: String, default: 'ticked' }, // 行数据中记录checkbox勾选状态的字段名
+ selectedField: { type: String, default: 'selected' }, // 行数据中记录行点击选中状态的字段名
+ tree: { type: Boolean, default: false }, // 树形表格模式,按照层级关系构建数据并以树形展现
+ treeLazyLoad: { type: Boolean, default: false }, // 树型表格模式,懒加载
+ treeIcon: { type: Function, default: undefined }, // 树形表格模式,图标函数,带有一个参数,行数据
+ treeDefaultExpandAll: { type: Boolean, default: false }, // 树形表格模式,树形表格数据加载后是否全部展开
+ treeTickStrategy: { type: String, default: 'leaf' }, // 树形表格模式,树勾选策略,strict:节点自己,leaf:包含子节点
+ treeRelationship: { type: String, default: 'parent' }, // 树形表格模式的数据关系,包括:parent, children,当为parent时组件根据数据主键与数据外键构建树形数据,否则需要自己构建好树形数据放到children属性中。
+ primaryKey: { type: String, default: 'id' }, // 数据主键(常规表格模式时,该字段可用作内置的编辑删除等功能对应的后端API入参,如:继承RestCrudController的update所需的入参。树形表格模式时,该字段为构建树数据的主键)
+ foreignKey: { type: String, default: 'parent' }, // 数据外键(常规表格模式时,该字段暂时无用,将来可用作多个表格的数据关系字段。树形表格模式时,该字段为构建树数据的关系字段)
+ refreshData: { type: Boolean, default: false }, // 新增、删除、修改成功后是否刷新数据列表,默认不刷新但是新增修改后台必须返回对应的行数据对象,删除则必须返回删除的记录集primaryKey集合。
+ dbClickOperation: { type: String, default: 'view' }, // 默认的双击操作:可填写内置或自定义按钮name,执行的操作为按钮对应的click,固定值提供:expand(展开双击的行)、none(双击不执行任何动作)
+ separator: { type: String, default: 'cell' }, // 表格分割线,支持:horizontal、vertical、cell、none
+ hideHeader: { type: Boolean, default: false }, // 隐藏表头
+ hideBottom: { type: Boolean, default: false }, // 隐藏底部
+ advancedQuery: { type: Boolean, default: false }, // 高级查询
+ groupMode: { type: String, default: undefined }, // 分组模式,支持:alone、merge
+ groupStartOpen: { type: [String, Array, Function], default: Constant.GROUP_START_OPEN.ALL }, // alone分组模式下默认展开的组,字符串支持:all、first、none,数组可配置多个组名。
+ groupByField: {
+ // 分组字段配置,当分组模式为 alone 时若配置的为数组,取数组第一个字段。
+ type: [String, Array],
+ default: () => {
+ return [];
+ },
+ },
+ appendRows: {
+ // 表格追加行,添加到当前表格数据行尾,可添加多行,支持跨行跨列配置,用于添加合计或者额外信息。
+ type: Array,
+ default: () => {
+ return [];
+ },
+ },
+ sortBy: {
+ type: Array,
+ default: () => {
+ return [];
+ },
+ }, // 默认排序字段,示例:['userName', '-lastModifyDate'],其中“-”开头表示倒序。
+ tickedRecord: {
+ // 默认勾选的记录,传入一个对象,包含:columnName(列名称,字符串类型,该属性不传默认根据数据主键查找)、data(需要勾选的数据,数组类型)
+ type: Object,
+ default: () => {
+ return {};
+ },
+ },
+ queryCriteria: {
+ // 查询条件,查询、翻页、刷新等操作都会带上的查询条件
+ type: Object,
+ default: () => {
+ return {};
+ },
+ },
+ columns: {
+ type: Array,
+ default: () => {
+ return [];
+ },
+ },
+ queryFormFields: {
+ type: Array,
+ default: () => {
+ return [];
+ },
+ },
+ queryFormColsNum: {
+ type: [Number, Object],
+ default: 0,
+ },
+ queryFormRowNum: { type: Number, default: 1 },
+ queryFormAttrs: {
+ type: Object,
+ default: () => {
+ return {};
+ },
+ },
+ toolbarActions: {
+ type: Array,
+ default: () => {
+ return [];
+ },
+ },
+ toolbarConfigure: {
+ type: Object,
+ default: () => {
+ return {};
+ },
+ },
+ pagination: {
+ type: Object,
+ default: () => {
+ return {
+ reqPageStart: 0,
+ rowsPerPage: 10,
+ };
+ },
+ },
+ editor: {
+ type: Object,
+ default: () => {
+ return {
+ dialog: {},
+ form: {},
+ };
+ },
+ },
+ viewer: {
+ type: Object,
+ default: () => {
+ return {
+ drawer: {},
+ panel: {
+ fields: [],
+ },
+ };
+ },
+ },
+ // 作为form中的组件使用----start
+ showIf: {
+ type: [Boolean, Function],
+ default: () => {
+ return true;
+ },
+ },
+ form: {
+ type: Object,
+ default: undefined,
+ },
+ // 作为form中的组件使用----end
+ };
+ }
+
+ /**
+ * 列初始化,设置默认属性并将子列提取到同级
+ * @param columns 所有列配置
+ * @param props 表格props
+ * @returns
+ */
+ public static initColumn(columns: Array, props: any) {
+ const gridColumns = [];
+ if (columns && columns.length > 0) {
+ const sortable = true;
+ gridColumns.push({ name: '_sortNo_', align: 'center', label: $t('rownum'), field: '_sortNo_', showIf: props.sortNo });
+ columns.forEach((item: any) => {
+ this.childrenHandler(item, gridColumns, sortable);
+ });
+ return gridColumns;
+ }
+ return [];
+ }
+
+ // 孩子列处理
+ private static childrenHandler(item: any, gridColumns: any, sortable: boolean) {
+ if (item.columns && item.columns.length > 0) {
+ item.columns.forEach((column) => {
+ this.childrenHandler(column, gridColumns, sortable);
+ });
+ } else {
+ this.columnStyle(item);
+ const col = {
+ ...{ align: 'left', label: item.name, field: item.name, name: item.name, sortable: sortable, showIf: true },
+ ...item,
+ };
+ if (Tools.isEmpty(col.name)) {
+ col.name = Tools.uuid();
+ }
+ gridColumns.push(col);
+ }
+ }
+
+ // 列样式处理
+ private static columnStyle(item: any) {
+ let style = '';
+ if (Tools.hasOwnProperty(item, 'style')) {
+ style = item.style;
+ }
+ if (Tools.hasOwnProperty(item, 'width')) {
+ if (typeof item.width === 'number') {
+ item.style = `min-width: ` + item.width + `px; width: ` + item.width + `px;max-width: ` + item.width + `px;` + style;
+ } else {
+ item.style = `min-width: ` + item.width + `; width: ` + item.width + `;max-width: ` + item.width + `;` + style;
+ }
+ delete item.width;
+
+ if (Tools.hasOwnProperty(item, 'classes')) {
+ item.classes = item.classes + ' truncate';
+ } else {
+ item.classes = 'truncate';
+ }
+ }
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/computed/ComputedManager.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/computed/ComputedManager.ts
new file mode 100644
index 00000000..9dda6a69
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/computed/ComputedManager.ts
@@ -0,0 +1,252 @@
+import { Tools } from '@/platform';
+import { computed } from 'vue';
+import { Base } from '../Base';
+import { Constant } from '../constant/Constant';
+import { GridTools } from '../GridTools';
+
+/**
+ * w-grid 表格计算属性管理器
+ * 主要是将多个组件都会用到的计算属性进行统一管理,避免通过组件反复传递
+ */
+export class ComputedManager extends Base {
+ /**
+ * 当前列头是否紧凑
+ */
+ denseHeader = computed(() => {
+ if (this.table?.configStore?.denseHeader) {
+ return true;
+ } else if (this.table?.configStore.dense !== false) {
+ return true;
+ } else {
+ return false;
+ }
+ });
+
+ /**
+ * 当前内容是否紧凑
+ */
+ denseBody = computed(() => {
+ if (this.table?.configStore?.denseBody) {
+ return true;
+ } else if (this.table?.configStore.dense !== false) {
+ return true;
+ } else {
+ return false;
+ }
+ });
+
+ /**
+ * 当前分页工具栏是否紧凑
+ */
+ denseBottom = computed(() => {
+ if (this.table?.configStore?.denseBottom) {
+ return true;
+ } else if (this.table?.configStore.dense !== false) {
+ return true;
+ } else {
+ return false;
+ }
+ });
+
+ /**
+ * 当前表格显示的所有列名
+ */
+ displayColumns = computed(() => {
+ const visibleColumns: string[] = [];
+ this.table?.columns.forEach((item) => {
+ if (!this.props.tree || (this.props.tree && item.name !== Constant.FIELD_NAMES.SORT_NO)) {
+ if (Tools.isEmpty(item.showIf)) {
+ visibleColumns.push(item.name);
+ } else if (typeof item.showIf === 'boolean' && item.showIf) {
+ visibleColumns.push(item.name);
+ }
+ }
+ });
+ return visibleColumns;
+ });
+
+ /**
+ * 合并分组时分组字段
+ */
+ mergeGroupByField = computed(() => {
+ if (typeof this.props.groupByField === 'string') {
+ [this.props.groupByField];
+ }
+ return this.props.groupByField;
+ });
+
+ /**
+ * 计算额外拓展的列数(排序号、checkbox复选框)
+ */
+ extColumnNum = computed(() => {
+ let num = 0;
+ if (this.props.tree) {
+ return num;
+ }
+ if (this.table?.configStore.showSortNo) {
+ num += 1;
+ }
+ if (this.table?.configStore.useCheckboxSelection) {
+ num += 1;
+ }
+ return num;
+ });
+
+ /**
+ * 计算当前表格显示的列数(包括拓展的排序号、checkbox复选框)
+ */
+ displayColumnNum = computed(() => {
+ let colspan = this.extColumnNum.value;
+ colspan += this.displayColumns.value.length;
+ return colspan;
+ });
+
+ /**
+ * 无数据块的样式
+ */
+ noDataStyle = computed(() => {
+ let style = {};
+ if (this.props.hideHeader) {
+ style = {
+ height: this.table?.store.location.hideHeaderNoDataHeight + 'px',
+ display: 'flex',
+ 'align-items': 'center',
+ 'justify-content': 'center',
+ };
+ } else {
+ let availableHeight = 0;
+ let otherHeight = 0;
+ let otherHeight2 = 0;
+
+ const parentHtmlElement = this.instance?.getHtmlElement()?.parentElement;
+ if (parentHtmlElement) {
+ availableHeight = Math.floor(parentHtmlElement.clientHeight);
+ }
+
+ otherHeight += this.table?.store.location.topHeight || 0;
+ otherHeight += this.table?.store.location.bottomHeight || 0;
+ otherHeight += this.table?.store.spaceHeight || 0;
+
+ otherHeight2 += this.table?.store.location.titleTotalHeight;
+ otherHeight2 += this.table?.store.location.middleScrollWidth - this.table?.store.location.middleWidth > 0 ? 15 : 0;
+
+ availableHeight -= otherHeight + otherHeight2;
+ availableHeight -= 2; //无数据增加的title行,下边框所占边框
+
+ style = {
+ height: this.props.height > 0 ? this.props.height - otherHeight2 - 1 + 'px' : availableHeight > 0 ? availableHeight + 'px' : '0px',
+ };
+ }
+ if (this.table?.store.resizeFlag) {
+ return style;
+ }
+ return style;
+ });
+
+ /**
+ * 内置按钮入参
+ */
+ actionArgs = computed(() => {
+ return {
+ selected: this.tools?.apiFM.getData.getSelectedRow(),
+ selecteds: this.tools?.apiFM.getData.getSelectedRows(),
+ ticked: this.tools?.apiFM.getData.getTickedRow(),
+ tickeds: this.tools?.apiFM.getData.getTickedRows(),
+ grid: this.instance,
+ selectedColName: this.table.store.cellSelected?.colName,
+ };
+ });
+
+ /**
+ * 表格样式
+ */
+ tableClass = computed(() => {
+ const classArr = ['sticky-header-column-table', 'w-grid'];
+ if (this.table.configStore.stickyNum && this.table.configStore.stickyNum > 0) {
+ if (this.table.componentRef.getHeaderRef().getColumnTitleState()?.columnTitleRowNum > 1) {
+ // 存在多行列头
+ const stickyColumn = this.table.originalColumns.filter((item, index) => {
+ return index < this.table.configStore.stickyNum;
+ });
+ let tdNum = this.extColumnNum.value;
+ stickyColumn.forEach((item: any, index: number) => {
+ tdNum += this.table.componentRef.getHeaderRef().getMoreColumnTitleMap()?.get(item.name)?.colspan;
+ });
+ // 处理表格数据列锁定
+ if (tdNum > 0) {
+ for (let n = 1; n <= tdNum; n++) {
+ classArr.push('sticky-header-column-table-td-' + n);
+ }
+ }
+ } else {
+ // 不存在多行列头
+ if (this.extColumnNum.value === 2) {
+ classArr.push('sticky-header-column-table-tr-1' + '-' + 1);
+ classArr.push('sticky-header-column-table-tr-1' + '-' + 2);
+ classArr.push('sticky-header-column-table-td-' + 1);
+ classArr.push('sticky-header-column-table-td-' + 2);
+ } else if (this.extColumnNum.value === 1) {
+ classArr.push('sticky-header-column-table-tr-1' + '-' + 1);
+ classArr.push('sticky-header-column-table-td-' + 1);
+ }
+ for (let i = 1; i <= this.table.configStore.stickyNum; i++) {
+ classArr.push('sticky-header-column-table-tr-1' + '-' + (i + this.extColumnNum.value));
+ classArr.push('sticky-header-column-table-td-' + (i + this.extColumnNum.value));
+ }
+ }
+ }
+ return classArr;
+ });
+
+ /**
+ * 表格高度样式
+ */
+ tableHeight = computed(() => {
+ let availableHeight = 0;
+ const parentHtmlElement = this.instance?.getHtmlElement()?.parentElement;
+ if (parentHtmlElement) {
+ availableHeight = Math.floor(parentHtmlElement.clientHeight);
+ }
+
+ availableHeight -= this.table?.store.location.topHeight || 0;
+ availableHeight -= this.table?.store.location.bottomHeight || 0;
+ availableHeight -= this.table?.store.spaceHeight || 0;
+
+ const style = {
+ height: this.props.height > 0 ? this.props.height + 'px' : availableHeight > 0 ? availableHeight + 'px' : '0px',
+ };
+ if (this.table?.store.resizeFlag) {
+ return style;
+ }
+ return style;
+ });
+
+ /**
+ * 用户可选分页大小配置
+ */
+ rowsPerPageOptions = computed(() => {
+ if (this.props.pageable && !this.props.localMode && !this.props.tree && this.table.store.location.middleWidth > 600) {
+ return this.table.store.pagination.rowsPerPageOptions;
+ }
+ return [];
+ });
+
+ /**
+ * 表格作为form中的字段使用时是否显示
+ */
+ showIf = computed(() => {
+ if (typeof this.props.showIf === 'function') {
+ return this.props.showIf({
+ value: null,
+ form: this.props.form,
+ });
+ } else {
+ return this.props.showIf;
+ }
+ });
+
+ setInstance(instance: any, tools?: GridTools): void {
+ this.instance = instance;
+ this.tools = tools;
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/constant/Constant.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/constant/Constant.ts
new file mode 100644
index 00000000..c8fd1821
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/constant/Constant.ts
@@ -0,0 +1,58 @@
+import { SelectMode } from './src/SelectMode';
+import { DndMode } from './src/DndMode';
+import { GroupMode } from './src/GroupMode';
+import { GroupStartOpen } from './src/GroupStartOpen';
+
+import { FormStatus } from './src/FormStatus';
+import { EditStatus } from './src/EditStatus';
+
+import { FieldNames } from './src/FieldNames';
+import { CriteriaOperator } from './src/CriteriaOperator';
+
+/**
+ * w-grid 常量
+ */
+export class Constant {
+ /**
+ * 选择模式
+ */
+ static SELECT_MODE = SelectMode;
+
+ /**
+ * 拖拽排序模式
+ */
+ static DND_MODE = DndMode;
+ /**
+ * 拖拽过程中透明背景图
+ */
+ static DND_BG_IMAGE = `data:image/svg+xml,%3Csvg %3E%3Cpath /%3E%3C/svg%3E`;
+
+ /**
+ * 数据分组模式
+ */
+ static GROUP_MODE = GroupMode;
+ /**
+ * 独立分组模式下默认展开的记录
+ */
+ static GROUP_START_OPEN = GroupStartOpen;
+
+ /**
+ * Form表单状态
+ */
+ static FORM_STATUS = FormStatus;
+
+ /**
+ * 内联编辑状态
+ */
+ static EDIT_STATUS = EditStatus;
+
+ /**
+ * 行数据字段名称定义
+ */
+ static FIELD_NAMES = FieldNames;
+
+ /**
+ * criteria查询支持的操作
+ */
+ static CRITERIA_OPERATOR = CriteriaOperator;
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/constant/src/CriteriaOperator.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/constant/src/CriteriaOperator.ts
new file mode 100644
index 00000000..3c900059
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/constant/src/CriteriaOperator.ts
@@ -0,0 +1,85 @@
+/**
+ * criteria查询支持的操作
+ */
+export class CriteriaOperator {
+ /**
+ * value is null or value=''
+ */
+ static isBlank = 'isBlank';
+ /**
+ * value is not null && value<>''
+ */
+ static notBlank = 'notBlank';
+ /**
+ * value is null
+ */
+ static isNull = 'isNull';
+ /**
+ * value is not null
+ */
+ static notNull = 'notNull';
+ /**
+ * =
+ */
+ static equals = 'equals';
+ /**
+ * <>
+ */
+ static notEqual = 'notEqual';
+ /**
+ * >
+ */
+ static greaterThan = 'greaterThan';
+ /**
+ * >=
+ */
+ static greaterOrEqual = 'greaterOrEqual';
+ /**
+ * <
+ */
+ static lessThan = 'lessThan';
+ /**
+ * <=
+ */
+ static lessOrEqual = 'lessOrEqual';
+ /**
+ * like %xxx%
+ */
+ static contains = 'contains';
+ /**
+ * not like %xxx%
+ */
+ static notContains = 'notContains';
+ /**
+ * like xxx%
+ */
+ static startsWith = 'startsWith';
+ /**
+ * not like xxx%
+ */
+ static notStartsWith = 'notStartsWith';
+ /**
+ * like %xxx
+ */
+ static endsWith = 'endsWith';
+ /**
+ * not like %xxx
+ */
+ static notEndsWith = 'notEndsWith';
+ /**
+ * min [],//被修改后的id集合
+ * }
+ */
+ public async beforeRemove(ids: any) {
+ return await this._beforeRemove.execute(ids);
+ }
+
+ /**
+ * 内置删除功能提交完成后触发事件
+ * @param ids 提交成功的id集合
+ */
+ public afterRemove(ids: any) {
+ this._afterRemove.execute(ids);
+ }
+
+ /**
+ * 内置编辑窗口打开后触发事件
+ * @param data
+ */
+ public afterEditorOpen(data: any) {
+ this._afterEditorOpen.execute(data);
+ }
+
+ setInstance(instance: any, tools: GridTools) {
+ this._rowClick.setInstance(instance, tools);
+ this._rowDbClick.setInstance(instance, tools);
+ this._aftetDnd.setInstance(instance, tools);
+ this._updateTicked.setInstance(instance, tools);
+ this._updateTickeds.setInstance(instance, tools);
+ this._beforeRequestData.setInstance(instance, tools);
+ this._afterRequestData.setInstance(instance, tools);
+ this._beforeEditorDataSubmit.setInstance(instance, tools);
+ this._afterEditorDataSubmit.setInstance(instance, tools);
+ this._beforeRemove.setInstance(instance, tools);
+ this._afterRemove.setInstance(instance, tools);
+ this._afterEditorOpen.setInstance(instance, tools);
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/AfterDragAndDrop.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/AfterDragAndDrop.ts
new file mode 100644
index 00000000..79fd62fe
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/AfterDragAndDrop.ts
@@ -0,0 +1,18 @@
+import { Base } from '../../Base';
+import { PropsType, TableType } from '../../index';
+
+/**
+ * w-grid 行拖拽排序后触发事件
+ */
+export class AfterDragAndDrop extends Base {
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.execute = this.execute.bind(this);
+ }
+
+ static NAME = 'afterDragAndDrop';
+
+ public execute(updateData: any) {
+ this.instance.emit(AfterDragAndDrop.NAME, { grid: this.instance, data: updateData });
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/AfterEditorDataSubmit.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/AfterEditorDataSubmit.ts
new file mode 100644
index 00000000..34090b1d
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/AfterEditorDataSubmit.ts
@@ -0,0 +1,18 @@
+import { Base } from '../../Base';
+import { PropsType, TableType } from '../../index';
+
+/**
+ * w-grid 内置编辑窗口数据提交完成后触发事件
+ */
+export class AfterEditorDataSubmit extends Base {
+ static NAME = 'afterEditorDataSubmit';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.execute = this.execute.bind(this);
+ }
+
+ public execute(data: any) {
+ this.instance.emit(AfterEditorDataSubmit.NAME, { grid: this.instance, data });
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/AfterEditorOpen.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/AfterEditorOpen.ts
new file mode 100644
index 00000000..3342ba92
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/AfterEditorOpen.ts
@@ -0,0 +1,18 @@
+import { Base } from '../../Base';
+import { PropsType, TableType } from '../../index';
+
+/**
+ * w-grid 内置编辑窗口打开之后触发事件
+ */
+export class AfterEditorOpen extends Base {
+ static NAME = 'afterEditorOpen';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.execute = this.execute.bind(this);
+ }
+
+ public execute(data: any) {
+ this.instance.emit(AfterEditorOpen.NAME, { grid: this.instance, data });
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/AfterRemove.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/AfterRemove.ts
new file mode 100644
index 00000000..2bb6b3d1
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/AfterRemove.ts
@@ -0,0 +1,18 @@
+import { Base } from '../../Base';
+import { PropsType, TableType } from '../../index';
+
+/**
+ * w-grid 内置删除功能提交完成后触发事件
+ */
+export class AfterRemove extends Base {
+ static NAME = 'afterRemove';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.execute = this.execute.bind(this);
+ }
+
+ public execute(ids: any) {
+ this.instance.emit(AfterRemove.NAME, { grid: this.instance, ids });
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/AfterRequestData.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/AfterRequestData.ts
new file mode 100644
index 00000000..ada5dbfc
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/AfterRequestData.ts
@@ -0,0 +1,18 @@
+import { Base } from '../../Base';
+import { PropsType, TableType } from '../../index';
+
+/**
+ * w-grid 数据请求完成后触发事件
+ */
+export class AfterRequestData extends Base {
+ static NAME = 'afterRequestData';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.execute = this.execute.bind(this);
+ }
+
+ public execute(rows: any) {
+ this.instance.emit(AfterRequestData.NAME, { grid: this.instance, rows });
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/BeforeEditorDataSubmit.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/BeforeEditorDataSubmit.ts
new file mode 100644
index 00000000..769ecb9c
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/BeforeEditorDataSubmit.ts
@@ -0,0 +1,35 @@
+import { Base } from '../../Base';
+import { PropsType, TableType } from '../../index';
+
+/**
+ * w-grid 内置编辑窗口数据提交前触发事件
+ */
+export class BeforeEditorDataSubmit extends Base {
+ static NAME = 'beforeEditorDataSubmit';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.execute = this.execute.bind(this);
+ }
+
+ public execute(data: any): any {
+ const result = {
+ data: data,
+ submit: true,
+ closeDialog: true,
+ };
+ this.instance.emit(BeforeEditorDataSubmit.NAME, {
+ grid: this.instance,
+ data: data,
+ callback: (_data: any, closeFlag: boolean = true) => {
+ if (typeof _data === 'boolean' && _data === false) {
+ result.submit = false;
+ } else {
+ result.data = _data;
+ }
+ result.closeDialog = closeFlag;
+ },
+ });
+ return result;
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/BeforeRemove.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/BeforeRemove.ts
new file mode 100644
index 00000000..c13992af
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/BeforeRemove.ts
@@ -0,0 +1,33 @@
+import { Base } from '../../Base';
+import { PropsType, TableType } from '../../index';
+
+/**
+ * w-grid 内置删除功能提交前触发事件
+ */
+export class BeforeRemove extends Base {
+ static NAME = 'beforeRemove';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.execute = this.execute.bind(this);
+ }
+
+ public async execute(ids: any) {
+ const result = {
+ submit: true,
+ ids: [],
+ };
+ await this.instance.emit(BeforeRemove.NAME, {
+ grid: this.instance,
+ ids: ids,
+ callback: (_ids: any) => {
+ if (_ids && Array.isArray(_ids)) {
+ result.ids.push(..._ids);
+ } else {
+ result.submit = false;
+ }
+ },
+ });
+ return result;
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/BeforeRequestData.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/BeforeRequestData.ts
new file mode 100644
index 00000000..85306e90
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/BeforeRequestData.ts
@@ -0,0 +1,25 @@
+import { Base } from '../../Base';
+import { PropsType, TableType } from '../../index';
+/**
+ * w-grid 表格请求数据前事件
+ */
+export class BeforeRequestData extends Base {
+ static NAME = 'beforeRequestData';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.execute = this.execute.bind(this);
+ }
+
+ public async execute(requestParams: any) {
+ let result = undefined;
+ await this.instance.emit(BeforeRequestData.NAME, {
+ grid: this.instance,
+ data: requestParams,
+ callback: (_requestParams: URLSearchParams | any) => {
+ result = _requestParams;
+ },
+ });
+ return result;
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/RowClick.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/RowClick.ts
new file mode 100644
index 00000000..5a75663d
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/RowClick.ts
@@ -0,0 +1,61 @@
+import { Base } from '../../Base';
+import { Constant } from '../../constant/Constant';
+import { PropsType, TableType } from '../../index';
+
+/**
+ * w-grid 行单击事件
+ */
+export class RowClick extends Base {
+ static NAME = 'rowClick';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.execute = this.execute.bind(this);
+ this.handleRowClick = this.handleRowClick.bind(this);
+ this.processRowSelection = this.processRowSelection.bind(this);
+ this.clickExitInlineEdit = this.clickExitInlineEdit.bind(this);
+ this.execClick = this.execClick.bind(this);
+ }
+
+ public execute(evt: any, row: any, index: number) {
+ if (!row) {
+ return;
+ }
+ const selected = this.instance.getSelectedRow();
+ this.handleRowClick(evt, row, index, selected);
+ }
+ private handleRowClick(evt: any, row: any, index: number, selected: any) {
+ if (this.clickExitInlineEdit(row, selected)) {
+ this.tools?.editFM.exitInlineEdit();
+ return;
+ }
+ if (this.execClick()) {
+ this.processRowSelection(evt, row, index);
+ }
+ }
+ private processRowSelection(evt: any, row: any, index: number) {
+ if (!evt.ctrlKey) {
+ this.instance.clearSelected();
+ if (!this.props.tree) {
+ this.instance.clearTicked();
+ }
+ }
+ row[this.props.selectedField] = true;
+ if (!this.props.tree) {
+ row[this.props.tickedField] = true;
+ this.tools?.opFM.resetHeaderCheckbox();
+ }
+ this.instance.emit(RowClick.NAME, { grid: this.instance, evt, row, index });
+ }
+ private clickExitInlineEdit(row: any, selected: any) {
+ return (
+ this.props.localMode &&
+ this.table?.store.inlineEditStatus !== Constant.EDIT_STATUS.NONE &&
+ this.props.selectMode === Constant.SELECT_MODE.ROW &&
+ row[Constant.FIELD_NAMES.ROW_KEY] !== selected[Constant.FIELD_NAMES.ROW_KEY]
+ );
+ }
+ private execClick() {
+ return this.table?.store.inlineEditStatus === Constant.EDIT_STATUS.NONE && this.props.selectMode !== Constant.SELECT_MODE.NONE;
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/RowDbClick.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/RowDbClick.ts
new file mode 100644
index 00000000..d7d20b44
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/RowDbClick.ts
@@ -0,0 +1,27 @@
+import { Constant } from '../../constant/Constant';
+import { Base } from '../../Base';
+import { PropsType, TableType } from '../../index';
+
+/**
+ * w-grid 行双击事件
+ */
+export class RowDbClick extends Base {
+ static NAME = 'rowDbClick';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.execute = this.execute.bind(this);
+ }
+
+ public execute(evt: any, row: any, index: number) {
+ if (this.table?.store.inlineEditStatus === Constant.EDIT_STATUS.NONE && this.props.selectMode !== Constant.SELECT_MODE.NONE) {
+ if (this.props.onRowDbClick) {
+ this.instance.emit(RowDbClick.NAME, { grid: this.instance, evt, row, index });
+ } else if (this.props.dbClickOperation === 'view') {
+ this.tools?.apiFM.operator.viewData();
+ } else if (this.props.dbClickOperation !== 'none') {
+ this.table.componentRef.getTopRef()?.dbClickOperation(row);
+ }
+ }
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/UpdateTicked.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/UpdateTicked.ts
new file mode 100644
index 00000000..854a9434
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/UpdateTicked.ts
@@ -0,0 +1,33 @@
+import { Constant } from '../../constant/Constant';
+import { Base } from '../../Base';
+import { PropsType, TableType } from '../../index';
+
+/**
+ * w-grid 行数据checkbox勾选状态改变事件
+ */
+export class UpdateTicked extends Base {
+ static NAME = 'updateTicked';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.execute = this.execute.bind(this);
+ }
+
+ public execute(evt: Event, row: any) {
+ const selectedField = this.props.selectedField;
+ const tickedField = this.props.tickedField;
+ if (this.table?.store.inlineEditStatus === Constant.EDIT_STATUS.NONE) {
+ row[selectedField] = row[tickedField];
+ this.tools?.opFM.resetHeaderCheckbox();
+ if (!row[tickedField]) {
+ // 取消选中时将选中的单元格也清空
+ this.table.store.cellSelected = {};
+ }
+ if (this.props.onUpdateTicked) {
+ this.instance.emit(UpdateTicked.NAME, { grid: this.instance, evt, row });
+ }
+ } else {
+ row[tickedField] = !row[tickedField];
+ }
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/UpdateTickeds.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/UpdateTickeds.ts
new file mode 100644
index 00000000..c18e350e
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/event/src/UpdateTickeds.ts
@@ -0,0 +1,38 @@
+import { Constant } from '../../constant/Constant';
+import { Base } from '../../Base';
+import { PropsType, TableType } from '../../index';
+
+/**
+ * w-grid 列头全部勾选checkbox状态改变事件
+ */
+export class UpdateTickeds extends Base {
+ static NAME = 'updateTickeds';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.execute = this.execute.bind(this);
+ }
+
+ public execute(value: boolean | null) {
+ const selectedField = this.props.selectedField;
+ const tickedField = this.props.tickedField;
+ if (this.table?.store.inlineEditStatus === Constant.EDIT_STATUS.NONE) {
+ if (value) {
+ this.table.rows.forEach((item) => {
+ item[tickedField] = true;
+ item[selectedField] = true;
+ });
+ } else {
+ this.table.rows.forEach((item) => {
+ item[tickedField] = false;
+ item[selectedField] = false;
+ });
+ }
+ } else if (this.table?.store.inlineEditStatus === Constant.EDIT_STATUS.ROW || this.table?.store.inlineEditStatus === Constant.EDIT_STATUS.CELL) {
+ this.table.store.headerTicked = null;
+ } else if (this.table?.store.inlineEditStatus === Constant.EDIT_STATUS.ROWS) {
+ this.table.store.headerTicked = false;
+ }
+ this.instance.emit(UpdateTickeds.NAME, { grid: this.instance, value: value });
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/ExposeApiManager.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/ExposeApiManager.ts
new file mode 100644
index 00000000..01e91c3d
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/ExposeApiManager.ts
@@ -0,0 +1,134 @@
+import { PropsType } from '../types/PropsType';
+
+import { GridTools } from '../GridTools';
+import { Button } from './src/Button';
+import { ComponentRef } from './src/ComponentRef';
+import { GetData } from './src/GetData';
+import { LocalMode } from './src/LocalMode';
+import { Operator } from './src/Operator';
+import { ResetProperty } from './src/ResetProperty';
+import { TableType } from '../types/TableType';
+
+/**
+ * w-grid 对外暴露API管理器
+ */
+export class ExposeApiManager {
+ /**
+ * 获取表格数据相关的API
+ */
+ getData: GetData;
+ /**
+ * 获取内置组件ref相关的API
+ */
+ componentRef: ComponentRef;
+ /**
+ * 表格操作相关的API
+ */
+ operator: Operator;
+ /**
+ * 本地模式API
+ */
+ localMode: LocalMode;
+ /**
+ * 重新设置表格属性API
+ */
+ resetProperty: ResetProperty;
+ /**
+ * 内置按钮API
+ */
+ button: Button;
+
+ constructor(props: PropsType, table: TableType) {
+ this.getData = new GetData(props, table);
+ this.componentRef = new ComponentRef(props, table);
+ this.operator = new Operator(props, table);
+ this.localMode = new LocalMode(props, table);
+ this.resetProperty = new ResetProperty(props, table);
+ this.button = new Button(props, table);
+ }
+
+ /**
+ * 创建暴露给用户使用的API
+ * @param tools
+ * @returns
+ */
+ static createExpose(tools: GridTools) {
+ return {
+ // 获取数据api
+ getRows: tools.apiFM.getData.getRows,
+ getSelectedRow: tools.apiFM.getData.getSelectedRow,
+ getSelectedRows: tools.apiFM.getData.getSelectedRows,
+ getTickedRow: tools.apiFM.getData.getTickedRow,
+ getTickedRows: tools.apiFM.getData.getTickedRows,
+ getCascadeChildren: tools.apiFM.getData.getCascadeChildren,
+ getCascadeParents: tools.apiFM.getData.getCascadeParents,
+ getSelectedCell: tools.apiFM.getData.getSelectedCell,
+
+ // 获取子组件ref的api
+ getQueryForm: tools.apiFM.componentRef.getQueryForm,
+ getEditorDialog: tools.apiFM.componentRef.getEditorDialog,
+ getCellEditorDialog: tools.apiFM.componentRef.getCellEditorDialog,
+ getEditorForm: tools.apiFM.componentRef.getEditorForm,
+ getCellEditorForm: tools.apiFM.componentRef.getCellEditorForm,
+ getViewerDrawer: tools.apiFM.componentRef.getViewerDrawer,
+ getViewerPanel: tools.apiFM.componentRef.getViewerPanel,
+
+ // 表格操作api
+ setSelected: tools.apiFM.operator.setSelected,
+ setTicked: tools.apiFM.operator.setTicked,
+ clearSelected: tools.apiFM.operator.clearSelected,
+ clearTicked: tools.apiFM.operator.clearTicked,
+ expand: tools.apiFM.operator.expand,
+ collapse: tools.apiFM.operator.collapse,
+
+ // 本地模式api
+ setLocalData: tools.apiFM.localMode.setLocalData,
+ addLocalData: tools.apiFM.localMode.addLocalData,
+ updateLocalData: tools.apiFM.localMode.updateLocalData,
+ removeLocalData: tools.apiFM.localMode.removeLocalData,
+
+ // 重新设置或覆盖现有表格属性配置api
+ setQueryCriteria: tools.apiFM.resetProperty.setQueryCriteria,
+ setQueryCriteriaFieldValue: tools.apiFM.resetProperty.setQueryCriteriaFieldValue,
+ setDataUrl: tools.apiFM.resetProperty.setDataUrl,
+ setFetchDataUrl: tools.apiFM.resetProperty.setFetchDataUrl,
+ setAddDataUrl: tools.apiFM.resetProperty.setAddDataUrl,
+ setEditDataUrl: tools.apiFM.resetProperty.setEditDataUrl,
+ setRemoveDataUrl: tools.apiFM.resetProperty.setRemoveDataUrl,
+
+ // 内置按钮api
+ query: tools.apiFM.button.query,
+ moreQuery: tools.apiFM.button.moreQuery,
+ reset: tools.apiFM.button.reset,
+ refresh: tools.apiFM.button.refresh,
+ add: tools.apiFM.button.add,
+ addTop: tools.apiFM.button.addTop,
+ addChild: tools.apiFM.button.addChild,
+ edit: tools.apiFM.button.edit,
+ cellEdit: tools.apiFM.button.cellEdit,
+ inlineCellEdit: tools.apiFM.button.inlineCellEdit,
+ inlineRowEdit: tools.apiFM.button.inlineRowEdit,
+ inlineRowsEdit: tools.apiFM.button.inlineRowsEdit,
+ clone: tools.apiFM.button.clone,
+ remove: tools.apiFM.button.remove,
+ view: tools.apiFM.button.view,
+ export: tools.apiFM.button.exportData,
+ resetDefaultValues: tools.apiFM.button.resetDefaultValues,
+
+ // =========其他API=========
+ refreshStyle: tools.opFM.resetStyleVariableValue,
+ getHtmlElement: () => {
+ return tools.table.componentRef.getTableRef()?.$el;
+ },
+ };
+ }
+
+ // 设置组件实例
+ setInstance(instance: any, tools: GridTools) {
+ this.getData.setInstance(instance, tools);
+ this.componentRef.setInstance(instance, tools);
+ this.operator.setInstance(instance, tools);
+ this.localMode.setInstance(instance, tools);
+ this.resetProperty.setInstance(instance, tools);
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/Button.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/Button.ts
new file mode 100644
index 00000000..807845a0
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/Button.ts
@@ -0,0 +1,133 @@
+import { Tools } from '@/platform';
+import { toRaw } from 'vue';
+import { Base } from '../../Base';
+import { Constant, PropsType, TableType } from '../../index';
+
+/**
+ * w-grid 内置按钮API
+ */
+export class Button extends Base {
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.getTopRef = this.getTopRef.bind(this);
+ this.getActionArgs = this.getActionArgs.bind(this);
+ this.query = this.query.bind(this);
+ this.moreQuery = this.moreQuery.bind(this);
+ this.reset = this.reset.bind(this);
+ this.refresh = this.refresh.bind(this);
+ this.add = this.add.bind(this);
+ this.addTop = this.addTop.bind(this);
+ this.addChild = this.addChild.bind(this);
+ this.edit = this.edit.bind(this);
+ this.cellEdit = this.cellEdit.bind(this);
+ this.inlineCellEdit = this.inlineCellEdit.bind(this);
+ this.inlineRowEdit = this.inlineRowEdit.bind(this);
+ this.inlineRowsEdit = this.inlineRowsEdit.bind(this);
+ this.clone = this.clone.bind(this);
+ this.remove = this.remove.bind(this);
+ this.view = this.view.bind(this);
+ this.exportData = this.exportData.bind(this);
+ this.resetDefaultValues = this.resetDefaultValues.bind(this);
+ this.beforeCellEdit = this.beforeCellEdit.bind(this);
+ }
+
+ private getTopRef() {
+ return this.table.componentRef.getTopRef();
+ }
+ private getActionArgs() {
+ return this.tools?.cm.actionArgs.value;
+ }
+
+ query() {
+ this.tools?.bm.query.click(this.getActionArgs());
+ }
+ moreQuery() {
+ this.tools?.bm.moreQuery.click(this.getActionArgs());
+ }
+ reset() {
+ this.tools?.bm.reset.click(this.getActionArgs());
+ }
+ refresh() {
+ this.tools?.bm.refresh.click(this.getActionArgs());
+ }
+ add() {
+ this.tools?.bm.add.click(this.getActionArgs());
+ }
+ addTop() {
+ this.tools?.bm.addTop.click(this.getActionArgs());
+ }
+ addChild(target: any) {
+ if (Tools.isEmpty(target)) {
+ throw new Error('[w-grid] The `addChild` method parameter cannot be null.');
+ }
+ this.tools?.apiFM.operator.setSelected(target);
+ this.tools?.bm.addChild.click(this.getActionArgs());
+ }
+ edit(target: any) {
+ if (Tools.isEmpty(target)) {
+ throw new Error('[w-grid] The `edit` method parameter cannot be null.');
+ }
+ this.tools?.apiFM.operator.setSelected(target);
+ this.tools?.bm.edit.click(this.getActionArgs());
+ }
+ cellEdit(targetRow: any, targetColName: string) {
+ this.beforeCellEdit(targetRow, targetColName);
+ this.tools?.bm.cellEdit.click(this.getActionArgs());
+ }
+ inlineCellEdit(targetRow: any, targetColName: string) {
+ this.beforeCellEdit(targetRow, targetColName);
+ this.tools?.bm.inlineCellEdit.click(this.getActionArgs());
+ }
+ inlineRowEdit(target: any) {
+ this.tools?.apiFM.operator.setSelected(target);
+ this.tools?.bm.inlineRowEdit.click(this.getActionArgs());
+ }
+ inlineRowsEdit() {
+ this.tools?.bm.inlineRowsEdit.click(this.getActionArgs());
+ }
+ clone(target: any) {
+ this.tools?.apiFM.operator.setSelected(target);
+ this.tools?.bm.clone.click(this.getActionArgs());
+ }
+ remove(target: any) {
+ this.tools?.apiFM.operator.setSelected(target);
+ this.getTopRef()?.remove(this.getActionArgs());
+ }
+ view(target: any) {
+ this.tools?.apiFM.operator.setSelected(target);
+ this.tools?.bm.view.click(this.getActionArgs());
+ }
+ exportData() {
+ this.tools?.bm.expand.click(this.getActionArgs());
+ }
+ resetDefaultValues() {
+ this.tools?.bm.resetDefaultValues.click(this.getActionArgs());
+ }
+
+ private beforeCellEdit(targetRow: any, targetColName: string) {
+ if (!this.table) {
+ return;
+ }
+ if (Tools.isEmpty(targetRow)) {
+ throw new Error('[w-grid] The `targetRow` parameter of the `cellEdit` method cannot be null.');
+ }
+ if (Tools.isEmpty(targetColName)) {
+ throw new Error('[w-grid] The `targetColName` parameter of the `cellEdit` method cannot be null.');
+ }
+ const result = this.tools?.dataFM.getRowsByTarget(this.table.rows, targetRow);
+ if (!result || result.length === 0) {
+ throw new Error('[w-grid] The `targetRow` does not exist in the table data.');
+ }
+ const row = result[0];
+ if (!row[targetColName]) {
+ throw new Error('[w-grid] The `targetColName` does not exist in the table data.');
+ }
+ this.table.store.cellSelected = {
+ row: toRaw(row),
+ rowKey: row[Constant.FIELD_NAMES.ROW_KEY],
+ primaryKey: row[this.props.primaryKey],
+ colName: targetColName,
+ value: row[targetColName],
+ };
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/ComponentRef.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/ComponentRef.ts
new file mode 100644
index 00000000..760ab7f5
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/ComponentRef.ts
@@ -0,0 +1,40 @@
+import { Base } from '../../Base';
+import { PropsType, TableType } from '../../index';
+
+/**
+ * w-grid 获取内置组件ref对象API
+ */
+export class ComponentRef extends Base {
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.getQueryForm = this.getQueryForm.bind(this);
+ this.getEditorDialog = this.getEditorDialog.bind(this);
+ this.getCellEditorDialog = this.getCellEditorDialog.bind(this);
+ this.getEditorForm = this.getEditorForm.bind(this);
+ this.getCellEditorForm = this.getCellEditorForm.bind(this);
+ this.getViewerDrawer = this.getViewerDrawer.bind(this);
+ this.getViewerPanel = this.getViewerPanel.bind(this);
+ }
+
+ getQueryForm() {
+ return this.table.componentRef.getTopRef().getQueryForm();
+ }
+ getEditorDialog() {
+ return this.table.componentRef.getEditorRef()?.getDialog();
+ }
+ getCellEditorDialog() {
+ return this.table.componentRef.getCellEditorRef()?.getDialog();
+ }
+ getEditorForm() {
+ return this.table.componentRef.getEditorRef()?.getForm();
+ }
+ getCellEditorForm() {
+ return this.table.componentRef.getCellEditorRef()?.getForm();
+ }
+ getViewerDrawer() {
+ return this.table.componentRef.getViewRef()?.getViewerDrawer();
+ }
+ getViewerPanel() {
+ return this.table.componentRef.getViewRef()?.getInfoPanel();
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/GetData.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/GetData.ts
new file mode 100644
index 00000000..e71651be
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/GetData.ts
@@ -0,0 +1,182 @@
+import { toRaw } from 'vue';
+import { Base } from '../../Base';
+import { PropsType, TableType } from '../../index';
+
+/**
+ * w-grid 获取表格数据相关API
+ */
+export class GetData extends Base {
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.getRows = this.getRows.bind(this);
+ this.getSelectedRow = this.getSelectedRow.bind(this);
+ this.getSelectedRows = this.getSelectedRows.bind(this);
+ this.getTickedRow = this.getTickedRow.bind(this);
+ this.getTickedRows = this.getTickedRows.bind(this);
+ this.getSelectedCell = this.getSelectedCell.bind(this);
+ this.getCascadeChildren = this.getCascadeChildren.bind(this);
+ this.getCascadeParents = this.getCascadeParents.bind(this);
+ this.getSelectRowsByFieldName = this.getSelectRowsByFieldName.bind(this);
+ this.getCascadeChildrenData = this.getCascadeChildrenData.bind(this);
+ this.getCascadeParentsData = this.getCascadeParentsData.bind(this);
+ }
+
+ /**
+ * 获取表格所有数据
+ */
+ getRows() {
+ return toRaw(this.table?.rows);
+ }
+
+ /**
+ * 获取单条选中记录
+ * @return 行数据对象
+ */
+ getSelectedRow() {
+ const selectedRows = [];
+ if (!this.table) {
+ return selectedRows;
+ }
+ this.getSelectRowsByFieldName(this.table.rows, selectedRows, this.props.selectedField);
+ if (selectedRows && selectedRows.length > 0) {
+ return toRaw(selectedRows)[0];
+ } else {
+ return undefined;
+ }
+ }
+
+ /**
+ * 获取所有选中记录
+ * @return 行数据对象数组
+ */
+ getSelectedRows() {
+ const selectedRows = [];
+ if (!this.table) {
+ return selectedRows;
+ }
+ this.getSelectRowsByFieldName(this.table.rows, selectedRows, this.props.selectedField);
+ return toRaw(selectedRows);
+ }
+
+ /**
+ * 获得单条checkbox勾选的记录
+ * @param containsNullStatus 是否包含半勾选状态的记录
+ * @return 行数据对象
+ */
+ getTickedRow = (containsNullStatus: boolean = false) => {
+ const tickedRows = [];
+ if (!this.table) {
+ return tickedRows;
+ }
+ this.getSelectRowsByFieldName(this.table.rows, tickedRows, this.props.tickedField, containsNullStatus);
+ if (tickedRows && tickedRows.length > 0) {
+ return toRaw(tickedRows)[0];
+ } else {
+ return undefined;
+ }
+ };
+
+ /**
+ * 获得checkbox勾选的所有记录
+ * @param containsNullStatus 是否包含半勾选状态的记录
+ * return 数组
+ */
+ getTickedRows(containsNullStatus: boolean = false) {
+ const tickedRows = [];
+ if (!this.table) {
+ return tickedRows;
+ }
+ this.getSelectRowsByFieldName(this.table.rows, tickedRows, this.props.tickedField, containsNullStatus);
+ return toRaw(tickedRows);
+ }
+
+ /**
+ * 获得选中的单元格
+ */
+ getSelectedCell() {
+ return {
+ row: this.table.store.cellSelected?.row,
+ colName: this.table.store.cellSelected?.colName,
+ value: this.table.store.cellSelected?.value,
+ };
+ }
+
+ /**
+ * 获取选择的行记录及所有子记录
+ * @param fieldName 属性名称
+ * @return 若传入了属性名称,则返回属性对应的数据数组
+ * 若没有传入属性名称,返回行数据对象数组
+ */
+ getCascadeChildren(fieldName: string) {
+ const row = this.getSelectedRow();
+ const rows = row ? [row] : [];
+ return this.getCascadeChildrenData(rows, fieldName);
+ }
+
+ /**
+ * 获取选中的行记录及所有父记录
+ * @param fieldName 属性名称
+ * @return 若传入了属性名称,则返回属性对应的数据数组
+ * 若没有传入属性名称,返回行数据对象数组
+ */
+ getCascadeParents(fieldName: string) {
+ const row = this.getSelectedRow();
+ if (row) {
+ const parent = this.getCascadeParentsData(row, fieldName);
+ const result = [fieldName ? row[fieldName] : row];
+ if (parent && parent.length > 0) {
+ result.push(...parent);
+ }
+ return result;
+ }
+ return undefined;
+ }
+
+ private getSelectRowsByFieldName(arr: Array, selectedRows: Array, fieldName: string, containsNullStatus: boolean = false) {
+ arr.forEach((item) => {
+ if (containsNullStatus && (item[fieldName] || item[fieldName] === null)) {
+ selectedRows.push(item);
+ } else if (item[fieldName]) {
+ selectedRows.push(item);
+ }
+ if (this.props.tree && item.children && item.children.length > 0) {
+ this.getSelectRowsByFieldName(item.children, selectedRows, fieldName);
+ }
+ });
+ }
+
+ private getCascadeChildrenData(rows: Array, fieldName: string) {
+ const data = [];
+ if (rows && rows.length > 0) {
+ rows.forEach((item: any, index) => {
+ data.push(fieldName ? item[fieldName] : item);
+ if (this.props.tree && item.children && item.children.length > 0) {
+ const childrenData = this.getCascadeChildrenData(item.children, fieldName);
+ data.push(...childrenData);
+ }
+ });
+ }
+ return data;
+ }
+
+ /**
+ * 获得所有父节点数据
+ * @param row 行数据对象
+ * @param fieldName 字段名称,为空时返回父节点对象,否则返回字段值
+ * @returns
+ */
+ public getCascadeParentsData(row: any, fieldName: string | null | undefined) {
+ const data = [];
+ if (row && row[this.props.foreignKey]) {
+ const parent = this.tools?.dataFM.getRow(this.table.rows, row[this.props.foreignKey], true);
+ if (parent) {
+ data.push(fieldName ? parent[fieldName] : parent);
+ if (parent[this.props.foreignKey]) {
+ const parentData = this.getCascadeParentsData(parent, fieldName);
+ data.push(...parentData);
+ }
+ }
+ }
+ return data;
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/LocalMode.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/LocalMode.ts
new file mode 100644
index 00000000..eb228b6c
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/LocalMode.ts
@@ -0,0 +1,209 @@
+import { Tools, TreeBuilder } from '@/platform';
+import { Base } from '../../Base';
+import { Constant, PropsType, TableType } from '../../index';
+
+/**
+ * w-grid 表格本地模式API
+ */
+export class LocalMode extends Base {
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.setLocalData = this.setLocalData.bind(this);
+ this.addLocalData = this.addLocalData.bind(this);
+ this.updateLocalData = this.updateLocalData.bind(this);
+ this.removeLocalData = this.removeLocalData.bind(this);
+
+ this.treeAddLocalData = this.treeAddLocalData.bind(this);
+ this.updateLocalDataError = this.updateLocalDataError.bind(this);
+ this.replaceRowHandler = this.replaceRowHandler.bind(this);
+ this.removeTreeRows = this.removeTreeRows.bind(this);
+ }
+
+ /**
+ * 设置本地记录
+ * @param rows 行数据对象数组
+ * @return 无返回
+ */
+ setLocalData(rows: Array) {
+ if (rows && Array.isArray(rows)) {
+ if (this.props.tree && this.props.treeRelationship === 'parent') {
+ const treeRows = TreeBuilder.build(rows, this.props.foreignKey, this.props.primaryKey);
+ this.table.rows = treeRows;
+ } else {
+ this.table.rows = rows;
+ }
+ this.tools?.dataFM.setExtraProperty(this.table.rows, undefined, true);
+ this.table.store.pagination.rowsNumber = this.table.rows.length;
+ this.tools?.opFM.resetStyleVariableValue();
+ }
+ }
+
+ /**
+ * 新增本地记录
+ * @param data 行数据对象或行数据对象数组
+ * @param index 增加的下标,可不传,默认增加到最后
+ * @return 无返回
+ */
+ addLocalData(data: any, index?: number) {
+ let success = false;
+ if (this.props.tree) {
+ success = this.treeAddLocalData(data);
+ } else {
+ if (data && Array.isArray(data) && data.length > 0) {
+ data.forEach((row) => {
+ if (!index || index >= this.table.rows.length) {
+ this.table.rows.push(row);
+ } else {
+ this.table.rows.splice(index, 0, row);
+ }
+ });
+ success = true;
+ } else if (data) {
+ if (!index || index >= this.table.rows.length) {
+ this.table.rows.push(data);
+ } else {
+ this.table.rows.splice(index, 0, data);
+ }
+ success = true;
+ }
+ }
+ if (success) {
+ this.tools?.dataFM.setExtraProperty(this.table.rows);
+ this.table.store.pagination.rowsNumber = this.table.rows.length;
+ this.tools?.opFM.resetStyleVariableValue();
+ }
+ }
+
+ /**
+ * 修改本地记录
+ * @param data 行数据对象或行数据对象数组
+ * @param fieldName 默认根据数据行主键或者前端行主键进行修改,若传入列名称则根据传入的列名称进行匹配修改。
+ * @return 无返回
+ */
+ updateLocalData(data: any, fieldName: string = '') {
+ if (data && Array.isArray(data)) {
+ data.forEach((item) => {
+ this.updateLocalDataError(fieldName, item);
+ this.replaceRowHandler(this.table.rows, item, fieldName);
+ });
+ } else if (data) {
+ this.updateLocalDataError(fieldName, data);
+ this.replaceRowHandler(this.table.rows, data, fieldName);
+ }
+ }
+
+ /**
+ * 删除本地数据 TODO: 待优化
+ * @param target 可传入单个行对象、行主键、前端行主键,也可传入行对象数组、行主键数组、前端行主键数组
+ * @return 无返回
+ */
+ removeLocalData(target) {
+ const result = this.tools?.dataFM.getRowsByTarget(this.table.rows, target);
+ if (result && result.length > 0) {
+ if (this.props.tree) {
+ result.forEach((item) => {
+ this.removeTreeRows(this.table.rows, item);
+ });
+ this.tools?.dataFM.setExtraProperty(this.table.rows);
+ } else {
+ result.forEach((item) => {
+ this.table.rows.splice(
+ this.table.rows.findIndex((v) => {
+ if (!Tools.isEmpty(v[this.props.primaryKey]) && Tools.isEmpty(item[this.props.primaryKey])) {
+ return v[this.props.primaryKey] === item[this.props.primaryKey];
+ } else {
+ return v[Constant.FIELD_NAMES.ROW_KEY] === item[Constant.FIELD_NAMES.ROW_KEY];
+ }
+ }),
+ 1,
+ );
+ });
+ this.table.store.pagination.rowsNumber = this.table.rows.length;
+ }
+ this.tools?.opFM.resetHeaderCheckbox();
+ this.tools?.opFM.resetStyleVariableValue();
+ }
+ }
+
+ /**
+ * 树表格新增
+ * @param data
+ */
+ private treeAddLocalData(data: any) {
+ let success = false;
+ if (data && Array.isArray(data) && data.length > 0) {
+ data.forEach((row) => {
+ const foreignKey = row[this.props.foreignKey];
+ if (foreignKey) {
+ const parent = this.tools?.dataFM.getRow(this.table.rows, foreignKey, false, this.props.primaryKey);
+ if (parent) {
+ if (parent.children) {
+ parent.children.push(row);
+ } else {
+ parent.children = [row];
+ }
+ } else {
+ this.table.rows.push(row);
+ }
+ } else {
+ this.table.rows.push(row);
+ }
+ });
+ success = true;
+ } else if (data) {
+ const foreignKey = data[this.props.foreignKey];
+ if (foreignKey) {
+ const parent = this.tools?.dataFM.getRow(this.table.rows, foreignKey, false, this.props.primaryKey);
+ if (parent) {
+ if (parent.children) {
+ parent.children.push(data);
+ } else {
+ parent.children = [data];
+ }
+ } else {
+ this.table.rows.push(data);
+ }
+ } else {
+ this.table.rows.push(data);
+ }
+ success = true;
+ }
+ return success;
+ }
+
+ private updateLocalDataError(fieldName: string, data: any) {
+ if (Tools.isEmpty(fieldName) && Tools.isEmpty(data[this.props.primaryKey]) && Tools.isEmpty(data[Constant.FIELD_NAMES.ROW_KEY])) {
+ throw new Error('[w-grid]Data does not contain `primaryKey` or `rowKey`.');
+ }
+ }
+
+ private replaceRowHandler(arr: Array, row: any, fieldName: string) {
+ for (let i = 0; i < arr.length; i++) {
+ if (!Tools.isEmpty(fieldName) && row[fieldName] === arr[i][fieldName]) {
+ // 根据设定的字段进行匹配
+ arr[i] = { ...arr[i], ...row };
+ break;
+ } else if (Tools.isEmpty(fieldName) && !Tools.isEmpty(row[this.props.primaryKey]) && row[this.props.primaryKey] === arr[i][this.props.primaryKey]) {
+ // 未设定根据哪个字段匹配时首先尝试用主键进行匹配。
+ arr[i] = { ...arr[i], ...row };
+ break;
+ } else if (Tools.isEmpty(fieldName) && row[Constant.FIELD_NAMES.ROW_KEY] === arr[i][Constant.FIELD_NAMES.ROW_KEY]) {
+ // 主键匹配失败则尝试用前端唯一键进行匹配
+ arr[i] = { ...arr[i], ...row };
+ break;
+ } else if (this.props.tree && arr[i].children && arr[i].children.length > 0) {
+ this.replaceRowHandler(arr[i].children, row, fieldName);
+ }
+ }
+ }
+
+ private removeTreeRows(arr: Array, row: any) {
+ arr.forEach((item, index) => {
+ if (row[this.props.primaryKey] === item[this.props.primaryKey] || row[Constant.FIELD_NAMES.ROW_KEY] === item[Constant.FIELD_NAMES.ROW_KEY]) {
+ arr.splice(index, 1);
+ } else if (item.children && item.children.length > 0) {
+ this.removeTreeRows(item.children, row);
+ }
+ });
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/Operator.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/Operator.ts
new file mode 100644
index 00000000..74d4c9ba
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/Operator.ts
@@ -0,0 +1,183 @@
+import { nextTick } from 'vue';
+import { Base } from '../../Base';
+import { Constant, PropsType, TableType } from '../../index';
+
+/**
+ * w-grid 表格操作相关API
+ */
+export class Operator extends Base {
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.refreshGrid = this.refreshGrid.bind(this);
+ this.viewData = this.viewData.bind(this);
+ this.setSelected = this.setSelected.bind(this);
+ this.setTicked = this.setTicked.bind(this);
+ this.clearSelected = this.clearSelected.bind(this);
+ this.clearTicked = this.clearTicked.bind(this);
+ this.expand = this.expand.bind(this);
+ this.collapse = this.collapse.bind(this);
+
+ this.processTree = this.processTree.bind(this);
+ this.processGroup = this.processGroup.bind(this);
+ this.setSelectedStatus = this.setSelectedStatus.bind(this);
+ this.setTickedStatus = this.setTickedStatus.bind(this);
+ }
+
+ /**
+ * 刷新表格
+ */
+ refreshGrid() {
+ nextTick(() => {
+ this.tools?.reqApiFM.fetchData({ pagination: this.table.store.pagination });
+ this.table.store.headerTicked = false;
+ });
+ }
+
+ /**
+ * 数据查看
+ */
+ viewData() {
+ this.table.componentRef.getViewRef()?.view();
+ }
+
+ /**
+ * 设置目标行选中
+ * @param target 可传入单个行对象、行主键、前端行主键,也可传入行对象数组、行主键数组、前端行主键数组。
+ * @return 无返回
+ */
+ setSelected(target: any) {
+ if (target) {
+ const result = this.tools?.dataFM.getRowsByTarget(this.table.rows, target);
+ if (result && result.length > 0) {
+ this.setSelectedStatus(result, true);
+ }
+ }
+ }
+
+ /**
+ * 设置目标行checkbox勾选
+ * @param target 可传入单个行对象、行主键、前端行主键,也可传入行对象数组、行主键数组、前端行主键数组。
+ * @return 无返回
+ */
+ setTicked(target: any) {
+ if (target) {
+ const result = this.tools?.dataFM.getRowsByTarget(this.table.rows, target);
+ if (result && result.length > 0) {
+ this.setTickedStatus(result, true);
+ }
+ }
+ }
+
+ /**
+ * 清空目标行选中
+ * @param target 可传入单个行对象、行主键、前端行主键,也可传入行对象数组、行主键数组、前端行主键数组,不传或者为空则清空全部选中状态
+ * @return 无返回
+ */
+ clearSelected(target: any = undefined) {
+ if (target || (Array.isArray(target) && target.length > 0)) {
+ const result = this.tools?.dataFM.getRowsByTarget(this.table.rows, target);
+ if (result && result.length > 0) {
+ this.setSelectedStatus(result, false);
+ }
+ } else if (this.table) {
+ this.setSelectedStatus(this.table?.rows, false);
+ }
+ }
+
+ /**
+ * 清空目标checkbox勾选记录
+ * @param target 可传入单个行对象、行主键、前端行主键,也可传入行对象数组、行主键数组、前端行主键数组,不传或者为空则清空全部选中状态
+ * @return 无返回
+ */
+ clearTicked(target: any = undefined) {
+ if (target || (Array.isArray(target) && target.length > 0)) {
+ const result = this.tools?.dataFM.getRowsByTarget(this.table.rows, target);
+ if (result && result.length > 0) {
+ this.setTickedStatus(result, false);
+ }
+ } else if (this.table) {
+ this.setTickedStatus(this.table.rows, false);
+ }
+ }
+
+ /**
+ * 展开表格行
+ * @param target 树表格模式:可传入单个行对象、行主键、前端行主键,也可传入行对象数组、行主键数组、前端行主键数组,不传或者为空则展开全部
+ * 独立分组模式:可传入当前分组字段的值,不传或者为空则展开全部
+ * @param cascadeChildren 树表格模式:是否级联子,级联模式下所有的子节点也展开,独立分组模式该参数无效。
+ */
+ expand(target: any = undefined, cascadeChildren: boolean = false) {
+ const expand = true;
+ if (this.props.tree) {
+ // 树表格模式
+ this.processTree(target, expand, cascadeChildren);
+ } else {
+ // 独立分组模式
+ this.processGroup(target, expand);
+ }
+ }
+
+ /**
+ * 收起表格行
+ * @param target 树表格模式:可传入单个行对象、行主键、前端行主键,也可传入行对象数组、行主键数组、前端行主键数组,不传或者为空则收起全部
+ * 独立分组模式:可传入当前分组字段的值,不传或者为空则收起全部
+ * @param cascadeParents 树表格模式:是否级联父,级联模式下往上找,将传入数据的父节点收起,独立分组模式该参数无效。
+ */
+ collapse(target: any = undefined, cascadeParents: boolean = false) {
+ const expand = false;
+ if (this.props.tree) {
+ // 树表格模式
+ this.processTree(target, expand, cascadeParents);
+ } else {
+ // 独立分组模式
+ this.processGroup(target, expand);
+ }
+ }
+
+ private processTree(target: any, expand: boolean, cascade: boolean) {
+ const rows = target ? this.tools?.dataFM.getRowsByTarget(this.table.rows, target) : this.table.rows;
+ if (rows) {
+ rows.forEach((item: any) => {
+ item[Constant.FIELD_NAMES.EXPAND] = expand;
+ if (cascade && item.children && item.children.length > 0) {
+ this.tools?.opFM.resetTreeGridExpand(item.children, expand);
+ } else if (!expand) {
+ const parents = this.tools?.apiFM.getData.getCascadeParentsData(item, null);
+ if (parents && parents.length > 0) {
+ this.tools?.opFM.resetTreeGridExpand(parents, expand);
+ }
+ }
+ });
+ }
+ }
+ private processGroup(target: any, expand: boolean) {
+ if (target) {
+ const findResult = this.table.configStore.aloneGroupRecords.find((item) => item.groupName === target);
+ if (!findResult) {
+ console.error('[w-grid] groupRecords not find `' + target + '`.');
+ return;
+ }
+ findResult['expand'] = expand;
+ } else {
+ this.table.configStore.aloneGroupRecords.forEach((item) => {
+ item['expand'] = expand;
+ });
+ }
+ }
+ private setSelectedStatus(arr: Array, status: boolean) {
+ arr.forEach((item) => {
+ item[this.props.selectedField] = status;
+ if (this.props.tree && item.children && item.children.length > 0) {
+ this.setSelectedStatus(item.children, status);
+ }
+ });
+ }
+ private setTickedStatus(arr: Array, status: boolean, cascadeChildren: boolean = true) {
+ arr.forEach((item) => {
+ item[this.props.tickedField] = status;
+ if (this.props.tree && cascadeChildren && item.children && item.children.length > 0) {
+ this.setTickedStatus(item.children, status, cascadeChildren);
+ }
+ });
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/ResetProperty.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/ResetProperty.ts
new file mode 100644
index 00000000..e9fd0291
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/expose-api/src/ResetProperty.ts
@@ -0,0 +1,118 @@
+import { Base } from '../../Base';
+import { PropsType, TableType } from '../../index';
+
+/**
+ * w-grid 重新设置表格属性API
+ */
+export class ResetProperty extends Base {
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.setQueryCriteria = this.setQueryCriteria.bind(this);
+ this.setQueryCriteriaFieldValue = this.setQueryCriteriaFieldValue.bind(this);
+ this.setDataUrl = this.setDataUrl.bind(this);
+ this.setFetchDataUrl = this.setFetchDataUrl.bind(this);
+ this.setAddDataUrl = this.setAddDataUrl.bind(this);
+ this.setEditDataUrl = this.setEditDataUrl.bind(this);
+ this.setRemoveDataUrl = this.setRemoveDataUrl.bind(this);
+ this.setQueryCriteriaFieldValueHandler = this.setQueryCriteriaFieldValueHandler.bind(this);
+ }
+
+ /**
+ * 设置查询criteria
+ * @param criteria
+ * @returns
+ */
+ setQueryCriteria(criteria: any) {
+ if (!this.table) {
+ return;
+ }
+ this.table.queryCriteria = criteria;
+ }
+
+ /**
+ * 设置查询criteria中的字段值
+ * @param fieldName 字段名称
+ * @param fieldValue 字段值
+ * @return 无返回
+ */
+ setQueryCriteriaFieldValue(fieldName: string, fieldValue: any) {
+ if (Object.keys(this.table?.queryCriteria).length === 0) {
+ console.error('[w-grid]The property `queryCriteria` is configured.');
+ return;
+ }
+ this.setQueryCriteriaFieldValueHandler(this.table?.queryCriteria, fieldName, fieldValue);
+ }
+
+ /**
+ * 设置基础URL
+ * @param url
+ */
+ setDataUrl(url: string) {
+ if (!this.table) {
+ return;
+ }
+ this.table.url.dataUrl = url;
+ }
+
+ /**
+ * 设置数据查询URL
+ * @param url
+ * @returns
+ */
+ setFetchDataUrl(url: string) {
+ if (!this.table) {
+ return;
+ }
+ this.table.url.fetchDataUrl = url;
+ }
+
+ /**
+ * 设置新增时保存URL
+ * @param url
+ * @returns
+ */
+ setAddDataUrl(url: string) {
+ if (!this.table) {
+ return;
+ }
+ this.table.url.addDataUrl = url;
+ }
+
+ /**
+ * 设置修改时保存URL
+ * @param url
+ * @returns
+ */
+ setEditDataUrl(url: string) {
+ if (!this.table) {
+ return;
+ }
+ this.table.url.editDataUrl = url;
+ }
+
+ /**
+ * 设置删除url
+ * @param url
+ * @returns
+ */
+ setRemoveDataUrl(url: string) {
+ if (!this.table) {
+ return;
+ }
+ this.table.url.removeDataUrl = url;
+ }
+
+ private setQueryCriteriaFieldValueHandler(criteria: any, fieldName: string, fieldValue: any) {
+ if (criteria.criteria && criteria.criteria.length > 0) {
+ criteria.criteria.forEach((item) => {
+ if (!item.criteria && item.fieldName && item.fieldName === fieldName) {
+ item.value = fieldValue;
+ } else {
+ this.setQueryCriteriaFieldValueHandler(item.criteria, fieldName, fieldValue);
+ }
+ });
+ } else if (criteria.fieldName && criteria.fieldName === fieldName) {
+ criteria.value = fieldValue;
+ }
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/function/Criteria.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/function/Criteria.ts
new file mode 100644
index 00000000..f939eb34
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/function/Criteria.ts
@@ -0,0 +1,158 @@
+import { Tools } from '@/platform';
+import { nextTick } from 'vue';
+import { Constant, PropsType, GridTools } from '../index';
+import { Base } from '../Base';
+import { criteriaOperator } from '../../../query-builder/criteria';
+import { TableType } from '../types/TableType';
+
+// 定义数据结构类型
+interface CriteriaType {
+ fieldName?: string;
+ operator: string;
+ value?: any;
+ start?: any;
+ end?: any;
+ criteria?: CriteriaType[];
+}
+
+/**
+ * w-grid Criteria相关函数
+ */
+export class Criteria extends Base {
+ /**
+ * 查询面板配置的字段对象
+ */
+ queryFormFields: any;
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.queryFormFields = GridTools.arrayToObject(props.queryFormFields, 'name');
+ this.buildCriteria = this.buildCriteria.bind(this);
+ this.buildURLSearchParams = this.buildURLSearchParams.bind(this);
+ }
+
+ /**
+ * 根据字段值与字段名构建 criteria 查询对象
+ * @param value
+ * @param fieldName
+ * @returns
+ */
+ private buildCriteria(value: any, fieldName: string) {
+ const queryOperator = this.queryFormFields[fieldName]['queryOperator'];
+ if (!Tools.isEmpty(queryOperator)) {
+ return {
+ fieldName: fieldName,
+ operator: queryOperator,
+ value: value,
+ };
+ } else if (typeof value === 'boolean' || typeof value === 'number' || (typeof value === 'string' && this.queryFormFields[fieldName]['type'] === 'w-date')) {
+ return {
+ fieldName: fieldName,
+ operator: Constant.CRITERIA_OPERATOR.equals,
+ value: value,
+ };
+ } else if (Array.isArray(value)) {
+ return {
+ fieldName: fieldName,
+ operator: Constant.CRITERIA_OPERATOR.inSet,
+ value: value,
+ };
+ } else if (typeof value === 'object' && this.queryFormFields[fieldName]['type'] === 'w-date-range') {
+ return {
+ fieldName: fieldName,
+ operator: Constant.CRITERIA_OPERATOR.betweenInclusive,
+ start: value['from'],
+ end: value['to'],
+ };
+ } else {
+ return {
+ fieldName: fieldName,
+ operator: Constant.CRITERIA_OPERATOR.contains,
+ value: value,
+ };
+ }
+ }
+
+ /**
+ * 根据请求入参拼上查询面板构建 criteria 查询对象,转换成 URLSearchParams 后返回
+ * @param reqParams 请求入参
+ * @returns URLSearchParams 对象
+ */
+ buildURLSearchParams(reqParams) {
+ const urlSearchParams = new URLSearchParams(reqParams);
+ // 处理默认查询条件
+ if (Object.keys(this.table.queryCriteria).length > 0) {
+ urlSearchParams.append('criteria', JSON.stringify(this.table.queryCriteria));
+ }
+ if (this.table.advancedQueryStatus) {
+ const conditions = this.advancedQueryConditions();
+ if (conditions && conditions.criteria.length > 0) {
+ urlSearchParams.append('criteria', JSON.stringify(conditions));
+ }
+ } else {
+ const queryForm = this.table.componentRef.getTopRef()?.getQueryForm();
+ if (queryForm) {
+ nextTick(() => {
+ const queryFormData = queryForm.getData();
+ Object.keys(queryFormData).forEach((item) => {
+ if (
+ (!Tools.isEmpty(queryFormData[item]) && !Array.isArray(queryFormData[item])) ||
+ (!Tools.isEmpty(queryFormData[item]) && Array.isArray(queryFormData[item]) && queryFormData[item].length > 0)
+ ) {
+ // 根据数据进行operator处理
+ const criteria = this.buildCriteria(queryFormData[item], item);
+ urlSearchParams.append('criteria', JSON.stringify(criteria));
+ }
+ });
+ });
+ }
+ }
+ return urlSearchParams;
+ }
+
+ /**
+ * 高级查询条件处理,排除掉所有字段名或者值为空的条件
+ * @returns
+ */
+ private advancedQueryConditions() {
+ let conditions: any = undefined;
+ if (this.table.advancedQueryModelValue && this.table.advancedQueryModelValue['criteria'][0]['fieldName']) {
+ conditions = {
+ ...this.table.advancedQueryModelValue,
+ criteria: this.cleanCriteria(this.table.advancedQueryModelValue['criteria']),
+ };
+ }
+ return conditions;
+ }
+
+ /**
+ *
+ * 清除无效criteria
+ * @param criteria
+ * @returns
+ */
+ private cleanCriteria(criteria: CriteriaType[]): CriteriaType[] {
+ return criteria.filter((c) => {
+ if (Tools.isEmpty(c.fieldName)) {
+ return false;
+ } else if (c.operator === criteriaOperator.between.name || c.operator === criteriaOperator.notBetween.name) {
+ if (Tools.isEmpty(c.start) && Tools.isEmpty(c.end)) {
+ return false;
+ }
+ } else if (
+ c.operator !== criteriaOperator.isBlank.name &&
+ c.operator !== criteriaOperator.notBlank.name &&
+ c.operator !== criteriaOperator.isNull.name &&
+ c.operator !== criteriaOperator.notNull.name &&
+ Tools.isEmpty(c.value)
+ ) {
+ return false;
+ }
+ // 如果存在嵌套的criteria,则递归调用
+ if (c.criteria) {
+ c.criteria = this.cleanCriteria(c.criteria);
+ }
+ return true;
+ });
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/function/DragAndDrop.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/function/DragAndDrop.ts
new file mode 100644
index 00000000..c46fe840
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/function/DragAndDrop.ts
@@ -0,0 +1,411 @@
+import { Tools } from '@/platform';
+import { toRaw, Ref, ref } from 'vue';
+import { Constant, PropsType, TableType } from '../index';
+import { Base } from '../Base';
+
+/**
+ * w-grid 表格拖拽相关函数
+ */
+export class DragAndDrop extends Base {
+ /**
+ * 拖拽线样式
+ */
+ static DRAG_LINE_STYLE = {
+ width: '2px',
+ style: 'dashed',
+ color: 'orange',
+ };
+ static DRAG_ICON = {
+ // 拖拽时父节点图标
+ name: 'bi-folder-symlink-fill',
+ color: 'amber',
+ };
+ private static OPERATOR_ADD = 'add';
+ private static OPERATOR_REMOVE = 'remove';
+ private static POSITION_TOP = 'borderTop';
+ private static POSITION_BOTTOM = 'borderBottom';
+
+ /**
+ * 拖拽过程中停留在图标与名字区域的计时器
+ */
+ timer: any = undefined;
+ /**
+ * 拖拽过程中停留在图标与名字区域的时间(用来判定是否需要展开该节点)
+ */
+ iconAndNameDwellTime: Ref = ref(0);
+ /**
+ * 拖拽过程中停留在图标与名字区域行
+ */
+ dwellRow: any = undefined;
+
+ // 待更新排序数据
+ private updateOrderData = [];
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.onDragStart = this.onDragStart.bind(this);
+ this.onDragEnd = this.onDragEnd.bind(this);
+ this.iconAndNameDragOver = this.iconAndNameDragOver.bind(this);
+ this.onDragOver = this.onDragOver.bind(this);
+ this.onDragLeave = this.onDragLeave.bind(this);
+ this.onDrop = this.onDrop.bind(this);
+ this.setDragIcon = this.setDragIcon.bind(this);
+
+ this.destroyTimer = this.destroyTimer.bind(this);
+ this.dropTargetIsIconAndName = this.dropTargetIsIconAndName.bind(this);
+ this.resetOrder = this.resetOrder.bind(this);
+ this.resetOrderDataHandler = this.resetOrderDataHandler.bind(this);
+ this.removeRecord = this.removeRecord.bind(this);
+ this.nodeSplice = this.nodeSplice.bind(this);
+ this.dragLessThanTarget = this.dragLessThanTarget.bind(this);
+ this.setOrder = this.setOrder.bind(this);
+ this.childrenResetOrder = this.childrenResetOrder.bind(this);
+ this.canDrag = this.canDrag.bind(this);
+ this.iconAndNameCanDrag = this.iconAndNameCanDrag.bind(this);
+ this.dragRecordsContains = this.dragRecordsContains.bind(this);
+ this.setDndStyle = this.setDndStyle.bind(this);
+ }
+
+ // 拖拽开始
+ public onDragStart(e: any, scope: any) {
+ const img = new Image();
+ img.src = Constant.DND_BG_IMAGE;
+ e.dataTransfer.setDragImage(img, 0, 0);
+ const selecteds = this.instance.getSelectedRows();
+ if (
+ selecteds.length > 0 &&
+ selecteds.findIndex((item) => {
+ return item[Constant.FIELD_NAMES.ROW_KEY] === scope.row[Constant.FIELD_NAMES.ROW_KEY];
+ }) > -1
+ ) {
+ this.table.store.dragRecords = selecteds;
+ } else {
+ this.instance.clearSelected();
+ // 触发选择
+ scope.row[this.props.selectedField] = true;
+ this.table.store.dragRecords = [scope.row];
+ }
+ e.dataTransfer.effectAllowed = 'move';
+ }
+
+ // 销毁计时器
+ private destroyTimer() {
+ if (this.timer) {
+ clearInterval(this.timer);
+ this.timer = undefined;
+ }
+ this.iconAndNameDwellTime.value = 0;
+ this.dwellRow = undefined;
+ }
+
+ // 拖拽结束
+ onDragEnd(e: any, scope: any) {
+ this.destroyTimer();
+ }
+
+ /**
+ * 拖拽至图标与名字区域触发
+ * @param e
+ * @param scope
+ */
+ iconAndNameDragOver(e: any, row: any) {
+ if (!this.iconAndNameCanDrag(this.table?.store.dragRecords, row[this.props.primaryKey])) {
+ this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_TOP);
+ this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_BOTTOM);
+ this.setDragIcon(this.table?.rows);
+ e.dataTransfer.dropEffect = 'none';
+ } else {
+ if (!this.timer) {
+ this.timer = setInterval(() => {
+ this.iconAndNameDwellTime.value++;
+ }, 500);
+ this.dwellRow = row;
+ }
+ row['_dragIcon_'] = DragAndDrop.DRAG_ICON.name;
+ }
+ }
+
+ // 拖拽过程触发
+ onDragOver(e: any, scope: any, trMiddleHeight: number) {
+ e.preventDefault();
+ if (!this.canDrag(this.table?.store.dragRecords, scope.row[this.props.foreignKey])) {
+ this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_TOP);
+ this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_BOTTOM);
+ this.setDragIcon(this.table?.rows);
+ e.dataTransfer.dropEffect = 'none';
+ } else {
+ if (e.offsetY <= trMiddleHeight && !this.dragRecordsContains(scope.row)) {
+ this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_BOTTOM);
+ this.setDndStyle(e, DragAndDrop.OPERATOR_ADD, DragAndDrop.POSITION_TOP);
+ } else if (e.offsetY > trMiddleHeight && !this.dragRecordsContains(scope.row)) {
+ this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_TOP);
+ this.setDndStyle(e, DragAndDrop.OPERATOR_ADD, DragAndDrop.POSITION_BOTTOM);
+ }
+ if (!this.dropTargetIsIconAndName(e)) {
+ this.destroyTimer();
+ if (!Tools.isUndefinedOrNull(scope.row[this.props.foreignKey])) {
+ this.setDragIcon(this.table?.rows, scope.row[this.props.foreignKey]);
+ } else {
+ this.setDragIcon(this.table?.rows);
+ }
+ }
+ e.dataTransfer.dropEffect = 'move';
+ }
+ e.stopPropagation();
+ }
+
+ /**
+ * 判定放置目标是否为图标与名字div
+ * @param e
+ * @returns
+ */
+ private dropTargetIsIconAndName(e) {
+ return typeof e.target.className === 'string' && e.target.className.split(' ').includes('treeGridIconAndName');
+ }
+
+ // 拖拽至不可放置区域触发
+ onDragLeave(e) {
+ this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_TOP);
+ this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_BOTTOM);
+ this.destroyTimer();
+ }
+
+ // 拖拽放置时触发
+ onDrop(e: any, scope: any, trMiddleHeight: number) {
+ e.preventDefault();
+ this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_TOP);
+ this.setDndStyle(e, DragAndDrop.OPERATOR_REMOVE, DragAndDrop.POSITION_BOTTOM);
+ this.setDragIcon(this.table?.rows);
+ if (!this.table) {
+ return false;
+ }
+ if (
+ this.table.store.dragRecords.findIndex((item) => {
+ return item[Constant.FIELD_NAMES.ROW_KEY] === scope.row[Constant.FIELD_NAMES.ROW_KEY];
+ }) > -1
+ ) {
+ return;
+ }
+ // 每次放置时清空待更新集合
+ this.updateOrderData.splice(0, this.updateOrderData.length);
+
+ if (this.dropTargetIsIconAndName(e)) {
+ this.table.store.dragRecords.forEach((item) => {
+ // 从表格数据中将拖拽数据删除
+ this.removeRecord(this.table?.rows, item);
+
+ // 重新设置父
+ item[this.props.foreignKey] = scope.row[this.props.primaryKey];
+ // 将拖动的数据全部加入到父的children中
+ if (scope.row.children) {
+ scope.row.children.push(item);
+ } else {
+ scope.row.children = [item];
+ }
+ this.updateOrderData.push(item);
+ });
+ } else {
+ // 将拖动的数据及其子节点放到目标数据位置,并将目标数据父节点下的子节点重新排序。
+ this.resetOrder(e, this.table.store.dragRecords, this.table.rows, scope.row, trMiddleHeight);
+ }
+
+ // 请求后端更新排序
+ if (this.props.dndMode === Constant.DND_MODE.SERVER && this.updateOrderData.length > 0) {
+ this.tools?.reqApiFM.updates(this.updateOrderData);
+ }
+
+ this.tools?.em.afterDragAndDrop(this.updateOrderData);
+ }
+
+ // 重新设置树的排序
+ private resetOrder(e, dragRecords, arr, targetData, trMiddleHeight: number) {
+ for (let i = 0; i < arr.length; i++) {
+ if (arr[i][Constant.FIELD_NAMES.ROW_KEY] === targetData[Constant.FIELD_NAMES.ROW_KEY]) {
+ this.resetOrderDataHandler(e, dragRecords, targetData, i, trMiddleHeight);
+ break;
+ } else if (arr[i].children && arr[i].children.length > 0) {
+ this.resetOrder(e, dragRecords, arr[i].children, targetData, trMiddleHeight);
+ }
+ }
+ }
+
+ // 数据处理
+ private resetOrderDataHandler(e, dragRecords, targetData, targetIndex, trMiddleHeight: number) {
+ dragRecords.forEach((item) => {
+ // 从表格数据中将拖拽数据删除
+ const itemIndex = this.removeRecord(this.table?.rows, item);
+ if (Tools.isEmpty(targetData[this.props.foreignKey])) {
+ this.nodeSplice(e, itemIndex, targetIndex, item, targetData, this.table?.rows, trMiddleHeight);
+ item[this.props.foreignKey] = null;
+ this.setOrder(this.table?.rows);
+ } else {
+ this.childrenResetOrder(e, this.table?.rows, item, targetData, itemIndex, targetIndex, trMiddleHeight);
+ }
+ });
+ }
+
+ // 删除表格中的数据
+ private removeRecord(arr, removeData) {
+ let index = 0;
+ for (let i = 0; i < arr.length; i++) {
+ if (arr[i][Constant.FIELD_NAMES.ROW_KEY] === removeData[Constant.FIELD_NAMES.ROW_KEY]) {
+ arr.splice(i, 1);
+ index = i;
+ break;
+ } else if (arr[i].children && arr[i].children.length > 0) {
+ const a = this.removeRecord(arr[i].children, removeData);
+ if (a > 0) {
+ index = a;
+ }
+ }
+ }
+ return index;
+ }
+
+ // 处理树节点的增加
+ private nodeSplice(e, dragIndex, targetIndex, drag, target, arr, trMiddleHeight: number) {
+ if (
+ (Tools.isEmpty(drag[this.props.foreignKey]) && Tools.isEmpty(target[this.props.foreignKey])) ||
+ drag[this.props.foreignKey] === target[this.props.foreignKey]
+ ) {
+ const lessThanResult = this.dragLessThanTarget(dragIndex, targetIndex);
+ // 父节点相同的同级处理
+ if (e.offsetY <= trMiddleHeight && lessThanResult) {
+ arr.splice(targetIndex - 1, 0, drag);
+ } else if (e.offsetY > trMiddleHeight && !lessThanResult) {
+ arr.splice(targetIndex + 1, 0, drag);
+ } else {
+ arr.splice(targetIndex, 0, drag);
+ }
+ } else {
+ // 拖拽节点与目标节点不同级处理
+ if (e.offsetY <= trMiddleHeight) {
+ arr.splice(targetIndex, 0, drag);
+ } else if (e.offsetY > trMiddleHeight) {
+ arr.splice(targetIndex + 1, 0, drag);
+ }
+ }
+ }
+
+ private dragLessThanTarget(dragIndex, targetIndex) {
+ if (dragIndex < targetIndex) {
+ return true;
+ }
+ return false;
+ }
+
+ // 设置排序号
+ private setOrder(arr) {
+ arr.forEach((item, index) => {
+ item[this.props.dndOrderBy] = index + 1;
+ // 添加至待更新集合中
+ this.updateOrderData.push({ ...toRaw(item), children: null });
+ });
+ }
+
+ // 增加并重新设置父属性
+ private childrenResetOrder(e, arr, addData, targetData, dragIndex, targetIndex, trMiddleHeight: number) {
+ for (let i = 0; i < arr.length; i++) {
+ if (arr[i][this.props.primaryKey] === targetData[this.props.foreignKey]) {
+ this.nodeSplice(e, dragIndex, targetIndex, addData, targetData, arr[i].children, trMiddleHeight);
+ // 修改父
+ addData[this.props.foreignKey] = arr[i][this.props.primaryKey];
+ // 重新设置排序号
+ this.setOrder(arr[i].children);
+ break;
+ } else if (arr[i].children && arr[i].children.length > 0) {
+ this.childrenResetOrder(e, arr[i].children, addData, targetData, dragIndex, targetIndex, trMiddleHeight);
+ }
+ }
+ }
+
+ // 设置拖拽目标的父节点icon图标
+ setDragIcon(arr: any, key: string = '') {
+ for (let i = 0; i < arr.length; i++) {
+ if (arr[i].children && arr[i].children.length > 0) {
+ if (!Tools.isEmpty(key) && arr[i][this.props.primaryKey] === key) {
+ if (Tools.isEmpty(arr[i]['_dragIcon_']) || arr[i]['_dragIcon_'] !== DragAndDrop.DRAG_ICON.name) {
+ arr[i]['_dragIcon_'] = DragAndDrop.DRAG_ICON.name;
+ }
+ } else {
+ arr[i]['_dragIcon_'] = '';
+ }
+ this.setDragIcon(arr[i].children, key);
+ } else {
+ arr[i]['_dragIcon_'] = '';
+ }
+ }
+ }
+
+ /**
+ * 判断是否可以拖拽
+ * @param dragRecords 拖拽的记录集
+ * @param targetParentKey 当前鼠标所在的目标行的父节点key
+ */
+ private canDrag(dragRecords, targetParentKey) {
+ if (Tools.isEmpty(targetParentKey)) {
+ return true;
+ }
+ // 判断拖拽的行是否为当前鼠标所在的目标行的父节点(只要存在父,一直往上找)
+ let result = true;
+ let parentKey = targetParentKey;
+ while (!Tools.isEmpty(parentKey)) {
+ if (
+ dragRecords.findIndex((item) => {
+ return item[this.props.primaryKey] === parentKey;
+ }) > -1
+ ) {
+ result = false;
+ parentKey = null;
+ } else {
+ parentKey = this.tools?.dataFM.getRow(this.table.rows, parentKey, true)[this.props.foreignKey];
+ }
+ }
+ return result;
+ }
+
+ /**
+ * 判断图标与名字区域是否可以拖拽
+ * @param dragRecords 拖拽的记录集
+ * @param targetPrimaryKey 当前鼠标所在的目标行的主键
+ */
+ private iconAndNameCanDrag(dragRecords: any, targetPrimaryKey: any) {
+ let result = true;
+ for (let i = 0; i < dragRecords.length; i++) {
+ const record = dragRecords[i];
+ if (record[this.props.primaryKey] === targetPrimaryKey) {
+ result = false;
+ break;
+ }
+ }
+ return result;
+ }
+
+ // 根据传入的数据行判断是否在当前拖拽的记录集合中
+ private dragRecordsContains(dataRow) {
+ if (!this.table) {
+ return false;
+ }
+ return (
+ this.table.store.dragRecords.findIndex((item) => {
+ return item[Constant.FIELD_NAMES.ROW_KEY] === dataRow[Constant.FIELD_NAMES.ROW_KEY];
+ }) > -1
+ );
+ }
+
+ private setDndStyle(e: any, operator: string, position: string) {
+ let targetChildrenNodes: any = [];
+ if (e.target.nodeName === 'TD') {
+ targetChildrenNodes = e.target?.parentNode?.children;
+ } else if (e.target.nodeName === 'DIV' && e.target.className.split(' ').includes('treeGridFirstTd')) {
+ targetChildrenNodes = e.target?.parentNode?.parentNode?.children;
+ }
+ if (targetChildrenNodes) {
+ for (let i = 0; i < targetChildrenNodes.length; i++) {
+ targetChildrenNodes[i].style[position + 'Width'] = operator === DragAndDrop.OPERATOR_ADD ? DragAndDrop.DRAG_LINE_STYLE.width : '';
+ targetChildrenNodes[i].style[position + 'Style'] = operator === DragAndDrop.OPERATOR_ADD ? DragAndDrop.DRAG_LINE_STYLE.style : '';
+ targetChildrenNodes[i].style[position + 'Color'] = operator === DragAndDrop.OPERATOR_ADD ? DragAndDrop.DRAG_LINE_STYLE.color : '';
+ }
+ }
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/function/InlineEdit.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/function/InlineEdit.ts
new file mode 100644
index 00000000..9bf1ec5e
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/function/InlineEdit.ts
@@ -0,0 +1,112 @@
+import { Tools } from '@/platform';
+import { Base } from '../Base';
+import { Constant, PropsType, TableType } from '../index';
+
+/**
+ * w-grid 内联编辑函数
+ */
+export class InlineEdit extends Base {
+ /**
+ * 内联编辑时所有组件的 ref 对象
+ */
+ componentRef = {};
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.setComponentRef = this.setComponentRef.bind(this);
+ this.getComponentRef = this.getComponentRef.bind(this);
+ this.getComponentRefs = this.getComponentRefs.bind(this);
+ this.exitInlineEdit = this.exitInlineEdit.bind(this);
+ this.getEditorFieldByName = this.getEditorFieldByName.bind(this);
+ this.isShowInlineEditor = this.isShowInlineEditor.bind(this);
+ }
+
+ /**
+ * 设置内联编辑时组件的 ref 对象
+ * @param el html 元素
+ * @param row 行数据
+ * @param col 列对象
+ */
+ public setComponentRef(el: any, row: any, col: any) {
+ if (el && !Tools.isEmpty(col.name)) {
+ this.componentRef[row[Constant.FIELD_NAMES.ROW_KEY] + '_' + col.name] = el;
+ }
+ }
+
+ /**
+ * 获取内联编辑时组件的 ref 对象
+ * @param rowKey 行主键
+ * @param colName 列名
+ */
+ public getComponentRef(rowKey: string, colName: string) {
+ return this.componentRef[rowKey + '_' + colName];
+ }
+
+ /**
+ * 获取所有内联编辑时组件的 ref 对象,传入空数组获取全部
+ * @param rowKey 行主键或者数组
+ * @returns
+ */
+ public getComponentRefs(rowKey: string | Array) {
+ const refs = [];
+ if (!Tools.isEmpty(this.componentRef)) {
+ const filterResult = Object.keys(this.componentRef).filter((item) => {
+ if (typeof rowKey === 'string') {
+ return item.startsWith(rowKey + '_');
+ } else {
+ return true;
+ }
+ });
+ if (filterResult.length > 0) {
+ filterResult.forEach((key) => {
+ refs.push(this.componentRef[key]);
+ });
+ }
+ }
+ return refs;
+ }
+
+ /**
+ * 退出内联编辑状态
+ */
+ public exitInlineEdit() {
+ if (this.table) {
+ this.table.store.inlineEditStatus = Constant.EDIT_STATUS.NONE;
+ this.table.store.cellSelected = undefined;
+ }
+ }
+
+ /**
+ * 根据传入的字段名获取表格 editor 中的配置。
+ * @param name
+ * @returns
+ */
+ public getEditorFieldByName(name: string) {
+ if (this.table && this.props?.editor?.form?.fields) {
+ const field = this.props.editor.form.fields.find((item) => item['name'] === name);
+ return field;
+ }
+ return undefined;
+ }
+
+ /**
+ * 是否显示内联编辑界面
+ * @param col
+ * @param rowKey
+ * @returns
+ */
+ public isShowInlineEditor(col: object, rowKey: string) {
+ if (Tools.hasOwnProperty(col, 'edit') && !col['edit']) {
+ return false;
+ }
+ if (
+ this.getEditorFieldByName(col['name']) &&
+ ((this.tools?.dataFM.isSelectedRow(rowKey) && this.table.store.inlineEditStatus === Constant.EDIT_STATUS.ROW) ||
+ this.table?.store.inlineEditStatus === Constant.EDIT_STATUS.ROWS ||
+ (this.tools?.dataFM.isSelectedCell(rowKey, col['name']) && this.table.store.inlineEditStatus === Constant.EDIT_STATUS.CELL))
+ ) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/function/Operator.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/function/Operator.ts
new file mode 100644
index 00000000..a63bcb32
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/function/Operator.ts
@@ -0,0 +1,272 @@
+import { Tools } from '@/platform';
+import { nextTick } from 'vue';
+import { Base } from '../Base';
+import { Constant, PropsType, TableType } from '../index';
+
+/**
+ * w-grid 表格操作函数
+ */
+export class Operator extends Base {
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.tableFullscreen = this.tableFullscreen.bind(this);
+ this.resetHeaderCheckbox = this.resetHeaderCheckbox.bind(this);
+ this.resetTreeGridExpand = this.resetTreeGridExpand.bind(this);
+ this.propsValidate = this.propsValidate.bind(this);
+ this.resize = this.resize.bind(this);
+ this.resetStyleVariableValue = this.resetStyleVariableValue.bind(this);
+ }
+
+ /**
+ * 表格全屏触发函数
+ * @param value true: 进入全屏, false: 退出全屏
+ */
+ tableFullscreen(value) {
+ this.table.configStore.isFullscreen = !this.table.configStore.isFullscreen;
+ if (value) {
+ this.table.store.location.tableInFullscreenY = this.table.store.location.yLocation;
+ this.table.store.location.noDataTrInFullscreenY = this.table.store.location.noDataTrYLocation;
+ } else {
+ this.table.store.location.yLocation = this.table.store.location.tableInFullscreenY;
+ this.table.store.location.noDataTrYLocation = this.table.store.location.noDataTrInFullscreenY;
+ this.table.store.location.tableInFullscreenY = 0;
+ this.table.store.location.noDataTrInFullscreenY = 0;
+ }
+ }
+
+ /**
+ * 重新设置表头勾选框状态
+ */
+ public resetHeaderCheckbox() {
+ this.tableError();
+ if (this.table) {
+ if (this.props.checkboxSelection) {
+ const ticked_ = [];
+ const rows = this.table?.rows || [];
+ rows.forEach((item) => {
+ if (item[this.props.tickedField]) {
+ ticked_.push(item);
+ }
+ });
+ if (ticked_.length === rows.length && rows.length > 0) {
+ // 全部勾选设置为 true
+ this.table.store.headerTicked = true;
+ } else if (ticked_.length > 0) {
+ // 存在一条勾选的记录设置为 null
+ this.table.store.headerTicked = null;
+ } else {
+ // 一条勾选的记录都没有设置为 false
+ this.table.store.headerTicked = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * 重新设置树表格的展开状态
+ * @param arr 需要设置的行数据集合
+ * @param expandStatus 需要设置的状态
+ */
+ public resetTreeGridExpand(arr: Array, expandStatus: boolean) {
+ if (!this.props.tree) {
+ return;
+ }
+ arr.forEach((item) => {
+ item[Constant.FIELD_NAMES.EXPAND] = expandStatus;
+ if (item.children && item.children.length > 0) {
+ this.resetTreeGridExpand(item.children, expandStatus);
+ }
+ });
+ }
+
+ /**
+ * 表格属性校验
+ */
+ propsValidate() {
+ if (
+ !Tools.isEmpty(this.props.groupMode) &&
+ ((typeof this.props.groupByField === 'string' && Tools.isEmpty(this.props.groupByField)) ||
+ (Array.isArray(this.props.groupByField) && this.props.groupByField.length === 0))
+ ) {
+ console.warn('[w-grid]Configured the `groupMode` property but did not configure the `groupByField` property.');
+ } else if (!Tools.isEmpty(this.props.groupMode)) {
+ const groupByField =
+ typeof this.props.groupByField === 'string' ? [this.props.groupByField] : Array.isArray(this.props.groupByField) ? this.props.groupByField : undefined;
+ if (groupByField && this.table?.columns.findIndex((item) => groupByField.includes(item.name)) === -1) {
+ throw new Error('[w-grid]`groupByField` configuration cannot be found in the table columns.');
+ }
+ }
+ }
+
+ /**
+ * 表格大小变化触发函数
+ * @returns
+ */
+ resize() {
+ if (!this || !this.table) {
+ return;
+ }
+ const tableElement = this.instance?.getHtmlElement();
+ if (!tableElement) {
+ return;
+ }
+ this.table.store.resizeFlag = !this.table.store.resizeFlag;
+ this.table.componentRef.getTopRef()?.handleQueryFormShowField();
+ this.table.componentRef.getTopRef()?.handleToolbarActions();
+ if (tableElement) {
+ this.table.store.location.yLocation = tableElement.getBoundingClientRect().y;
+ if (tableElement.getElementsByClassName('noDataTr').length > 0) {
+ this.table.store.location.noDataTrYLocation = tableElement.getElementsByClassName('noDataTr')[0].getBoundingClientRect().y;
+ }
+ this.table.store.location.topHeight = tableElement.getElementsByClassName('q-table__top')[0]?.clientHeight;
+ this.table.store.location.middleWidth = tableElement.getElementsByClassName('q-table__middle')[0]?.clientWidth;
+ this.table.store.location.middleScrollWidth = tableElement.getElementsByClassName('q-table__middle')[0]?.scrollWidth;
+ this.table.store.location.columnHeadHeight = tableElement.getElementsByTagName('thead')[0]?.clientHeight;
+ this.table.store.location.hideHeaderNoDataHeight = tableElement.getElementsByClassName('q-table__middle')[0]?.clientHeight;
+ // 判断是否有数据,没数据修改 middleHeight
+ if ((this.table.rows && this.table.rows.length > 0) || this.props.hideBottom) {
+ this.table.store.location.middleHeight = tableElement.getElementsByClassName('q-table__middle')[0]?.clientHeight;
+ } else {
+ let scrollHeight = 0;
+ if (this.table.store.location.middleScrollWidth - this.table.store.location.middleWidth > 0) {
+ scrollHeight = 15;
+ }
+ this.table.store.location.middleHeight = this.table.store.location.columnHeadHeight + scrollHeight;
+ }
+ let titleTotalHeight = tableElement.getElementsByTagName('thead')[0]?.offsetHeight;
+ // 无数据时列头会增加一行,多表头的top距离计算会出错,需减掉多出来的提示行。
+ if (this.table.rows.length === 0) {
+ const noDataTrHeight = tableElement.getElementsByClassName('noDataTr')[0]?.offsetHeight;
+ if (titleTotalHeight && titleTotalHeight > 0) {
+ titleTotalHeight = titleTotalHeight - noDataTrHeight;
+ } else {
+ titleTotalHeight = noDataTrHeight;
+ }
+ }
+ this.table.store.location.titleTotalHeight = titleTotalHeight;
+ }
+ }
+
+ /**
+ * 重新设置样式变量值
+ * @param time 延迟时间
+ */
+ public resetStyleVariableValue(time: number = 500) {
+ const tableElement = this.instance.getHtmlElement();
+ setTimeout(() => {
+ if (!this.table) {
+ this.tableError();
+ return;
+ }
+ if (tableElement) {
+ this.table.store.location.yLocation = tableElement.getBoundingClientRect()?.y;
+ if (tableElement.getElementsByClassName('noDataTr').length > 0) {
+ this.table.store.location.noDataTrYLocation = tableElement.getElementsByClassName('noDataTr')[0]?.getBoundingClientRect().y;
+ }
+ this.table.store.location.topHeight = tableElement.getElementsByClassName('q-table__top')[0]?.clientHeight;
+ this.table.store.location.bottomHeight = tableElement.getElementsByClassName('q-table__bottom')[0]?.clientHeight;
+ this.table.store.location.middleWidth = tableElement.getElementsByClassName('q-table__middle')[0]?.clientWidth;
+ this.table.store.location.middleScrollWidth = tableElement.getElementsByClassName('q-table__middle')[0]?.scrollWidth;
+ this.table.store.location.columnHeadHeight = tableElement.getElementsByTagName('thead')[0]?.clientHeight;
+ this.table.store.location.hideHeaderNoDataHeight = tableElement.getElementsByClassName('q-table__middle')[0]?.clientHeight;
+ // 判断是否有数据,没数据修改 middleHeight
+ if (this.table?.rows?.length > 0 || this.props.hideBottom) {
+ this.table.store.location.middleHeight = tableElement.getElementsByClassName('q-table__middle')[0]?.clientHeight;
+ } else {
+ let scrollHeight = 0;
+ if (this.table.store.location.middleScrollWidth - this.table.store.location.middleWidth > 0) {
+ scrollHeight = 15;
+ }
+ this.table.store.location.middleHeight = this.table.store.location.columnHeadHeight + scrollHeight;
+ }
+
+ const tableDom = tableElement.getElementsByTagName('table')[0];
+ if (this.table.configStore.stickyNum > 0) {
+ if (this.table.componentRef.getHeaderRef()?.getColumnTitleState()?.columnTitleRowNum > 1) {
+ const theadDom = tableDom.getElementsByTagName('thead')[0];
+ const theadTrDom = theadDom.getElementsByTagName('tr');
+ tableDom.style.setProperty('--columnWidth' + '-' + 0 + '-' + 0, 0 + 'px');
+ for (let i = 0; i < theadDom.getElementsByTagName('tr').length; i++) {
+ const theadThDom = theadTrDom[i].getElementsByTagName('th');
+ for (let k = 0; k < theadThDom.length; k++) {
+ tableDom.style.setProperty('--columnWidth' + '-' + (i + 1) + '-' + (k + 1), theadThDom[k].offsetWidth + 'px');
+ }
+ }
+ }
+
+ const column1Width = tableDom.getElementsByTagName('td')[0]?.offsetWidth;
+ const column2Width = tableDom.getElementsByTagName('td')[1]?.offsetWidth;
+ const column3Width = tableDom.getElementsByTagName('td')[2]?.offsetWidth;
+ const column4Width = tableDom.getElementsByTagName('td')[3]?.offsetWidth;
+ const column5Width = tableDom.getElementsByTagName('td')[4]?.offsetWidth;
+ const column6Width = tableDom.getElementsByTagName('td')[5]?.offsetWidth;
+ const column7Width = tableDom.getElementsByTagName('td')[6]?.offsetWidth;
+ const column8Width = tableDom.getElementsByTagName('td')[7]?.offsetWidth;
+ const column9Width = tableDom.getElementsByTagName('td')[8]?.offsetWidth;
+
+ tableDom.style.setProperty('--column1Width', column1Width + 'px');
+ tableDom.style.setProperty('--column2Width', column1Width + column2Width + 'px');
+ tableDom.style.setProperty('--column3Width', column1Width + column2Width + column3Width + 'px');
+ tableDom.style.setProperty('--column4Width', column1Width + column2Width + column3Width + column4Width + 'px');
+ tableDom.style.setProperty('--column5Width', column1Width + column2Width + column3Width + column4Width + column5Width + 'px');
+ tableDom.style.setProperty('--column6Width', column1Width + column2Width + column3Width + column4Width + column5Width + column6Width + 'px');
+ tableDom.style.setProperty(
+ '--column7Width',
+ column1Width + column2Width + column3Width + column4Width + column5Width + column6Width + column7Width + 'px',
+ );
+ tableDom.style.setProperty(
+ '--column8Width',
+ column1Width + column2Width + column3Width + column4Width + column5Width + column6Width + column7Width + column8Width + 'px',
+ );
+ tableDom.style.setProperty(
+ '--column9Width',
+ column1Width + column2Width + column3Width + column4Width + column5Width + column6Width + column7Width + column8Width + column9Width + 'px',
+ );
+ }
+ let titleTotalHeight = tableDom.getElementsByTagName('thead')[0]?.offsetHeight;
+ // 无数据时列头会增加一行,多表头的top距离计算会出错,需减掉多出来的提示行。
+ if (this.table.rows.length === 0) {
+ const noDataTrHeight = tableElement.getElementsByClassName('noDataTr')[0]?.offsetHeight;
+ if (titleTotalHeight && titleTotalHeight > 0) {
+ titleTotalHeight = titleTotalHeight - noDataTrHeight;
+ } else {
+ titleTotalHeight = noDataTrHeight;
+ }
+ }
+ this.table.store.location.titleTotalHeight = titleTotalHeight;
+ }
+ }, time);
+
+ tableElement.getElementsByTagName('table')[0].style.setProperty('--tableHeadBgColor', this.table?.color.headBgColor);
+ tableElement.getElementsByTagName('table')[0].style.setProperty('--stickyBgColor', this.table?.color.stickyBgColor);
+ tableElement.getElementsByTagName('table')[0].style.setProperty('--tableBorderColor', this.table?.color.borderColor);
+ tableElement.getElementsByTagName('table')[0].style.setProperty('--tableColumnTitleHeight', (this.tools?.cm.denseHeader.value ? 28 : 48) + 'px');
+ let headerPadding = '8px';
+ if (this.tools?.cm.denseHeader) {
+ headerPadding = '4px';
+ }
+ tableElement.getElementsByTagName('table')[0].style.setProperty('--tableHeaderPadding', headerPadding);
+ let bodyPadding = '8px';
+ if (this.tools?.cm.denseBody) {
+ bodyPadding = '4px';
+ }
+ tableElement.getElementsByTagName('table')[0].style.setProperty('--tableBodyPadding', bodyPadding);
+ tableElement.getElementsByTagName('table')[0].style.setProperty('--tableBodyHeight', (this.tools?.cm.denseBody.value ? 24 : 48) + 'px');
+ nextTick(() => {
+ if (this.tools?.cm.denseBody.value && !this.props.hideBottom) {
+ if (tableElement.getElementsByClassName('q-table__bottom').length > 0) {
+ tableElement.getElementsByClassName('q-table__bottom')[0].style.setProperty('--tableBottomHeight', 33 + 'px');
+ tableElement.getElementsByClassName('q-table__bottom')[0].style.setProperty('--tableBottomButtonHeight', 24 + 'px');
+ }
+ } else if (!this.props.hideBottom && tableElement.getElementsByClassName('q-table__bottom').length > 0) {
+ tableElement.getElementsByClassName('q-table__bottom')[0].style.setProperty('--tableBottomHeight', 50 + 'px');
+ tableElement.getElementsByClassName('q-table__bottom')[0].style.setProperty('--tableBottomButtonHeight', 40 + 'px');
+ }
+ });
+ if (this.props.title || this.props.toolbarActions.length > 0 || this.props.configButton) {
+ tableElement.getElementsByClassName('q-table__top')[0].style.setProperty('--tableTopPadding', '8px');
+ } else {
+ tableElement.getElementsByClassName('q-table__top')[0].style.setProperty('--tableTopPadding', '0px');
+ }
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/function/RequestApi.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/function/RequestApi.ts
new file mode 100644
index 00000000..5560379c
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/function/RequestApi.ts
@@ -0,0 +1,339 @@
+import { $t, axios, noErrorAxios, NotifyManager, Tools, TreeBuilder, ServerExceptionHandler } from '@/platform';
+import { toRaw } from 'vue';
+import { Base } from '../Base';
+import { Constant, PropsType, TableType } from '../index';
+
+/**
+ * w-grid 请求后端API函数
+ */
+export class RequestApi extends Base {
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.fetchData = this.fetchData.bind(this);
+ this.treeFetchDataByForeignKey = this.treeFetchDataByForeignKey.bind(this);
+ this.save = this.save.bind(this);
+ this.updates = this.updates.bind(this);
+ this.remove = this.remove.bind(this);
+
+ this.requestHandle = this.requestHandle.bind(this);
+ this.responseHandle = this.responseHandle.bind(this);
+ this.setExpandDatas = this.setExpandDatas.bind(this);
+ this.setTreeExpandDatas = this.setTreeExpandDatas.bind(this);
+ this.setAlongGroupExpandDatas = this.setAlongGroupExpandDatas.bind(this);
+ this.getFetchUrl = this.getFetchUrl.bind(this);
+ }
+
+ /**
+ * 请求数据
+ * @param ops 分页排序信息
+ */
+ async fetchData(ops: any) {
+ this.tools?.editFM.exitInlineEdit();
+
+ this.table.store.loading = true;
+ const resp: any = await this.requestHandle(ops);
+ this.table.store.loading = false;
+
+ // 处理返回的结果
+ this.responseHandle(resp, ops);
+
+ this.tools?.dataFM.setExtraProperty(this.table.rows, ops);
+ this.tools?.opFM.resetHeaderCheckbox();
+ this.tools?.opFM.resetStyleVariableValue(100);
+ // 判断数据加载后是否展开树形表格
+ if (this.table.store.expand) {
+ if (this.props.treeLazyLoad) {
+ this.table.store.alreadyLoadAllData = true;
+ this.table.store.loading = true;
+ const datas = await this.tools?.reqApiFM.treeFetchDataByForeignKey();
+ this.tools?.apiFM.localMode.setLocalData(datas);
+ this.table.store.loading = false;
+ this.tools?.apiFM.operator.expand(undefined, true);
+ } else {
+ this.tools?.apiFM.operator.expand();
+ }
+ } else if (this.props.tree && this.props.treeLazyLoad && this.table.store.expandDatas.length > 0) {
+ for (let i = 0; i < this.table.store.expandDatas.length; i++) {
+ const primaryKey = this.table.store.expandDatas[i];
+ if (!this.table.store.lazyloadNoChildrenDatas.includes(primaryKey)) {
+ const childrenData = await this.tools?.reqApiFM.treeFetchDataByForeignKey(primaryKey);
+ if (childrenData.length > 0) {
+ this.tools?.apiFM.localMode.addLocalData(childrenData);
+ } else {
+ const row = this.tools?.dataFM.getRow(this.table.rows, primaryKey, false);
+ if (row) {
+ row[Constant.FIELD_NAMES.LAZYLOAD_NO_CHILDREN] = true;
+ }
+ }
+ } else {
+ const row = this.tools?.dataFM.getRow(this.table.rows, primaryKey, false);
+ if (row) {
+ row[Constant.FIELD_NAMES.LAZYLOAD_NO_CHILDREN] = true;
+ }
+ }
+ }
+ }
+ // 触发请求完成后事件
+ this.tools?.em.afterRequestData(toRaw(this.table.rows));
+ }
+
+ /**
+ * 树表格根据关系字段查询
+ * @param primaryKey 加载哪个主键下面的子节点,不传入加载所有
+ */
+ async treeFetchDataByForeignKey(primaryKey?: string) {
+ const url = this.getFetchUrl();
+ const urlSearchParams = new URLSearchParams({ pageable: false });
+ if (primaryKey) {
+ const criteria = {
+ fieldName: this.props.foreignKey,
+ operator: Constant.CRITERIA_OPERATOR.equals,
+ value: primaryKey,
+ };
+ urlSearchParams.append('criteria', JSON.stringify(criteria));
+ }
+ const resp = await axios.get(url, { params: urlSearchParams }).catch((error) => {
+ console.error('[w-grid]fetch data error:', error);
+ this.table.store.loading = false;
+ });
+ let resultData: any = [];
+ if (resp && resp.data) {
+ const responseData = resp.data;
+ if (Array.isArray(responseData)) {
+ resultData = responseData;
+ } else if (typeof responseData === 'object' && responseData.content) {
+ resultData = responseData.content;
+ }
+ }
+ return resultData;
+ }
+
+ /**
+ * 新增、修改、复制、新增顶级节点、新增子节点保存数据
+ * @param method 请求方式
+ * @param data 数据
+ * @param url url
+ * @param formRef form对象
+ * @param callback 请求成功回调
+ * @param errorCallback 请求错误回调
+ */
+ save(method: string, data: any, url: string, formRef: any, callback: any, errorCallback: any) {
+ const requestParams = {
+ method: method,
+ headers: { 'content-type': 'application/json;charset=utf-8;' },
+ data: data,
+ url: url,
+ };
+ noErrorAxios(requestParams)
+ .then((resp) => {
+ if (callback) {
+ callback(resp);
+ }
+ NotifyManager.info($t('tip.operationSuccess') || '');
+ this.tools?.em.afterEditorDataSubmit({ grid: this.instance, data: resp.data });
+ })
+ .catch((error) => {
+ if (error?.code === 1001) {
+ // 验证错误
+ formRef?.setValidationErrors(error.data);
+ } else {
+ ServerExceptionHandler.handle(error);
+ }
+ if (errorCallback) {
+ errorCallback(error);
+ }
+ });
+ }
+
+ /**
+ * 批量更新数据
+ * @param data 数据集合
+ * @param callback 回调函数
+ */
+ updates(data: Array, callback: any = undefined) {
+ const requestParams = {
+ method: 'PUT',
+ headers: { 'content-type': 'application/json;charset=utf-8;' },
+ data: data,
+ url: this.table?.url.dataUrl + '/updates',
+ };
+ noErrorAxios(requestParams)
+ .then((resp) => {
+ if (!Tools.isEmpty(callback)) {
+ callback(resp?.data);
+ }
+ })
+ .catch((error) => {
+ if (error?.code === 1001) {
+ // 验证错误
+ NotifyManager.error('服务器验证未通过');
+ } else {
+ ServerExceptionHandler.handle(error);
+ }
+ });
+ }
+
+ /**
+ * 删除数据
+ * @param ids 删除的主键集合
+ */
+ remove(ids: Array) {
+ const requestParams: any = {
+ method: 'DELETE',
+ url: this.table.url.removeDataUrl || this.table.url.dataUrl,
+ data: ids,
+ };
+ axios(requestParams)
+ .then((resp) => {
+ // 触发请求完成后事件
+ this.tools?.em.afterRemove(resp?.data);
+ NotifyManager.info($t('tip.operationSuccess') || '');
+ if (this.props.refreshData || !this.props.tree) {
+ this.tools?.apiFM.operator.refreshGrid();
+ } else {
+ this.tools?.apiFM.localMode.removeLocalData(resp?.data);
+ }
+ })
+ .catch((error) => {
+ console.error('[w-grid]Remove error:', error);
+ });
+ }
+
+ private async requestHandle(ops: any) {
+ const reqParams: any = {};
+ if (this.props.pageable && !this.props.tree && !this.props.localMode) {
+ reqParams.page = ops.pagination.page;
+ reqParams.size = ops.pagination.rowsPerPage;
+ }
+ reqParams.pageable = this.props.tree || this.props.localMode ? false : this.props.pageable;
+ if (ops.pagination.sortBy && ops.pagination.sortBy !== '') {
+ if (ops.pagination.descending) {
+ reqParams.sortBy = '-' + ops.pagination.sortBy;
+ reqParams.descending = ops.pagination.descending;
+ } else {
+ reqParams.sortBy = ops.pagination.sortBy;
+ }
+ } else if (this.props.sortBy && this.props.sortBy.length > 0) {
+ reqParams.sortBy = this.props.sortBy;
+ }
+
+ // 后台 RestCrudController 查询
+ let urlSearchParams = this.tools?.criteriaFM.buildURLSearchParams(reqParams);
+
+ // 请求前事件触发
+ const _requestParams = await this.tools?.em.beforeRequestData(urlSearchParams);
+ if (_requestParams) {
+ urlSearchParams = _requestParams;
+ }
+ const url = this.getFetchUrl();
+
+ // 树型表格懒加载
+ if (this.props.tree && this.props.treeLazyLoad && urlSearchParams) {
+ const criteria = {
+ fieldName: this.props.foreignKey,
+ operator: Constant.CRITERIA_OPERATOR.isNull,
+ };
+ urlSearchParams.append('criteria', JSON.stringify(criteria));
+ }
+ const resp = await axios.get(url, { params: urlSearchParams }).catch((error) => {
+ console.error('[w-grid]fetch data error:', error);
+ this.table.store.loading = false;
+ });
+ return resp;
+ }
+
+ private responseHandle(resp: any, ops: any) {
+ if (resp && resp.data) {
+ if (!this.table.store.expand) {
+ // 记录当前已展开记录
+ this.setExpandDatas();
+ }
+ const responseData = resp.data;
+ if (Array.isArray(responseData)) {
+ this.table.rows = responseData;
+ this.table.store.pagination.rowsNumber = responseData.length;
+ } else if (typeof responseData === 'object' && responseData.content) {
+ if (this.props.pageable) {
+ this.table.store.pagination.page = this.table.store.pagination.reqPageStart === 0 ? responseData.number + 1 : responseData.number;
+ this.table.store.pagination.rowsPerPage = responseData.size || this.table.store.pagination.rowsPerPage;
+ }
+ this.table.store.pagination.rowsNumber = responseData.totalElements;
+ this.table.rows = responseData.content;
+ } else {
+ this.table.rows = [];
+ }
+
+ if (this.props.tree) {
+ if (Array.isArray(responseData)) {
+ this.table.store.pagination.rowsNumber = responseData.length;
+ this.table.store.pagination.rowsPerPage = 0;
+ this.table.rows =
+ this.props.treeRelationship === 'parent' ? TreeBuilder.build(responseData, this.props.foreignKey, this.props.primaryKey) : responseData;
+ } else if (typeof responseData === 'object' && responseData.content) {
+ this.table.store.pagination.rowsNumber = responseData.content.length;
+ this.table.store.pagination.rowsPerPage = 0;
+ this.table.rows =
+ this.props.treeRelationship === 'parent'
+ ? TreeBuilder.build(responseData.content, this.props.foreignKey, this.props.primaryKey)
+ : responseData.content;
+ }
+ }
+ } else {
+ this.table.rows = [];
+ }
+ this.table.store.pagination.sortBy = ops.pagination.sortBy;
+ this.table.store.pagination.descending = ops.pagination.descending;
+ }
+
+ /**
+ * 设置 store 中记录的当前已展开记录
+ * @param
+ */
+ private setExpandDatas() {
+ if (this.props.tree) {
+ this.table.store.expandDatas = [];
+ this.table.store.lazyloadNoChildrenDatas = [];
+ this.setTreeExpandDatas(toRaw(this.table.rows));
+ } else if (this.props.groupMode === Constant.GROUP_MODE.ALONE) {
+ this.setAlongGroupExpandDatas();
+ }
+ }
+
+ private setTreeExpandDatas(array: Array) {
+ array.forEach((item) => {
+ if (item[Constant.FIELD_NAMES.EXPAND]) {
+ if (this.props.treeLazyLoad) {
+ const parents = this.tools?.apiFM.getData.getCascadeParentsData(item, null);
+ if (parents.length === 0 || parents.findIndex((parent) => !parent[Constant.FIELD_NAMES.EXPAND]) === -1) {
+ this.table.store.expandDatas.push(item[this.props.primaryKey]);
+ }
+ } else {
+ this.table.store.expandDatas.push(item[this.props.primaryKey]);
+ }
+ }
+ if (item[Constant.FIELD_NAMES.LAZYLOAD_NO_CHILDREN] && (!item.children || item.children.length === 0)) {
+ this.table.store.lazyloadNoChildrenDatas.push(item[this.props.primaryKey]);
+ }
+ if (item.children?.length > 0) {
+ this.setTreeExpandDatas(item.children);
+ }
+ });
+ }
+
+ private setAlongGroupExpandDatas() {
+ this.table.configStore.aloneGroupRecords.forEach((item) => {
+ if (item['expand'] && !this.table.store.expandDatas.includes(item['groupName'])) {
+ this.table.store.expandDatas.push(item['groupName']);
+ } else if (!item['expand']) {
+ const index = this.table.store.expandDatas.findIndex((tmp) => tmp === item['groupName']);
+ if (index > -1) {
+ this.table.store.expandDatas.splice(index, 1);
+ }
+ }
+ });
+ }
+
+ private getFetchUrl() {
+ return this.table?.url.fetchDataUrl || this.table?.url.dataUrl || '';
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/function/RowData.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/function/RowData.ts
new file mode 100644
index 00000000..26956663
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/function/RowData.ts
@@ -0,0 +1,440 @@
+import { toRaw } from 'vue';
+import sortArray from 'sort-array';
+import { Tools } from '@/platform';
+import { Base } from '../Base';
+import { Constant, GridTools, PropsType, TableType } from '../index';
+
+/**
+ * 行数据相关处理函数
+ */
+export class RowData extends Base {
+ private aloneGroupRowIndex: number = 0;
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.getRow = this.getRow.bind(this);
+ this.getRowsByTarget = this.getRowsByTarget.bind(this);
+ this.isSelectedRow = this.isSelectedRow.bind(this);
+ this.isSelectedCell = this.isSelectedCell.bind(this);
+ this.setOldValue = this.setOldValue.bind(this);
+ this.isLastRow = this.isLastRow.bind(this);
+ this.checkLastRow = this.checkLastRow.bind(this);
+ this.setExtraProperty = this.setExtraProperty.bind(this);
+ this.arrayOrderBy = this.arrayOrderBy.bind(this);
+ this.mergeSortFields = this.mergeSortFields.bind(this);
+ this.mergeDefaultSortBy = this.mergeDefaultSortBy.bind(this);
+ this.rowDataExtraPropertyHandle = this.rowDataExtraPropertyHandle.bind(this);
+ this.mergeGroupBy = this.mergeGroupBy.bind(this);
+ this.aloneGroupBy = this.aloneGroupBy.bind(this);
+ this.setRowIndex = this.setRowIndex.bind(this);
+ this.initRowDataExtraProperty = this.initRowDataExtraProperty.bind(this);
+ this.getInitRowTicked = this.getInitRowTicked.bind(this);
+ this.getInitRowSelected = this.getInitRowSelected.bind(this);
+ this.pushRow = this.pushRow.bind(this);
+ this.rowsKeyIsEmptyError = this.rowsKeyIsEmptyError.bind(this);
+ this.rowsKeyNotExistError = this.rowsKeyNotExistError.bind(this);
+ }
+
+ /**
+ * 从传入的行记录集合中查找符合条件的行数据(树型表格也适用)
+ * @param array 集合。
+ * @param key 行数据主键或行数据前端主键。
+ * @param parentFlag key为父ID标识,当key为父ID时,通过行数据主键进行匹配,否则根据传入的 fieldName 进行匹配。
+ * @param fieldName 字段名称,默认为行数据前端主键名。
+ * @returns
+ */
+ public getRow(array: Array, key: string, parentFlag: boolean, fieldName: string = Constant.FIELD_NAMES.ROW_KEY): any {
+ let result = undefined;
+ for (let i = 0; i < array.length; i++) {
+ const item = array[i];
+ if (parentFlag ? item[this.props.primaryKey] === key : item[fieldName] === key || item[this.props.primaryKey] === key) {
+ result = item;
+ break;
+ } else if (this.props.tree && item.children && item.children.length > 0) {
+ const temp = this.getRow(item.children, key, parentFlag);
+ if (temp) {
+ result = temp;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * 根据传入的数据集合及目标对象查找表格行记录
+ * @param rows 表格数据集合
+ * @param target 目标对象,可以是行对象、行对象数组、行主键、前端主键。
+ * @returns 行数据数组集合
+ */
+ public getRowsByTarget(rows: Array, target: any) {
+ const result = [];
+ if (target && Array.isArray(target) && target.length > 0) {
+ target.forEach((item) => {
+ this.rowsKeyIsEmptyError(item);
+ this.pushRow(rows, result, item);
+ });
+ } else if (!Tools.isEmpty(target)) {
+ this.pushRow(rows, result, target);
+ }
+ return result;
+ }
+
+ /**
+ * 判定传入的主键是否处于行选中状态
+ * @param rowKey 前端主键
+ */
+ public isSelectedRow(rowKey: string) {
+ if (this.table && !Tools.isEmpty(rowKey)) {
+ const selected = this.tools?.apiFM.getData.getSelectedRow();
+ if (selected) {
+ return rowKey === selected[Constant.FIELD_NAMES.ROW_KEY];
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 判定传入的行主键及列名是否处于单元格选中状态
+ * @param rowKey 前端主键
+ * @param colName 列名
+ */
+ public isSelectedCell(rowKey: string, colName: string) {
+ if (this.table && !Tools.isEmpty(rowKey) && !Tools.isEmpty(colName)) {
+ const selected = this.tools?.apiFM.getData.getSelectedCell();
+ if (selected?.colName && rowKey === selected['row'][Constant.FIELD_NAMES.ROW_KEY] && colName === selected.colName) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 设置内联编辑修改前的行数据值
+ * @param row 行数据
+ */
+ public setOldValue(row) {
+ const values = Object.values(Constant.FIELD_NAMES.getExtraFields(this.props));
+ Object.keys(row).forEach((key) => {
+ if (
+ values.findIndex((item) => {
+ return item === key;
+ }) < 0
+ ) {
+ row[Constant.FIELD_NAMES.ROW_OLD_VALUE][key] = toRaw(row[key]);
+ }
+ });
+ }
+
+ /**
+ * 设置表格附加属性及分组处理
+ * @param rows
+ * @returns
+ */
+ public async setExtraProperty(rows: Array, ops?: any, lazyloadNoChildren: boolean = false) {
+ // 清空分组记录
+ this.table.configStore.aloneGroupRecords = [];
+ // 重置分组行下标
+ this.aloneGroupRowIndex = 0;
+ if (this.props.groupMode === Constant.GROUP_MODE.MERGE && !this.props.tree) {
+ // 如果存在需要合并的字段,并且不是树表格,重新对数据进行排序
+ this.table.store.mergeGroupRecords = {};
+ const groupByFields = this.tools?.cm.mergeGroupByField.value;
+ this.arrayOrderBy(rows, groupByFields, ops);
+ }
+ this.rowDataExtraPropertyHandle(rows, lazyloadNoChildren);
+ }
+
+ /**
+ * 判断传入的行是否为数据的最后一行
+ * @param row 校验的行
+ */
+ isLastRow(row: any) {
+ if (this.tools?.table?.rows?.length === 0) {
+ return true;
+ }
+ const lastRow = this.tools?.table.rows[this.tools.table.rows.length - 1];
+ return this.checkLastRow(lastRow, row);
+ }
+
+ private checkLastRow(lastRow: any, row: any) {
+ if (this.tools?.props.tree && row[Constant.FIELD_NAMES.EXPAND] && !Tools.isEmpty(row.children) && row.children.length > 0) {
+ const childrenLastRow = row.children[row.children.length - 1];
+ if (childrenLastRow[Constant.FIELD_NAMES.EXPAND] && !Tools.isEmpty(childrenLastRow.children) && childrenLastRow.children.length > 0) {
+ return this.checkLastRow(lastRow, childrenLastRow);
+ } else {
+ return childrenLastRow[Constant.FIELD_NAMES.ROW_KEY] === row[Constant.FIELD_NAMES.ROW_KEY];
+ }
+ } else {
+ return lastRow[Constant.FIELD_NAMES.ROW_KEY] === row[Constant.FIELD_NAMES.ROW_KEY];
+ }
+ }
+
+ /**
+ * 对象数组根据依次根据属性名进行排序
+ * @param array 对象数组
+ * @param sortFields 属性名
+ */
+ private arrayOrderBy(array: Array, sortFields: any, ops: any) {
+ let fields = [...sortFields];
+ // 将分组字段与排序字段进行合并
+ fields = this.mergeSortFields(fields, ops);
+ const customOrders = {};
+ // 对每一个字段进行排序
+ for (const field of fields) {
+ const desc = field.startsWith('-');
+ const _field = desc ? field.substring(1) : field;
+ const fieldSortArr = array.map((item) => item[_field]);
+ sortArray(fieldSortArr, { order: desc ? 'desc' : 'asc' });
+ customOrders[_field] = fieldSortArr;
+ }
+ const byFields = fields.map((item) => (item.startsWith('-') ? item.substring(1) : item));
+ sortArray(array, {
+ by: byFields,
+ order: byFields,
+ customOrders: customOrders,
+ });
+ }
+
+ private mergeSortFields(fields: Array, ops: any) {
+ const sortBy = ops?.pagination?.sortBy;
+ if (sortBy) {
+ const prefix = ops.pagination.descending ? '-' : '';
+ if (fields.findIndex((tmp) => tmp === sortBy) === -1) {
+ fields.push(prefix + sortBy);
+ } else {
+ fields = fields.map((tmp) => {
+ if (tmp === sortBy) {
+ return prefix + sortBy;
+ }
+ return tmp;
+ });
+ }
+ return fields;
+ } else {
+ return this.mergeDefaultSortBy(fields);
+ }
+ }
+
+ private mergeDefaultSortBy(fields: Array) {
+ if (Array.isArray(this.tools?.props.sortBy) && this.tools?.props.sortBy.length > 0) {
+ this.tools.props.sortBy.forEach((item) => {
+ if (item.startsWith('-')) {
+ if (fields.findIndex((tmp) => tmp === item.substring(1)) === -1) {
+ fields.push(item);
+ } else {
+ fields = fields.map((tmp) => {
+ if (tmp === item.substring(1)) {
+ return item;
+ }
+ return tmp;
+ });
+ }
+ } else {
+ if (fields.findIndex((tmp) => tmp === item) === -1) {
+ fields.push(item);
+ }
+ }
+ });
+ }
+ return fields;
+ }
+
+ private rowDataExtraPropertyHandle(rows: Array, lazyloadNoChildren: boolean = false) {
+ if (rows && rows.length > 0) {
+ rows.forEach((item: any, index) => {
+ this.initRowDataExtraProperty(item, lazyloadNoChildren);
+ this.mergeGroupBy(item);
+ this.aloneGroupBy(item);
+ if (item[this.props.tickedField] === true) {
+ item[Constant.FIELD_NAMES.TICKED_COUNT] = 1;
+ } else if (item[Constant.FIELD_NAMES.TICKED_COUNT] === false) {
+ item[Constant.FIELD_NAMES.TICKED_COUNT] = 0;
+ } else {
+ item[Constant.FIELD_NAMES.TICKED_COUNT] = 0;
+ }
+ if (this.props.tree && item.children && item.children.length > 0) {
+ this.rowDataExtraPropertyHandle(item.children, lazyloadNoChildren);
+ item[Constant.FIELD_NAMES.CHILDREN_TICKED_COUNT] = 0;
+ item.children.forEach((child) => {
+ item[Constant.FIELD_NAMES.CHILDREN_TICKED_COUNT] += child[Constant.FIELD_NAMES.TICKED_COUNT];
+ });
+ if (item[Constant.FIELD_NAMES.CHILDREN_TICKED_COUNT] === 0) {
+ item[this.props.tickedField] = false;
+ item[Constant.FIELD_NAMES.TICKED_COUNT] = 0;
+ } else if (item[Constant.FIELD_NAMES.CHILDREN_TICKED_COUNT] === item.children.length) {
+ item[this.props.tickedField] = true;
+ item[Constant.FIELD_NAMES.TICKED_COUNT] = 1;
+ } else {
+ item[this.props.tickedField] = null;
+ item[Constant.FIELD_NAMES.TICKED_COUNT] = 0;
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * 合并模式分组
+ * @param rowData
+ */
+ private mergeGroupBy(rowData: any) {
+ if (this.props.groupMode === Constant.GROUP_MODE.MERGE && !this.props.tree) {
+ const groupByFields = this.tools?.cm.mergeGroupByField.value;
+ if (groupByFields && Array.isArray(groupByFields)) {
+ groupByFields.forEach((columnName: any) => {
+ const tmpArr = [rowData[Constant.FIELD_NAMES.ROW_KEY]];
+ if (this.table.store.mergeGroupRecords[columnName]) {
+ if (this.table.store.mergeGroupRecords[columnName][rowData[columnName]]) {
+ this.table.store.mergeGroupRecords[columnName][rowData[columnName]].push(rowData[Constant.FIELD_NAMES.ROW_KEY]);
+ } else {
+ this.table.store.mergeGroupRecords[columnName][rowData[columnName]] = tmpArr;
+ }
+ } else {
+ this.table.store.mergeGroupRecords[columnName] = { [rowData[columnName]]: tmpArr };
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * 单独模式分组
+ */
+ private aloneGroupBy(rowData: any) {
+ if (this.props.groupMode === Constant.GROUP_MODE.ALONE) {
+ const groupByField = this.tools?.table.configStore.aloneGroupByField;
+ if (groupByField) {
+ const expand = this.getAloneGroupDefaultExpand(rowData, groupByField);
+ const findResult = this.table.configStore.aloneGroupRecords.find((tmp) => tmp['groupName'] === rowData[groupByField]);
+ if (!findResult) {
+ this.table.configStore.aloneGroupRecords.push({ groupName: rowData[groupByField], expand: expand, rows: [rowData] });
+ } else {
+ findResult.rows.push(rowData);
+ }
+ this.setRowIndex(rowData);
+ }
+ }
+ }
+
+ /**
+ * 获得独立分组默认展开状态
+ * @param rowData
+ * @param groupByField
+ * @returns
+ */
+ private getAloneGroupDefaultExpand(rowData: any, groupByField: string) {
+ let expand = false;
+ if (typeof this.props.groupStartOpen === 'string' && this.props.groupStartOpen === Constant.GROUP_START_OPEN.ALL) {
+ expand = true;
+ } else if (
+ typeof this.props.groupStartOpen === 'string' &&
+ this.props.groupStartOpen === Constant.GROUP_START_OPEN.FIRST &&
+ this.aloneGroupRowIndex === 0
+ ) {
+ expand = true;
+ } else if (Array.isArray(this.props.groupStartOpen) && this.props.groupStartOpen.includes(rowData[groupByField])) {
+ expand = true;
+ } else if (typeof this.props.groupStartOpen === 'function') {
+ expand = this.props.groupStartOpen({
+ groupByField: rowData[groupByField],
+ row: rowData,
+ });
+ }
+ // 当前展开记录中已经有,强制改为展开状态
+ if (this.table.store.expandDatas.length > 0 && this.table.store.expandDatas.includes(rowData[groupByField])) {
+ expand = true;
+ }
+ return expand;
+ }
+
+ /**
+ * 设置行下标
+ * @param rowData
+ */
+ private setRowIndex(rowData: any) {
+ rowData[Constant.FIELD_NAMES.ROW_INDEX] = this.aloneGroupRowIndex;
+ this.aloneGroupRowIndex++;
+ }
+
+ /**
+ * 初始化行数据附加属性
+ * @param rowData 行数据对象
+ */
+ private async initRowDataExtraProperty(rowData: any, lazyloadNoChildren: boolean = false) {
+ rowData[Constant.FIELD_NAMES.ROW_KEY] = Tools.uuid();
+ rowData[this.props.tickedField] = this.getInitRowTicked(rowData);
+ rowData[this.props.selectedField] = this.getInitRowSelected(rowData);
+ rowData[Constant.FIELD_NAMES.ROW_OLD_VALUE] = {};
+ this.setOldValue(rowData);
+ if (this.props.tree) {
+ if (this.table.store.expandDatas.length > 0 && this.table.store.expandDatas.includes(rowData[this.props.primaryKey])) {
+ rowData[Constant.FIELD_NAMES.EXPAND] = true;
+ } else if (Tools.isEmpty(rowData[Constant.FIELD_NAMES.EXPAND])) {
+ rowData[Constant.FIELD_NAMES.EXPAND] = false;
+ }
+ if (lazyloadNoChildren && (!rowData.children || rowData.children.length === 0)) {
+ rowData[Constant.FIELD_NAMES.LAZYLOAD_NO_CHILDREN] = true;
+ }
+ }
+ }
+
+ /**
+ * 获取初始化行的ticked状态
+ * @param rowData
+ * @returns
+ */
+ private getInitRowTicked(rowData: any) {
+ if (rowData[this.props.tickedField]) {
+ return true;
+ } else if (Object.keys(this.props.tickedRecord).length > 0 && this.props.tickedRecord['data']?.length > 0) {
+ const columnName = this.props.tickedRecord['columnName'] || this.props.primaryKey;
+ const data = this.props.tickedRecord['data'];
+ if (
+ data.findIndex((item) => {
+ return item === rowData[columnName];
+ }) > -1
+ ) {
+ return true;
+ }
+ }
+ return false;
+ }
+ /**
+ * 获取初始化行的selected状态
+ * @param rowData
+ * @returns
+ */
+ private getInitRowSelected(rowData: any) {
+ if (rowData[this.props.selectedField]) {
+ return true;
+ } else if (!this.props.tree && rowData[this.props.tickedField]) {
+ return true;
+ } else if (this.props.tree && !this.props.checkboxSelection && rowData[this.props.tickedField]) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private pushRow(rows: Array, result: Array, item: any) {
+ const row = this.getRow(
+ rows,
+ typeof item === 'object' ? item[this.props.primaryKey] || item[Constant.FIELD_NAMES.ROW_KEY] : item,
+ false,
+ item[this.props.primaryKey] ? this.props.primaryKey : Constant.FIELD_NAMES.ROW_KEY,
+ );
+ if (row) {
+ result.push(row);
+ } else {
+ this.rowsKeyNotExistError(typeof item === 'object' ? item[this.props.primaryKey] || item[Constant.FIELD_NAMES.ROW_KEY] : item);
+ }
+ }
+ private rowsKeyIsEmptyError(data: any) {
+ if (typeof data === 'object' && Tools.isEmpty(data[this.props.primaryKey]) && Tools.isEmpty(data[Constant.FIELD_NAMES.ROW_KEY])) {
+ throw new Error('[w-grid]Data does not contain `primaryKey` or `rowKey`.');
+ }
+ }
+ private rowsKeyNotExistError(data: any) {
+ throw new Error('[w-grid]`' + data + '` Not Found in Data Row.');
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/grid.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/grid.ts
deleted file mode 100644
index 85e8944d..00000000
--- a/io.sc.platform.core.frontend/src/platform/components/grid/ts/grid.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-import { Tools, t } from '@/platform';
-
-// 拖拽排序模式
-export const dndMode = {
- local: 'local', // 本地排序
- server: 'server', // 服务端排序
-};
-export const dndImage = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' %3E%3Cpath /%3E%3C/svg%3E`;
-
-// 选择模式
-export const selectMode = {
- none: 'none', // 不允许选择
- row: 'row', // 行选择
- cell: 'cell', // 单元格选择
-};
-
-// 分组模式
-export const groupMode = {
- alone: 'alone', // 单独行进行分组
- merge: 'merge', // 合并行进行分组
-};
-
-// 表格内容区域快速编辑状态
-export const editStatus = {
- none: 'none', // 不在编辑状态
- cell: 'cellEdit', // 单元格编辑状态
- row: 'rowEdit', // 行编辑状态
- rows: 'rowsEdit', // 所有行编辑状态
-};
-
-// 内置编辑窗口中form的状态
-export const formStatus = {
- add: 'add', // 新增
- edit: 'edit', // 编辑
- clone: 'clone', // 复制
- addTop: 'addTop', // 新增顶级节点
- addChild: 'addChild', // 新增子节点
-};
-
-// 列样式处理
-const columnStyle = (item: any) => {
- let style = '';
- if (Tools.hasOwnProperty(item, 'style')) {
- style = item.style;
- }
- if (Tools.hasOwnProperty(item, 'width')) {
- if (typeof item.width === 'number') {
- item.style = `min-width: ` + item.width + `px; width: ` + item.width + `px;max-width: ` + item.width + `px;` + style;
- } else {
- item.style = `min-width: ` + item.width + `; width: ` + item.width + `;max-width: ` + item.width + `;` + style;
- }
- delete item.width;
-
- if (Tools.hasOwnProperty(item, 'classes')) {
- item.classes = item.classes + ' truncate';
- } else {
- item.classes = 'truncate';
- }
- }
-};
-
-// 孩子列处理
-const childrenHandler = (item: any, gridColumns: any) => {
- if (item.columns && item.columns.length > 0) {
- item.columns.forEach((column) => {
- childrenHandler(column, gridColumns);
- });
- } else {
- columnStyle(item);
- const col = {
- ...{ align: 'left', label: item.name, field: item.name, name: item.name, sortable: true, showIf: true },
- ...item,
- };
- if (Tools.isEmpty(col.name)) {
- col.name = Tools.uuid();
- }
- gridColumns.push(col);
- }
-};
-
-// 列的默认属性
-export const columnDefaultProps = (columns: any, sortNo: boolean) => {
- const gridColumns = [];
- if (columns && columns.length > 0) {
- gridColumns.push({ name: '_sortNo_', align: 'center', label: t('rownum'), field: '_sortNo_', showIf: sortNo });
- columns.forEach((item: any) => {
- childrenHandler(item, gridColumns);
- });
- return gridColumns;
- }
- return [];
-};
-
-export const sortByProperties = (arr, properties) => {
- for (const prop of properties) {
- arr.sort((a, b) => {
- if (a[prop] < b[prop]) {
- return -1;
- } else if (a[prop] > b[prop]) {
- return 1;
- }
- return 0;
- });
- }
-};
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/index.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/index.ts
new file mode 100644
index 00000000..b7d03ee4
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/index.ts
@@ -0,0 +1,14 @@
+import { Constant } from './constant/Constant';
+
+import { EventManager } from './event/EventManager';
+import { ExposeApiManager } from './expose-api/ExposeApiManager';
+import { Init } from './Init';
+
+import type { TableType } from './types/TableType';
+import type { EventType } from './types/EventType';
+import type { PropsType } from './types/PropsType';
+import type { UrlType } from './types/table/UrlType';
+
+import { GridTools } from './GridTools';
+
+export { Constant, EventManager, ExposeApiManager, Init, EventType, PropsType, UrlType, TableType, GridTools };
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/Button.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/Button.ts
new file mode 100644
index 00000000..09873cbb
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/Button.ts
@@ -0,0 +1,55 @@
+import { Tools } from '@/platform';
+import { Loading } from 'quasar';
+import { nextTick } from 'vue';
+import { Base } from '../Base';
+/**
+ * w-grid 内置按钮超类
+ */
+export abstract class Button extends Base {
+ /**
+ * 按钮名称
+ */
+ abstract name: string;
+
+ /**
+ * 获取按钮配置到toolbar中的配置属性
+ */
+ abstract getButtonConfig(args: any): object | string;
+
+ /**
+ * 打开editor窗口
+ * @param grid 表格实例对象
+ * @param title 窗口标题
+ * @param status 窗口中Form的状态
+ * @param data 编辑的数据,新增为 undefined
+ */
+ openEditor(grid: any, title: string, status: string, data: any) {
+ grid.getEditorDialog().show();
+ nextTick(() => {
+ grid.getEditorDialog().setTitle(title);
+ grid.getEditorForm().setStatus(status);
+ if (!Tools.isEmpty(data)) {
+ grid.getEditorForm().setData(data);
+ }
+ });
+ }
+
+ /**
+ * 显示处理中/加载中遮罩
+ * @param msg
+ */
+ showLoading(msg: string = '正在处理,请稍等...') {
+ Loading.show({
+ message: msg,
+ boxClass: 'bg-grey-2 text-grey-9',
+ spinnerColor: 'primary',
+ });
+ }
+
+ /**
+ * 隐藏处理中/加载中遮罩
+ */
+ hideLoading() {
+ Loading.hide();
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/ButtonManager.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/ButtonManager.ts
new file mode 100644
index 00000000..67f5884f
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/ButtonManager.ts
@@ -0,0 +1,227 @@
+import { Tools } from '@/platform';
+import { GridTools } from '../GridTools';
+import { PropsType } from '../types/PropsType';
+
+import { TableType } from '../types/TableType';
+import { Add } from './buttons/Add';
+import { AddChild } from './buttons/AddChild';
+import { AddTop } from './buttons/AddTop';
+import { CellEdit } from './buttons/CellEdit';
+import { Clone } from './buttons/Clone';
+import { Edit } from './buttons/Edit';
+import { Expand } from './buttons/Expand';
+import { Export } from './buttons/Export';
+import { InlineCellEdit } from './buttons/InlineCellEdit';
+import { InlineRowEdit } from './buttons/InlineRowEdit';
+import { InlineRowsEdit } from './buttons/InlineRowsEdit';
+import { MoreQuery } from './buttons/MoreQuery';
+import { Query } from './buttons/Query';
+import { AdvancedQuery } from './buttons/AdvancedQuery';
+import { Refresh } from './buttons/Refresh';
+import { Remove } from './buttons/Remove';
+import { Reset } from './buttons/Reset';
+import { ResetDefaultValues } from './buttons/ResetDefaultValues';
+import { Separator } from './buttons/Separator';
+import { View } from './buttons/view';
+import { FullScreen } from './buttons/FullScreen';
+
+/**
+ * w-grid 内置按钮管理器
+ */
+export class ButtonManager {
+ // 查询
+ query: Query;
+ moreQuery: MoreQuery;
+ advancedQuery: AdvancedQuery;
+ reset: Reset;
+ refresh: Refresh;
+
+ // 新增
+ add: Add;
+ addTop: AddTop;
+ addChild: AddChild;
+
+ // 编辑
+ edit: Edit;
+ cellEdit: CellEdit;
+ inlineCellEdit: InlineCellEdit;
+ inlineRowEdit: InlineRowEdit;
+ inlineRowsEdit: InlineRowsEdit;
+ clone: Clone;
+
+ // 删除
+ remove: Remove;
+
+ // 其他
+ fullScreen: FullScreen;
+ separator: Separator;
+ view: View;
+ export: Export;
+ expand: Expand;
+ resetDefaultValues: ResetDefaultValues;
+
+ constructor(props: PropsType, table: TableType) {
+ this.query = new Query(props, table);
+ this.moreQuery = new MoreQuery(props, table);
+ this.advancedQuery = new AdvancedQuery(props, table);
+ this.reset = new Reset(props, table);
+ this.refresh = new Refresh(props, table);
+
+ this.add = new Add(props, table);
+ this.addTop = new AddTop(props, table);
+ this.addChild = new AddChild(props, table);
+
+ this.edit = new Edit(props, table);
+ this.cellEdit = new CellEdit(props, table);
+ this.inlineCellEdit = new InlineCellEdit(props, table);
+ this.inlineRowEdit = new InlineRowEdit(props, table);
+ this.inlineRowsEdit = new InlineRowsEdit(props, table);
+ this.clone = new Clone(props, table);
+
+ this.remove = new Remove(props, table);
+
+ this.fullScreen = new FullScreen(props, table);
+ this.separator = new Separator(props, table);
+ this.view = new View(props, table);
+ this.export = new Export(props, table);
+ this.expand = new Expand(props, table);
+ this.resetDefaultValues = new ResetDefaultValues(props, table);
+ }
+
+ /**
+ * 获取按钮配置
+ * @param name 按钮名称,不传入则以`{ 按钮名称:按钮配置 }`对象的形式返回所有按钮配置。
+ * @returns
+ */
+ getButtonConfig(name?: string) {
+ let config = {};
+ if (Tools.isEmpty(name)) {
+ config = {
+ [this.query.name]: this.query.getButtonConfig(),
+ [this.moreQuery.name]: this.moreQuery.getButtonConfig(),
+ [this.advancedQuery.name]: this.advancedQuery.getButtonConfig(),
+ [this.reset.name]: this.reset.getButtonConfig(),
+ [this.refresh.name]: this.refresh.getButtonConfig(),
+
+ [this.add.name]: this.add.getButtonConfig(),
+ [this.addTop.name]: this.addTop.getButtonConfig(),
+ [this.addChild.name]: this.addChild.getButtonConfig(),
+
+ [this.edit.name]: this.edit.getButtonConfig(),
+ [this.cellEdit.name]: this.cellEdit.getButtonConfig(),
+ [this.inlineCellEdit.name]: this.inlineCellEdit.getButtonConfig(),
+ [this.inlineRowEdit.name]: this.inlineRowEdit.getButtonConfig(),
+ [this.inlineRowsEdit.name]: this.inlineRowsEdit.getButtonConfig(),
+ [this.clone.name]: this.clone.getButtonConfig(),
+
+ [this.remove.name]: this.remove.getButtonConfig(),
+
+ [this.fullScreen.name]: this.fullScreen.getButtonConfig(),
+ [this.separator.name]: this.separator.getButtonConfig(),
+ [this.view.name]: this.view.getButtonConfig(),
+ [this.export.name]: this.export.getButtonConfig(),
+ [this.expand.name]: this.expand.getButtonConfig(),
+ [this.resetDefaultValues.name]: this.resetDefaultValues.getButtonConfig(),
+ };
+ } else {
+ switch (name) {
+ case this.query.name:
+ config = this.query.getButtonConfig();
+ break;
+ case this.moreQuery.name:
+ config = this.moreQuery.getButtonConfig();
+ break;
+ case this.advancedQuery.name:
+ config = this.advancedQuery.getButtonConfig();
+ break;
+ case this.reset.name:
+ config = this.reset.getButtonConfig();
+ break;
+ case this.refresh.name:
+ config = this.refresh.getButtonConfig();
+ break;
+
+ case this.add.name:
+ config = this.add.getButtonConfig();
+ break;
+ case this.addTop.name:
+ config = this.addTop.getButtonConfig();
+ break;
+ case this.addChild.name:
+ config = this.addChild.getButtonConfig();
+ break;
+
+ case this.edit.name:
+ config = this.edit.getButtonConfig();
+ break;
+ case this.cellEdit.name:
+ config = this.cellEdit.getButtonConfig();
+ break;
+ case this.inlineCellEdit.name:
+ config = this.inlineCellEdit.getButtonConfig();
+ break;
+ case this.inlineRowEdit.name:
+ config = this.inlineRowEdit.getButtonConfig();
+ break;
+ case this.inlineRowsEdit.name:
+ config = this.inlineRowsEdit.getButtonConfig();
+ break;
+ case this.clone.name:
+ config = this.clone.getButtonConfig();
+ break;
+
+ case this.remove.name:
+ config = this.remove.getButtonConfig();
+ break;
+
+ case this.fullScreen.name:
+ config = this.fullScreen.getButtonConfig();
+ break;
+ case this.separator.name:
+ config = this.separator.getButtonConfig();
+ break;
+ case this.view.name:
+ config = this.view.getButtonConfig();
+ break;
+ case this.export.name:
+ config = this.export.getButtonConfig();
+ break;
+ case this.expand.name:
+ config = this.expand.getButtonConfig();
+ break;
+ case this.resetDefaultValues.name:
+ config = this.resetDefaultValues.getButtonConfig();
+ break;
+ }
+ }
+ return config;
+ }
+
+ setInstance(instance: any, tools: GridTools) {
+ this.query.setInstance(instance, tools);
+ this.moreQuery.setInstance(instance, tools);
+ this.advancedQuery.setInstance(instance, tools);
+ this.reset.setInstance(instance, tools);
+ this.refresh.setInstance(instance, tools);
+
+ this.add.setInstance(instance, tools);
+ this.addTop.setInstance(instance, tools);
+ this.addChild.setInstance(instance, tools);
+
+ this.edit.setInstance(instance, tools);
+ this.cellEdit.setInstance(instance, tools);
+ this.inlineCellEdit.setInstance(instance, tools);
+ this.inlineRowEdit.setInstance(instance, tools);
+ this.inlineRowsEdit.setInstance(instance, tools);
+ this.clone.setInstance(instance, tools);
+
+ this.remove.setInstance(instance, tools);
+
+ this.fullScreen.setInstance(instance, tools);
+ this.separator.setInstance(instance, tools);
+ this.view.setInstance(instance, tools);
+ this.export.setInstance(instance, tools);
+ this.expand.setInstance(instance, tools);
+ this.resetDefaultValues.setInstance(instance, tools);
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Add.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Add.ts
new file mode 100644
index 00000000..64c12917
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Add.ts
@@ -0,0 +1,30 @@
+import { $t } from '@/platform';
+import { Button } from '../Button';
+import { Constant, PropsType, TableType } from '../../index';
+
+export class Add extends Button {
+ name = 'add';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ click(args) {
+ this.openEditor(args.grid, $t('action.addNew'), Constant.FORM_STATUS.ADD, undefined);
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: 'add',
+ labelI18nKey: 'action.addNew',
+ label: $t('action.addNew'),
+ click: this.click,
+ afterEditorOpen: (args) => {
+ this.tools?.em.afterEditorOpen(undefined);
+ },
+ };
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/AddChild.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/AddChild.ts
new file mode 100644
index 00000000..b933bd76
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/AddChild.ts
@@ -0,0 +1,36 @@
+import { $t } from '@/platform';
+import { Button } from '../Button';
+import { Constant, PropsType, TableType } from '../../index';
+
+export class AddChild extends Button {
+ name = 'addChild';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ click(args) {
+ this.openEditor(args.grid, $t('action.addChild'), Constant.FORM_STATUS.ADD_CHILD, undefined);
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: 'playlist_add',
+ labelI18nKey: 'action.addChild',
+ label: $t('action.addChild'),
+ enableIf: (args) => {
+ if (args.selected) {
+ return true;
+ }
+ return false;
+ },
+ click: this.click,
+ afterEditorOpen: (args) => {
+ this.tools?.em.afterEditorOpen(undefined);
+ },
+ };
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/AddTop.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/AddTop.ts
new file mode 100644
index 00000000..a7507df2
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/AddTop.ts
@@ -0,0 +1,30 @@
+import { $t } from '@/platform';
+import { Button } from '../Button';
+import { Constant, PropsType, TableType } from '../../index';
+
+export class AddTop extends Button {
+ name = 'addTop';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ click(args) {
+ this.openEditor(args.grid, $t('action.addTop'), Constant.FORM_STATUS.ADD_TOP, undefined);
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: 'add',
+ labelI18nKey: 'action.addTop',
+ label: $t('action.addTop'),
+ click: (args) => {},
+ afterEditorOpen: (args) => {
+ this.tools?.em.afterEditorOpen(undefined);
+ },
+ };
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/AdvancedQuery.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/AdvancedQuery.ts
new file mode 100644
index 00000000..a2d287c0
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/AdvancedQuery.ts
@@ -0,0 +1,34 @@
+import { $t } from '@/platform';
+import { PropsType, TableType } from '../../index';
+import { Button } from '../Button';
+
+export class AdvancedQuery extends Button {
+ name = 'advancedQuery';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ click(args) {
+ this.table.advancedQueryStatus = !this.table.advancedQueryStatus;
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: 'manage_search',
+ labelI18nKey: 'action.advancedQuery',
+ label: $t('action.advancedQuery'),
+ enableIf: (args) => {
+ if (this.props.queryFormFields.length > 0 && this.props.advancedQuery) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ click: this.click,
+ };
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/CellEdit.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/CellEdit.ts
new file mode 100644
index 00000000..7157868b
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/CellEdit.ts
@@ -0,0 +1,45 @@
+import { $t, NotifyManager, Tools } from '@/platform';
+import { Button } from '../Button';
+import { Constant, PropsType, TableType } from '../../index';
+
+export class CellEdit extends Button {
+ name = 'cellEdit';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ click(args) {
+ const selectedCell = args.grid.getSelectedCell();
+ if (args.grid.props.selectMode !== Constant.SELECT_MODE.CELL) {
+ console.warn('[w-grid]`selectMode` property is not `cell`, Cannot use cell editing function.');
+ return false;
+ } else if (Tools.isEmpty(selectedCell.colName)) {
+ NotifyManager.info('请选择要编辑的单元格');
+ return false;
+ } else if (!this.tools?.editFM.getEditorFieldByName(selectedCell.colName)) {
+ console.warn('[w-grid]The column selected is not configured with a component type for editing.');
+ return false;
+ }
+ // 弹出模态框编辑,但是只编辑该列的值
+ args.grid.getCellEditorDialog().show();
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: 'border_color',
+ labelI18nKey: 'action.edit',
+ label: $t('action.edit'),
+ enableIf: (args) => {
+ if (!Tools.isEmpty(args.grid.getSelectedCell()['colName'])) {
+ return true;
+ }
+ return false;
+ },
+ click: this.click,
+ };
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Clone.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Clone.ts
new file mode 100644
index 00000000..d79f5e5a
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Clone.ts
@@ -0,0 +1,41 @@
+import { $t, NotifyManager } from '@/platform';
+import { Button } from '../Button';
+import { Constant, PropsType, TableType } from '../../index';
+
+export class Clone extends Button {
+ name = 'clone';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ click(args) {
+ if (!args.selected) {
+ NotifyManager.warn($t('action.copy.tip'));
+ } else {
+ args.selected[args.grid.props.primaryKey] = undefined;
+ this.openEditor(args.grid, $t('action.copy'), Constant.FORM_STATUS.CLONE, args.selected);
+ }
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: 'content_copy',
+ labelI18nKey: 'action.copy',
+ label: $t('action.copy'),
+ enableIf: (args) => {
+ if (args.selected) {
+ return true;
+ }
+ return false;
+ },
+ click: this.click,
+ afterEditorOpen: (args) => {
+ this.tools?.em.afterEditorOpen(args.selected);
+ },
+ };
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Edit.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Edit.ts
new file mode 100644
index 00000000..224a4ff6
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Edit.ts
@@ -0,0 +1,40 @@
+import { $t, NotifyManager } from '@/platform';
+import { Button } from '../Button';
+import { Constant, PropsType, TableType } from '../../index';
+
+export class Edit extends Button {
+ name = 'edit';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ click(args) {
+ if (!args.selected) {
+ NotifyManager.warn($t('action.edit.tip'));
+ } else {
+ this.openEditor(args.grid, $t('action.edit'), Constant.FORM_STATUS.EDIT, args.selected);
+ }
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: 'edit',
+ labelI18nKey: 'action.edit',
+ label: $t('action.edit'),
+ enableIf: (args) => {
+ if (args.selected) {
+ return true;
+ }
+ return false;
+ },
+ click: this.click,
+ afterEditorOpen: (args) => {
+ this.tools?.em.afterEditorOpen(args.selected);
+ },
+ };
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Expand.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Expand.ts
new file mode 100644
index 00000000..0a4297b6
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Expand.ts
@@ -0,0 +1,42 @@
+import { $t } from '@/platform';
+import { PropsType, TableType } from '../../index';
+import { Button } from '../Button';
+
+export class Expand extends Button {
+ name = 'expand';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ async click(args) {
+ if (this.table.store.expand) {
+ this.tools?.apiFM.operator.collapse(undefined, true);
+ } else {
+ if (this.props.tree && this.props.treeLazyLoad && !this.table.store.alreadyLoadAllData) {
+ this.table.store.alreadyLoadAllData = true;
+ this.table.store.loading = true;
+ const datas = await this.tools?.reqApiFM.treeFetchDataByForeignKey();
+ this.tools?.apiFM.localMode.setLocalData(datas);
+ this.table.store.loading = false;
+ }
+ this.tools?.apiFM.operator.expand(undefined, true);
+ }
+ this.table.store.expand = !this.table.store.expand;
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: () => {
+ return this.table.store.expand ? 'expand_less' : 'expand_more';
+ },
+ label: () => {
+ return this.table.store.expand ? $t('action.collapseAll') : $t('action.expandAll');
+ },
+ click: this.click,
+ };
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Export.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Export.ts
new file mode 100644
index 00000000..578e3571
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Export.ts
@@ -0,0 +1,89 @@
+import { $t, axios, NotifyManager, Tools } from '@/platform';
+import { exportFile } from 'quasar';
+import { PropsType, TableType } from '../../index';
+import { Button } from '../Button';
+
+export class Export extends Button {
+ name = 'export';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getExportData = this.getExportData.bind(this);
+ this.wrapCsvValue = this.wrapCsvValue.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ async click(args) {
+ this.showLoading();
+ let exportData = args.grid.getRows();
+ // 判断是否配置了 url, 以不分页形式请求后端获取全部数据一把导出。
+ if (!Tools.isEmpty(args.grid.props.fetchDataUrl) || !Tools.isEmpty(args.grid.props.dataUrl)) {
+ const fetchResult = await this.getExportData();
+ if (fetchResult && fetchResult.length > 0) {
+ exportData = fetchResult;
+ }
+ }
+ const content = [args.grid.props.columns.map((col) => this.wrapCsvValue(col.label))]
+ .concat(
+ exportData.map((row) =>
+ args.grid.props.columns
+ .map((col) =>
+ this.wrapCsvValue(typeof col.field === 'function' ? col.field(row) : row[col.field === void 0 ? col.name : col.field], col.format, row),
+ )
+ .join(','),
+ ),
+ )
+ .join('\r\n');
+
+ const status = exportFile('table-export.csv', content, {
+ encoding: 'utf-8',
+ mimeType: 'text/csv',
+ byteOrderMark: '\uFEFF', // 解决乱码问题
+ });
+
+ if (status !== true) {
+ NotifyManager.error($t('action.export.failed'));
+ }
+ this.hideLoading();
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: 'file_download',
+ labelI18nKey: 'action.export',
+ label: $t('action.export'),
+ click: this.click,
+ };
+ }
+
+ private async getExportData() {
+ let resultData = [];
+ const reqParams: any = { pageable: false };
+ const urlSearchParams = this.tools?.criteriaFM.buildURLSearchParams(reqParams);
+ const resp = await axios.get(this.table?.url.fetchDataUrl || this.table.url.dataUrl, { params: urlSearchParams });
+ if (resp && resp.data) {
+ const responseData = resp.data;
+ if (Array.isArray(responseData)) {
+ resultData = responseData;
+ } else if (typeof responseData === 'object' && responseData.content) {
+ resultData = responseData.content;
+ }
+ }
+ return resultData;
+ }
+
+ private wrapCsvValue(val, formatFn, row) {
+ let formatted = formatFn !== void 0 ? formatFn(val, row) : val;
+ formatted = formatted === void 0 || formatted === null ? '' : String(formatted);
+ formatted = formatted.split('"').join('""');
+ /**
+ * Excel accepts \n and \r in strings, but some other CSV parsers do not
+ * Uncomment the next two lines to escape new lines
+ */
+ // .split('\n').join('\\n')
+ // .split('\r').join('\\r')
+ return `"${formatted}"`;
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/FullScreen.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/FullScreen.ts
new file mode 100644
index 00000000..cd98a9a3
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/FullScreen.ts
@@ -0,0 +1,30 @@
+import { $t } from '@/platform';
+import { PropsType, TableType } from '../../index';
+import { Button } from '../Button';
+
+export class FullScreen extends Button {
+ name = 'fullScreen';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ click(args) {
+ this.table.componentRef.getTableRef().toggleFullscreen();
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: () => {
+ return this.table.configStore.isFullscreen ? 'fullscreen_exit' : 'fullscreen';
+ },
+ label: () => {
+ return this.table.configStore.isFullscreen ? $t('logout') : $t('fullScreen');
+ },
+ click: this.click,
+ };
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/InlineCellEdit.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/InlineCellEdit.ts
new file mode 100644
index 00000000..f125e269
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/InlineCellEdit.ts
@@ -0,0 +1,44 @@
+import { $t, Tools, NotifyManager } from '@/platform';
+import { Button } from '../Button';
+import { Constant, PropsType, TableType } from '../../index';
+
+export class InlineCellEdit extends Button {
+ name = 'inlineCellEdit';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ click(args) {
+ const selectedCell = args.grid.getSelectedCell();
+ if (args.grid.props.selectMode !== Constant.SELECT_MODE.CELL) {
+ console.warn('[w-grid]`selectMode` property is not `cell`, Cannot use cell editing function.');
+ return false;
+ } else if (Tools.isEmpty(selectedCell.colName)) {
+ NotifyManager.info('请选择要编辑的单元格');
+ return false;
+ } else if (!this.tools?.editFM.getEditorFieldByName(selectedCell.colName)) {
+ console.warn('[w-grid]The column selected is not configured with a component type for editing.');
+ return false;
+ }
+ this.table.store.inlineEditStatus = Constant.EDIT_STATUS.CELL;
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: 'border_color',
+ labelI18nKey: 'action.edit',
+ label: $t('action.edit'),
+ enableIf: (args) => {
+ if (!Tools.isEmpty(args.grid.getSelectedCell()['colName'])) {
+ return true;
+ }
+ return false;
+ },
+ click: this.click,
+ };
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/InlineRowEdit.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/InlineRowEdit.ts
new file mode 100644
index 00000000..07fb53e7
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/InlineRowEdit.ts
@@ -0,0 +1,37 @@
+import { $t } from '@/platform';
+import { Button } from '../Button';
+import { Constant, PropsType, TableType } from '../../index';
+
+export class InlineRowEdit extends Button {
+ name = 'inlineRowEdit';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ click(args) {
+ if (this.props?.editor?.form?.fields?.length > 0) {
+ this.table.store.inlineEditStatus = Constant.EDIT_STATUS.ROW;
+ } else {
+ console.warn('[w-grid]Not configured with a component type for editing.');
+ }
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: 'edit_note',
+ labelI18nKey: 'action.edit',
+ label: $t('action.edit'),
+ enableIf: (args) => {
+ if (args.selected) {
+ return true;
+ }
+ return false;
+ },
+ click: this.click,
+ };
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/InlineRowsEdit.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/InlineRowsEdit.ts
new file mode 100644
index 00000000..f7bf94d0
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/InlineRowsEdit.ts
@@ -0,0 +1,35 @@
+import { $t } from '@/platform';
+import { Button } from '../Button';
+import { Constant, PropsType, TableType } from '../../index';
+
+export class InlineRowsEdit extends Button {
+ name = 'inlineRowsEdit';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ click(args) {
+ if (this.props.editor?.form?.fields?.length > 0) {
+ this.table.store.inlineEditStatus = Constant.EDIT_STATUS.ROWS;
+ // 清空勾选与选中
+ args.grid.clearSelected();
+ args.grid.clearTicked();
+ this.table.store.headerTicked = false;
+ } else {
+ console.warn('[w-grid]Not configured with a component type for editing.');
+ }
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: 'app_registration',
+ labelI18nKey: 'action.edit',
+ label: $t('action.edit'),
+ click: this.click,
+ };
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/MoreQuery.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/MoreQuery.ts
new file mode 100644
index 00000000..f6e74612
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/MoreQuery.ts
@@ -0,0 +1,35 @@
+import { $t } from '@/platform';
+import { Button } from '../Button';
+import { Constant, PropsType, TableType } from '../../index';
+
+export class MoreQuery extends Button {
+ name = 'moreQuery';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ click(args) {
+ this.table.moreQueryStatus = !this.table.moreQueryStatus;
+ this.table.componentRef.getTopRef()?.handleQueryFormShowField();
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: 'zoom_in',
+ labelI18nKey: 'action.moreQueryConditions',
+ label: $t('action.moreQueryConditions'),
+ enableIf: (args) => {
+ if (this.props.queryFormFields.length <= this.table.queryFormDisplayFields.length && !this.table.moreQueryStatus) {
+ return false;
+ } else {
+ return true;
+ }
+ },
+ click: this.click,
+ };
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Query.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Query.ts
new file mode 100644
index 00000000..93552787
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Query.ts
@@ -0,0 +1,27 @@
+import { $t } from '@/platform';
+import { PropsType, TableType } from '../../index';
+import { Button } from '../Button';
+
+export class Query extends Button {
+ name = 'query';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ click(args) {
+ this.tools?.apiFM.operator.refreshGrid();
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: 'search',
+ labelI18nKey: 'action.query',
+ label: $t('action.query'),
+ click: this.click,
+ };
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Refresh.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Refresh.ts
new file mode 100644
index 00000000..8e78563a
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Refresh.ts
@@ -0,0 +1,27 @@
+import { $t } from '@/platform';
+import { PropsType, TableType } from '../../index';
+import { Button } from '../Button';
+
+export class Refresh extends Button {
+ name = 'refresh';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ click(args) {
+ this.tools?.apiFM.operator.refreshGrid();
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: 'loop',
+ labelI18nKey: 'action.refresh',
+ label: $t('action.refresh'),
+ click: this.click,
+ };
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Remove.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Remove.ts
new file mode 100644
index 00000000..709b27ed
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Remove.ts
@@ -0,0 +1,80 @@
+import { $t, DialogManager } from '@/platform';
+import { Constant, PropsType, TableType } from '../../index';
+import { Button } from '../Button';
+
+export class Remove extends Button {
+ name = 'remove';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ this.remove = this.remove.bind(this);
+ this.getIds = this.getIds.bind(this);
+ }
+
+ click(args: any, tips: boolean = true) {
+ if (!tips) {
+ this.remove(args);
+ } else {
+ DialogManager.yesnoDialog({
+ title: $t('confirm') || '',
+ message: $t('action.remove.tip') || '',
+ yesCallback: () => {
+ this.remove(args);
+ },
+ });
+ }
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: 'delete',
+ labelI18nKey: 'action.remove',
+ label: $t('action.remove'),
+ enableIf: (args) => {
+ if (args.ticked) {
+ return true;
+ } else if (args.selected) {
+ return true;
+ }
+ return false;
+ },
+ click: this.click,
+ };
+ }
+
+ private async remove(args: any) {
+ let ids = this.getIds(args);
+ // 触发请求前事件
+ const eventResult = await this.tools?.em.beforeRemove(ids);
+ if (eventResult?.ids.length > 0) {
+ ids = eventResult.ids;
+ }
+ if (eventResult.submit) {
+ if (this.props.localMode) {
+ this.tools?.apiFM.localMode.removeLocalData(ids);
+ } else {
+ this.tools?.reqApiFM.remove(ids);
+ }
+ }
+ }
+
+ // 准备id集合
+ private getIds(args): Array {
+ const ids = [];
+ const tickedRows = args.tickeds;
+ const selectedRows = args.selecteds;
+ if (tickedRows?.length > 0) {
+ tickedRows.forEach((item) => {
+ ids.push(this.props.localMode ? item[Constant.FIELD_NAMES.ROW_KEY] : item[this.props.primaryKey]);
+ });
+ } else if (selectedRows?.length > 0) {
+ selectedRows.forEach((item) => {
+ ids.push(this.props.localMode ? item[Constant.FIELD_NAMES.ROW_KEY] : item[this.props.primaryKey]);
+ });
+ }
+ return ids;
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Reset.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Reset.ts
new file mode 100644
index 00000000..62763610
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Reset.ts
@@ -0,0 +1,27 @@
+import { $t } from '@/platform';
+import { PropsType, TableType } from '../../index';
+import { Button } from '../Button';
+
+export class Reset extends Button {
+ name = 'reset';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ click(args) {
+ args.grid.getQueryForm().reset();
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: 'restart_alt',
+ labelI18nKey: 'action.reset',
+ label: $t('action.reset'),
+ click: this.click,
+ };
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/ResetDefaultValues.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/ResetDefaultValues.ts
new file mode 100644
index 00000000..592e2c55
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/ResetDefaultValues.ts
@@ -0,0 +1,53 @@
+import { $t, axios, DialogManager, NotifyManager } from '@/platform';
+import { PropsType, TableType } from '../../index';
+import { Button } from '../Button';
+
+export class ResetDefaultValues extends Button {
+ name = 'resetDefaultValues';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ this.resetDefaultValues = this.resetDefaultValues.bind(this);
+ }
+
+ click(args, tips: boolean = true) {
+ if (!tips) {
+ this.resetDefaultValues(args);
+ } else {
+ DialogManager.yesnoDialog({
+ title: $t('confirm'),
+ message: $t('action.resetDefaultValues.tip'),
+ yesCallback: () => {
+ this.resetDefaultValues(args);
+ },
+ });
+ }
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: 'bi-copy',
+ labelI18nKey: 'action.resetDefaultValues',
+ label: $t('action.resetDefaultValues'),
+ click: this.click,
+ };
+ }
+
+ resetDefaultValues(args) {
+ const requestParams: any = {
+ method: 'POST',
+ url: this.table.url.dataUrl + '/resetDefaultValues',
+ };
+ axios(requestParams)
+ .then(() => {
+ NotifyManager.info($t('tip.operationSuccess'));
+ args.grid.refresh();
+ })
+ .catch((error) => {
+ console.error('[w-grid]ResetDefaultValues error:', error);
+ });
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Separator.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Separator.ts
new file mode 100644
index 00000000..9262b661
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/Separator.ts
@@ -0,0 +1,15 @@
+import { PropsType, TableType } from '../../index';
+import { Button } from '../Button';
+
+export class Separator extends Button {
+ name = 'separator';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ getButtonConfig() {
+ return 'separator';
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/View.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/View.ts
new file mode 100644
index 00000000..c11a1384
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/toolbar/buttons/View.ts
@@ -0,0 +1,33 @@
+import { $t } from '@/platform';
+import { PropsType, TableType } from '../../index';
+import { Button } from '../Button';
+
+export class View extends Button {
+ name = 'view';
+
+ constructor(props: PropsType, table: TableType) {
+ super(props, table);
+ this.click = this.click.bind(this);
+ this.getButtonConfig = this.getButtonConfig.bind(this);
+ }
+
+ click(args) {
+ this.tools?.apiFM.operator.viewData();
+ }
+
+ getButtonConfig() {
+ return {
+ name: this.name,
+ icon: 'visibility',
+ labelI18nKey: 'action.view',
+ label: $t('action.view'),
+ enableIf: (args) => {
+ if (args.selected) {
+ return true;
+ }
+ return false;
+ },
+ click: this.click,
+ };
+ }
+}
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/CriteriaType.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/CriteriaType.ts
new file mode 100644
index 00000000..97d2fa0d
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/CriteriaType.ts
@@ -0,0 +1,5 @@
+export type CriteriaType = {
+ fieldName: string;
+ operator: string;
+ value: any;
+}
\ No newline at end of file
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/EventType.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/EventType.ts
new file mode 100644
index 00000000..e46cc0d8
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/EventType.ts
@@ -0,0 +1,26 @@
+export type EventType = {
+ // 行点击事件
+ (e: 'rowClick', args: object): void;
+ // 行双击事件
+ (e: 'rowDbClick', args: object): void;
+ // 行数据checkbox勾选状态改变事件
+ (e: 'updateTicked', args: object): void;
+ // 表格列头全部勾选checkbox状态改变事件
+ (e: 'updateTickeds', args: object): void;
+ // 表格请求数据前事件
+ (e: 'beforeRequestData', args: object): void;
+ // 表格请求数据后事件
+ (e: 'afterRequestData', args: object): void;
+ // 内置新增编辑功能数据提交前触发事件
+ (e: 'beforeEditorDataSubmit', args: object): void;
+ // 内置新增编辑功能数据提交完成后触发事件
+ (e: 'afterEditorDataSubmit', args: object): void;
+ // 内置删除功能提交完成前回调函数
+ (e: 'beforeRemove', args: object): void;
+ // 内置删除功能提交完成后回调函数
+ (e: 'afterRemove', args: object): void;
+ // 新增窗口打开之后
+ (e: 'afterEditorOpen', args: object): void;
+ // 行拖拽排序完成后
+ (e: 'afterDragAndDrop', args: object): void;
+}
\ No newline at end of file
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/PropsType.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/PropsType.ts
new file mode 100644
index 00000000..cd1061b0
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/PropsType.ts
@@ -0,0 +1,246 @@
+export type PropsType = {
+ /**
+ * 表格高度
+ */
+ height: number,
+ /**
+ * 表格标题
+ */
+ title: string,
+ /**
+ * 自动加载数据
+ */
+ autoFetchData: boolean,
+ /**
+ * 本地模式,启用该模式后,除了查询数据依旧通过 autoFetchData 与 url 控制外,其他所有与后端交互的请求都失效,同时分页属性也失效
+ */
+ localMode: boolean,
+ /**
+ * 选择模式
+ */
+ selectMode: string,
+ /**
+ * 拖拽数据行排序的模式,默认不可拖拽,字符串`local`为本地排序,只根据配置的字段进行本地排序,不会将数据发送至后端,字符串`server`为远程排序,拖拽完成后会将数据发送至后端。
+ */
+ dndMode?: undefined | string,
+ /**
+ * 拖拽排序时根据哪个字段进行排序,默认为`order`字段
+ */
+ dndOrderBy: string,
+ /**
+ * 是否需要分页,当启用树形表格模式时该属性失效,树形表格不支持分页。
+ */
+ pageable: boolean,
+ /**
+ * 是否显示表格配置按钮
+ */
+ configButton: boolean,
+ /**
+ * 表格数据操作请求的URL前缀
+ */
+ dataUrl: string,
+ /**
+ * 获取数据URL
+ */
+ fetchDataUrl: string,
+ /**
+ * 新增数据url
+ */
+ addDataUrl: string,
+ /**
+ * 修改数据url
+ */
+ editDataUrl: string,
+ /**
+ * 删除数据url
+ */
+ removeDataUrl: string,
+ /**
+ * 表格整体紧凑模式
+ */
+ dense?: undefined | boolean,
+ /**
+ * 表格列标题紧凑模式
+ */
+ denseHeader?: undefined | boolean,
+ /**
+ * 表格数据行紧凑模式
+ */
+ denseBody?: undefined | boolean,
+ /**
+ * 表格底部分页栏紧凑模式
+ */
+ denseBottom?: undefined | boolean,
+ /**
+ * 表格toolbar按钮栏紧凑模式
+ */
+ denseToolbar?: undefined | boolean,
+ /**
+ * 是否显示排序号
+ */
+ sortNo: boolean,
+ /**
+ * 从左侧开始冻结列数,复选框与排序列不计算在内,支持1-10列。
+ */
+ stickyNum: number,
+ /**
+ * checkbox选择模式,默认启用
+ */
+ checkboxSelection: boolean,
+ /**
+ * 行数据中记录checkbox勾选状态的字段名
+ */
+ tickedField: string,
+ /**
+ * 行数据中记录行点击选中状态的字段名
+ */
+ selectedField: string,
+ /**
+ * 树形表格模式,按照层级关系构建数据并以树形展现
+ */
+ tree: boolean,
+ /**
+ * 树型表格模式, 懒加载
+ */
+ treeLazyLoad: boolean,
+ /**
+ * 树形表格模式,图标函数,带有一个参数,行数据
+ */
+ treeIcon?: Function | undefined,
+ /**
+ * 树形表格模式,树形表格数据加载后是否全部展开
+ */
+ treeDefaultExpandAll: boolean,
+ /**
+ * 树形表格模式,树勾选策略,strict:节点自己,leaf:包含子节点
+ */
+ treeTickStrategy: string,
+ /**
+ * 树形表格模式的数据关系,包括:parent, children,当为parent时组件根据数据主键与数据外键构建树形数据,否则需要自己构建好树形数据放到children属性中。
+ */
+ treeRelationship: string,
+ /**
+ * 数据主键(常规表格模式时,该字段可用作内置的编辑删除等功能对应的后端API入参,如:继承RestCrudController的update所需的入参。树形表格模式时,该字段为构建树数据的主键)
+ */
+ primaryKey: string,
+ /**
+ * 数据外键(常规表格模式时,该字段暂时无用,将来可用作多个表格的数据关系字段。树形表格模式时,该字段为构建树数据的关系字段)
+ */
+ foreignKey: string,
+ /**
+ * 新增、删除、修改成功后是否刷新数据列表,默认不刷新但是新增修改后台必须返回对应的行数据对象,删除则必须返回删除的记录集primaryKey集合。
+ */
+ refreshData?: boolean,
+ /**
+ * 默认的双击操作:可填写内置或自定义按钮name,执行的操作为按钮对应的click,固定值提供:expand(展开双击的行)、none(双击不执行任何动作)
+ */
+ dbClickOperation: string,
+ /**
+ * 表格分割线,支持:horizontal、vertical、cell、none
+ */
+ separator: string,
+ /**
+ * 隐藏表头
+ */
+ hideHeader?: boolean,
+ /**
+ * 隐藏底部
+ */
+ hideBottom?: boolean,
+ /**
+ * 分组模式,支持:alone、merge
+ */
+ groupMode?: undefined | string,
+ /**
+ * alone分组模式下默认展开的组,字符串支持:all、first、none,数组可配置多个组名。
+ */
+ groupStartOpen?: string | Array | Function,
+ /**
+ * 分组字段配置,当分组模式为 alone 时若配置的为数组,取数组第一个字段。
+ */
+ groupByField: string | Array,
+ /**
+ * 表格追加行,添加到当前表格数据行尾,可添加多行,支持跨行跨列配置,用于添加合计或者额外信息。
+ */
+ appendRows: Array,
+ /**
+ * 默认排序字段,示例:['userName', '-lastModifyDate'],其中“-”开头表示倒序。
+ */
+ sortBy: Array,
+ /**
+ * 默认勾选的记录,传入一个对象,包含:columnName(列名称,字符串类型,该属性不传默认根据数据主键查找)、data(需要勾选的数据,数组类型)
+ */
+ tickedRecord: any,
+ /**
+ * 高级查询
+ */
+ advancedQuery: boolean,
+ /**
+ * 查询条件,查询、翻页、刷新等操作都会带上的查询条件
+ */
+ queryCriteria: any,
+ /**
+ * 列定义
+ */
+ columns: Array,
+ /**
+ * 查询表单字段
+ */
+ queryFormFields: Array,
+ /**
+ * 查询表单一行显示几个元素
+ */
+ queryFormColsNum: any,
+ /**
+ * 查询表单显示几行
+ */
+ queryFormRowNum: number,
+ /**
+ * 查询表单 form 组件属性
+ */
+ queryFormAttrs: any,
+ /**
+ * toolbar 按钮
+ */
+ toolbarActions: Array,
+ /**
+ * toolbar 属性
+ */
+ toolbarConfigure: any,
+ /**
+ * 分页大小,后台初始页码等配置
+ */
+ pagination: any,
+ /**
+ * 编辑相关窗口、表单、字段配置
+ */
+ editor?: any,
+ /**
+ * 查看抽屉、字段配置
+ */
+ viewer?: any,
+ /**
+ * 作为form中的组件使用时控制表格是否显示
+ */
+ showIf?: boolean | Function,
+ /**
+ * 作为form中的组件使用时配置表单form实例
+ */
+ form?: any,
+ /**
+ * 用户自定义单击函数
+ */
+ onRowClick?: Function,
+ /**
+ * 用户自定义双击函数
+ */
+ onRowDbClick?: Function,
+ /**
+ * 用户自定义数据勾选函数
+ */
+ onUpdateTicked?: Function,
+ /**
+ * 用户自定义拖拽回调函数
+ */
+ onAfterDragAndDrop?: Function,
+}
\ No newline at end of file
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/TableType.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/TableType.ts
new file mode 100644
index 00000000..fd1f1fbc
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/TableType.ts
@@ -0,0 +1,60 @@
+import { ColorType } from '../types/table/ColorType';
+import { UrlType } from '../types/table/UrlType';
+import { ConfigPanelStoreType } from '../types/table/ConfigPanelStoreType';
+import { StoreType } from '../types/table/StoreType';
+import { ComponentRefType } from '../types/table/ComponentRefType';
+
+export type TableType = {
+ /**
+ * 用户传入的原始列
+ */
+ originalColumns: Array;
+ /**
+ * 处理完默认属性并将多表头中配置的列平铺后的列
+ */
+ columns: Array;
+ /**
+ * 当前表格数据
+ */
+ rows: Array;
+ /**
+ * 默认 criteria 查询条件
+ */
+ queryCriteria: any;
+ /**
+ * 当前更多查询是否为激活状态
+ */
+ moreQueryStatus: boolean;
+ /**
+ * 高级查询是否为激活状态
+ */
+ advancedQueryStatus: boolean,
+ /**
+ * 高级查询模型值
+ */
+ advancedQueryModelValue?: object,
+ /**
+ * 查询 Form 当前显示的字段
+ */
+ queryFormDisplayFields: Array;
+ /**
+ * 获取子组件ref对象的函数
+ */
+ componentRef: ComponentRefType;
+ /**
+ * 颜色配置
+ */
+ color: ColorType;
+ /**
+ * URL配置
+ */
+ url: UrlType;
+ /**
+ * 表格右上角用户自定义配置按钮涉及到的变量
+ */
+ configStore: ConfigPanelStoreType;
+ /**
+ * 变量存储对象
+ */
+ store: StoreType;
+}
\ No newline at end of file
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/CellSelectType.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/CellSelectType.ts
new file mode 100644
index 00000000..38cd6fd2
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/CellSelectType.ts
@@ -0,0 +1,26 @@
+
+/**
+ * 单元格选择类型
+ */
+export type CellSelectType = {
+ /**
+ * 行数据
+ */
+ row: any,
+ /**
+ * 前端主键
+ */
+ rowKey: string,
+ /**
+ * 数据主键
+ */
+ primaryKey?: string | number,
+ /**
+ * 列名称
+ */
+ colName: string,
+ /**
+ * 值
+ */
+ value?: any,
+}
\ No newline at end of file
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/ColorType.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/ColorType.ts
new file mode 100644
index 00000000..54de1ee0
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/ColorType.ts
@@ -0,0 +1,18 @@
+export type ColorType = {
+ /**
+ * dark模式下的背景颜色
+ */
+ darkBgColor: string,
+ /**
+ * 列头背景颜色
+ */
+ headBgColor: string,
+ /**
+ * 边框线颜色
+ */
+ borderColor: string,
+ /**
+ * 锁定列时背景颜色
+ */
+ stickyBgColor: string,
+};
\ No newline at end of file
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/ComponentRefType.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/ComponentRefType.ts
new file mode 100644
index 00000000..d72fb748
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/ComponentRefType.ts
@@ -0,0 +1,9 @@
+export type ComponentRefType = {
+ getTableRef: Function,
+ getTopRef: Function,
+ getHeaderRef: Function,
+ getBodyRef: Function,
+ getEditorRef: Function,
+ getCellEditorRef: Function,
+ getViewRef: Function,
+}
\ No newline at end of file
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/ConfigPanelStoreType.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/ConfigPanelStoreType.ts
new file mode 100644
index 00000000..dfc2943e
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/ConfigPanelStoreType.ts
@@ -0,0 +1,57 @@
+/**
+ * 表格右上角用户自定义配置按钮涉及到的变量
+ */
+export type ConfigPanelStoreType = {
+ /**
+ * 当前配置面板是否显示
+ */
+ showConfigPanel: boolean,
+ /**
+ * 表格当前是否为全屏模式
+ */
+ isFullscreen: boolean,
+ /**
+ * 表格分割线
+ */
+ separator: string,
+ /**
+ * 锁定列数
+ */
+ stickyNum: number,
+ /**
+ * 是否使用checkbox选择模式
+ */
+ useCheckboxSelection: boolean,
+ /**
+ * 是否显示前端序号列
+ */
+ showSortNo: boolean,
+ /**
+ * 表格整体紧凑
+ */
+ dense: boolean,
+ /**
+ * 按钮栏紧凑
+ */
+ denseToolbar: boolean,
+ /**
+ * 列头紧凑
+ */
+ denseHeader: boolean,
+ /**
+ * 内容紧凑
+ */
+ denseBody: boolean,
+ /**
+ * 底部分页工具栏紧凑
+ */
+ denseBottom: boolean,
+ /**
+ * 分组模式为单独,行数据分组记录。
+ */
+ aloneGroupRecords: Array,
+ /**
+ * 分组模式为单独时,当前分组字段
+ */
+ aloneGroupByField: undefined | string,
+}
\ No newline at end of file
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/GridLocationType.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/GridLocationType.ts
new file mode 100644
index 00000000..f7f2a003
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/GridLocationType.ts
@@ -0,0 +1,53 @@
+/**
+ * 表格位置信息类型
+ */
+export type GridLocationType = {
+ /**
+ * 表格当前所在Y轴位置
+ */
+ yLocation: number,
+ /**
+ * 无数据提示行所在Y轴位置
+ */
+ noDataTrYLocation: number,
+ /**
+ * 表格当前top区域高度(表格列头上方区域)
+ */
+ topHeight: number,
+ /**
+ * 表格当前bottom区域高度(无可用数据、分页那块区域)
+ */
+ bottomHeight: number,
+ /**
+ * 表格当前middle区域宽度(表格列头及数据行区域)
+ */
+ middleWidth: number,
+ /**
+ * 表格出现滚动条时的宽度,若无滚动条则与 middleWidth 一致
+ */
+ middleScrollWidth: number,
+ /**
+ * 表格当前middle区域高度(表格列头及数据行区域)
+ */
+ middleHeight: number,
+ /**
+ * 表格当前列头高度
+ */
+ columnHeadHeight: number,
+ /**
+ * 当前表格title排除无数据行的总高度
+ */
+ titleTotalHeight: number,
+ /**
+ * 隐藏表头时无数据提示区域高度
+ */
+ hideHeaderNoDataHeight: number,
+ /**
+ * 表格进入全屏前y轴位置
+ */
+ tableInFullscreenY: number,
+ /**
+ * 表格进入全屏前无数据提示行y轴位置
+ */
+ noDataTrInFullscreenY: number,
+}
\ No newline at end of file
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/PaginationConfigType.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/PaginationConfigType.ts
new file mode 100644
index 00000000..bd895930
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/PaginationConfigType.ts
@@ -0,0 +1,26 @@
+
+/**
+ * 分页配置类型
+ */
+export type PaginationConfigType = {
+ /**
+ * 显示边界按钮链接
+ */
+ boundaryLinks: boolean,
+ /**
+ * 始终显示第一页和最后一页按钮(如果不使用“输入”)
+ */
+ boundaryNumbers: boolean,
+ /**
+ * 显示方向按钮
+ */
+ directionLinks: boolean,
+ /**
+ * 页面可用时显示省略号
+ */
+ ellipses: boolean,
+ /**
+ * 一次显示的最大页面链接数;0表示无限
+ */
+ maxPages: number
+}
\ No newline at end of file
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/PaginationType.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/PaginationType.ts
new file mode 100644
index 00000000..a63885a3
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/PaginationType.ts
@@ -0,0 +1,39 @@
+import { PaginationConfigType } from './PaginationConfigType';
+
+/**
+ * 分页信息类型
+ */
+export type PaginationType = {
+ /**
+ * 排序字段
+ */
+ sortBy: any,
+ /**
+ * 是否倒序
+ */
+ descending: boolean,
+ /**
+ * 配置信息
+ */
+ config: PaginationConfigType,
+ /**
+ * 后端起始页码
+ */
+ reqPageStart: number,
+ /**
+ * 当前页码
+ */
+ page: number,
+ /**
+ * 一页显示多少行
+ */
+ rowsPerPage: number,
+ /**
+ * 总条数
+ */
+ rowsNumber: number,
+ /**
+ * 用户可选的每页显示数量
+ */
+ rowsPerPageOptions: Array
+}
\ No newline at end of file
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/StoreType.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/StoreType.ts
new file mode 100644
index 00000000..e45833c7
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/StoreType.ts
@@ -0,0 +1,77 @@
+import { GridLocationType } from './GridLocationType';
+import { PaginationType } from './PaginationType';
+import { CellSelectType } from './CellSelectType';
+
+/**
+ * 变量存储对象
+ */
+export type StoreType = {
+ /**
+ * 当前表格是否全部展开(树模式、单独分组模式通用)
+ */
+ expand: boolean,
+ /**
+ * 记录展开的数据集合(用于树模式、单独分组模式刷新表格时还原已经展开的数据记录)
+ */
+ expandDatas: Array,
+ /**
+ * 已经加载全部数据标记(用于树表格`全部展开`按钮判断是否要加载当前所有数据的子节点)
+ */
+ alreadyLoadAllData: boolean,
+ /**
+ * 懒加载无子节点记录集合(用于树表格懒加载模式刷新或重新加载数据时不再请求该节点的子节点)
+ */
+ lazyloadNoChildrenDatas: Array,
+ /**
+ * 非树表格表头全选checkbox状态
+ */
+ headerTicked: null | boolean,
+ /**
+ * 拖拽排序过程中的记录
+ */
+ dragRecords: Array,
+ /**
+ * 表格内联编辑状态
+ */
+ inlineEditStatus: string,
+ /**
+ * 单元格选择模式时当前选中记录
+ */
+ cellSelected?: CellSelectType,
+ /**
+ * 分组模式为合并,行数据合并记录。
+ */
+ mergeGroupRecords: any,
+ /**
+ * 默认留白高度
+ */
+ spaceHeight: number,
+ /**
+ * 无数据提示i18n的key
+ */
+ noDataLabelI18nKey: string,
+ /**
+ * 表格当前是否处于加载状态
+ */
+ loading: boolean,
+ /**
+ * 加载提示i18n的key
+ */
+ loadingLabelI18nKey: string,
+ /**
+ * 单元格高度
+ */
+ bodyCellHeight: number,
+ /**
+ * 表格大小变化标记,每次触发 resize 后更改
+ */
+ resizeFlag: boolean,
+ /**
+ * 表格位置信息
+ */
+ location: GridLocationType,
+ /**
+ * 表格分页信息
+ */
+ pagination: PaginationType,
+}
\ No newline at end of file
diff --git a/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/UrlType.ts b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/UrlType.ts
new file mode 100644
index 00000000..c4d0d1b1
--- /dev/null
+++ b/io.sc.platform.core.frontend/src/platform/components/grid/ts/types/table/UrlType.ts
@@ -0,0 +1,7 @@
+export type UrlType = {
+ dataUrl: string,
+ fetchDataUrl: string,
+ addDataUrl: string,
+ editDataUrl: string,
+ removeDataUrl: string,
+}
\ No newline at end of file
diff --git a/io.sc.platform.core.frontend/src/platform/components/toolbar/ChildrenBtn.vue b/io.sc.platform.core.frontend/src/platform/components/toolbar/ChildrenBtn.vue
index edea14c9..4d62411f 100644
--- a/io.sc.platform.core.frontend/src/platform/components/toolbar/ChildrenBtn.vue
+++ b/io.sc.platform.core.frontend/src/platform/components/toolbar/ChildrenBtn.vue
@@ -2,7 +2,7 @@
{
- return [];
- },
- },
grid: {
type: Object,
default: () => {
@@ -206,6 +201,13 @@ const props = defineProps({
const containerRef = ref();
const localFlag = ref(false);
+watch(
+ () => modelValue.value,
+ (newVal, oldVal) => {
+ handleButtons();
+ },
+);
+
// 将所有按钮根据 name 提取到 JSON 对象中
const buttonsJson = {};
const extractButton = (btns: any) => {
@@ -220,42 +222,7 @@ const extractButton = (btns: any) => {
}
}
};
-extractButton(props.buttons);
-
-// // 将按钮形成父子关系
-// const handleChildren = (parent, children, arr) => {
-// for (const item of arr) {
-// if (Array.isArray(item)) {
-// const other = item.splice(1);
-// const children_ = [];
-// handleChildren(item[0].name, children_, other);
-// children.push({ parent, children: children_, button: buttonsJson[item[0].name] });
-// } else if (typeof item === 'object') {
-// children.push({ parent, children: null, button: buttonsJson[item.name] });
-// } else if (typeof item === 'string' && item === 'separator') {
-// // 当最后一个元素是分割符时,不再追加分割符,避免两个按钮之间出现重复的无意义分割符
-// if (children.length > 0 && children[children.length - 1].button !== 'separator') {
-// children.push({ parent, children: null, button: 'separator' });
-// }
-// }
-// }
-// };
-// const buttons_ = [];
-// for (const btn of props.buttons) {
-// if (Array.isArray(btn)) {
-// const other = btn.splice(1);
-// const children = [];
-// handleChildren(btn[0].name, children, other);
-// buttons_.push({ parent: null, children: children, button: buttonsJson[btn[0].name] });
-// } else if (typeof btn === 'object') {
-// buttons_.push({ parent: null, children: null, button: buttonsJson[btn.name] });
-// } else if (typeof btn === 'string' && btn === 'separator') {
-// // 当最后一个元素是分割符时,不再追加分割符,避免两个按钮之间出现重复的无意义分割符
-// if (buttons_.length > 0 && buttons_[buttons_.length - 1].button !== 'separator') {
-// buttons_.push({ parent: null, children: null, button: 'separator' });
-// }
-// }
-// }
+extractButton(modelValue.value);
const buttons_ = [];
const handleChildrenSeparator = (arr) => {
@@ -285,8 +252,8 @@ const handleChildrenSeparator = (arr) => {
};
const handleButtons = () => {
buttons_.splice(0, buttons_.length);
- for (let i = 0; i < props.buttons.length; i++) {
- const btn: any = props.buttons[i];
+ for (let i = 0; i < modelValue.value.length; i++) {
+ const btn: any = modelValue.value[i];
if (i === 0 && typeof btn === 'string' && btn === 'separator') {
// 按钮数组第一个元素为分割符时直接跳过
continue;
@@ -362,17 +329,21 @@ const reset = (currentWidth) => {
};
const baseActionsComputed = computed(() => {
- localFlag.value;
+ if (localFlag.value) {
+ return baseActions.value;
+ }
return baseActions.value;
});
const moreActionsComputed = computed(() => {
- localFlag.value;
+ if (localFlag.value) {
+ return moreActions.value;
+ }
return moreActions.value;
});
eventBus.on('onLocaleChanged', () => {
nextTick(() => {
- extractButton(props.buttons);
+ extractButton(modelValue.value);
handleButtons();
baseActions.value = buttons_;
isActionWidthInitializedRef.value = false;
@@ -381,15 +352,6 @@ eventBus.on('onLocaleChanged', () => {
}
});
});
-// onUpdated(() => {
-// console.info('onupdated====', props.buttons);
-// });
-// watch(
-// () => props.buttons.map((item) => item['label']),
-// (newVal, oldVal) => {
-// console.info('watch====', newVal);
-// },
-// );
const loadingComputed = computed(() => {
return function (btn) {
diff --git a/io.sc.platform.core.frontend/src/platform/i18n/messages.json b/io.sc.platform.core.frontend/src/platform/i18n/messages.json
index 64d9f317..86c8a475 100644
--- a/io.sc.platform.core.frontend/src/platform/i18n/messages.json
+++ b/io.sc.platform.core.frontend/src/platform/i18n/messages.json
@@ -124,6 +124,7 @@
"action.query": "Query",
"action.moreQueryConditions": "More Query Conditions",
+ "action.advancedQuery": "Advanced Query",
"action.reset": "Reset",
"action.refresh": "Refresh",
"action.addNew": "Add New",
diff --git a/io.sc.platform.core.frontend/src/platform/i18n/messages_tw_CN.json b/io.sc.platform.core.frontend/src/platform/i18n/messages_tw_CN.json
index b9e5b767..4d09d301 100644
--- a/io.sc.platform.core.frontend/src/platform/i18n/messages_tw_CN.json
+++ b/io.sc.platform.core.frontend/src/platform/i18n/messages_tw_CN.json
@@ -124,6 +124,7 @@
"action.query": "查詢",
"action.moreQueryConditions": "更多查詢條件",
+ "action.advancedQuery": "高级查詢",
"action.reset": "重置",
"action.refresh": "刷新",
"action.addNew": "新增",
diff --git a/io.sc.platform.core.frontend/src/platform/i18n/messages_zh_CN.json b/io.sc.platform.core.frontend/src/platform/i18n/messages_zh_CN.json
index 96a44567..a67e3554 100644
--- a/io.sc.platform.core.frontend/src/platform/i18n/messages_zh_CN.json
+++ b/io.sc.platform.core.frontend/src/platform/i18n/messages_zh_CN.json
@@ -124,6 +124,7 @@
"action.query": "查询",
"action.moreQueryConditions": "更多查询条件",
+ "action.advancedQuery": "高级查询",
"action.reset": "重置",
"action.refresh": "刷新",
"action.addNew": "新增",