Browse Source

1、表格组件内容过长时鼠标悬浮title支持函数配置。

2、表格组件增加本地模式。
3、表格组件cell选择模式下双击编辑失效。
4、表格组件内容区域编辑增加单元格编辑模式,同时增加单元格弹窗编辑。
5、表格组件增加内联单元格编辑、内联行编辑等。
6、表格组件修复多行表头右边框丢失问题。
7、表格组件修复无数据时出现双重下边框问题。
7、表格组件增加行数据自动合并功能。
8、w-number组件调整为默认可输入小数且不控制精度,可设置精度。
9、增加w-integer组件,只可以输入整数类型的数据。
main
likunming 4 months ago
parent
commit
d079449e3d
  1. 9
      io.sc.platform.core.frontend/src/platform/components/dialog/WDialog.vue
  2. 144
      io.sc.platform.core.frontend/src/platform/components/grid/CellEditor.vue
  3. 14
      io.sc.platform.core.frontend/src/platform/components/grid/GridAppendContent.vue
  4. 28
      io.sc.platform.core.frontend/src/platform/components/grid/GridBody.vue
  5. 64
      io.sc.platform.core.frontend/src/platform/components/grid/GridEditToolbar.vue
  6. 94
      io.sc.platform.core.frontend/src/platform/components/grid/GridEditor.vue
  7. 2
      io.sc.platform.core.frontend/src/platform/components/grid/GridHeader.vue
  8. 159
      io.sc.platform.core.frontend/src/platform/components/grid/GridTd.vue
  9. 198
      io.sc.platform.core.frontend/src/platform/components/grid/GridTop.vue
  10. 188
      io.sc.platform.core.frontend/src/platform/components/grid/TreeGridRow.vue
  11. 61
      io.sc.platform.core.frontend/src/platform/components/grid/WGrid.vue
  12. 1
      io.sc.platform.core.frontend/src/platform/components/grid/css/grid.css
  13. 34
      io.sc.platform.core.frontend/src/platform/components/grid/ts/grid.ts
  14. 3
      io.sc.platform.core.frontend/src/platform/components/index.ts
  15. 72
      io.sc.platform.core.frontend/src/platform/components/number/WInteger.vue
  16. 14
      io.sc.platform.core.frontend/src/platform/components/number/WNumber.vue
  17. 9
      io.sc.platform.core.frontend/src/platform/components/toolbar/WToolbar.vue

9
io.sc.platform.core.frontend/src/platform/components/dialog/WDialog.vue

@ -156,10 +156,19 @@ const setTitle = (title) => {
titleRef.value = title;
};
const setWidth = (width) => {
//
};
const setHeight = (height) => {
//
};
defineExpose({
show,
hide,
getContent,
setTitle,
setWidth,
setHeight,
});
</script>

144
io.sc.platform.core.frontend/src/platform/components/grid/CellEditor.vue

@ -0,0 +1,144 @@
<template>
<w-dialog ref="dialogRef" :title="dialog.title" v-bind="props.grid.props.editor?.cellEditor" :buttons="dialog.buttons">
<w-form ref="dialogFormRef" :cols-num="1" :fields="fieldsComputed" class="pt-1.5 px-1.5"></w-form>
</w-dialog>
</template>
<script setup lang="ts">
import { ref, reactive, inject, computed, toRaw } from 'vue';
import { t, Tools, noErrorAxios, NotifyManager } from '@/platform';
const dialogRef = ref();
const dialogFormRef = ref();
const props = defineProps({
grid: {
//
type: Object,
default: () => {
return {};
},
},
url: {
// url
type: Object,
default: () => {
return {};
},
},
getRow: {
type: Function,
default: () => {},
},
componentInfo: {
type: Object,
default: () => {
return {};
},
},
rowKeyName: {
type: String,
default: '',
},
});
const table = inject('table');
const dialog = {
title: t('action.edit'),
buttons: [
{
icon: 'beenhere',
labelI18nKey: 'action.submit',
label: t('action.submit'),
loading: false,
click: async () => {
dialog.buttons[0].loading = true;
const result = await dialogFormRef.value.validate();
if (result) {
const cellSelected = table['cellSelected'];
const data = dialogFormRef.value.getData();
const row = props.getRow(table.rows, cellSelected['row'][props.rowKeyName], false);
if (row) {
row[cellSelected['colName']] = data[cellSelected['colName']];
//
if (!props.grid.props.localMode) {
const requestParams = {
method: 'PUT',
headers: { 'content-type': 'application/json;charset=utf-8;' },
data: toRaw(row),
url: getUrl(row[props.grid.props.primaryKey]),
};
noErrorAxios(requestParams)
.then((resp) => {
dialog.buttons[0].loading = false;
props.grid.emit('afterEditorDataSubmit', { grid: props.grid, data: resp.data });
NotifyManager.info(t('tip.operationSuccess'));
dialogRef.value.hide();
})
.catch((error) => {
const response = error?.response;
const status = response?.status;
const data = response?.data;
if (data?.code === 1001) {
//
if (error.response.data.data) {
dialogFormRef.value.setValidationErrors(error.response.data.data);
}
} else {
//
if (status === 500) {
NotifyManager.error(t(data?.errorMessageI18nKey));
} else {
NotifyManager.error(t(status));
}
}
dialog.buttons[0].loading = false;
});
} else {
dialogRef.value.hide();
}
}
}
dialog.buttons[0].loading = false;
},
},
],
};
const fieldsComputed = computed(() => {
const fields = <any>[];
const cellSelected = table['cellSelected'];
if (Object.keys(cellSelected).length > 0) {
const column = table['columns'].find((item) => item['name'] === cellSelected['colName']);
if (column) {
fields.push({
name: column['name'],
type: column['type'],
label: column['label'],
defaultValue: cellSelected['value'],
...column['attrs'],
});
}
}
return fields;
});
const getUrl = (primaryKey) => {
if (!Tools.isEmpty(props.url.editDataUrl)) {
return props.url.editDataUrl + '/' + primaryKey;
} else {
return props.url.dataUrl + '/' + primaryKey;
}
};
const getDialog = () => {
return dialogRef.value;
};
const getForm = () => {
return dialogFormRef.value;
};
defineExpose({
getDialog,
getForm,
});
</script>

14
io.sc.platform.core.frontend/src/platform/components/grid/GridAppendContent.vue

@ -1,17 +1,15 @@
<template>
<!-- <template v-if="typeof value === 'object' && value.componentType && col.value.bindModelValue">
<component :is="col.value.componentType" v-bind="col.value.attrs" v-model="props.getRow(table.rows, row[props.rowKey], false)[col.name]"></component>
</template>
<template v-else-if="col.value && typeof col.value === 'object' && col.value.componentType">
<component :is="col.value.componentType" v-bind="col.value.attrs"></component>
<template v-if="typeof value === 'object' && value.componentType">
<component :is="value.componentType" v-bind="value.attrs"></component>
</template>
<template v-else>
<span v-dompurify-html="!Tools.isEmpty(col.value) ? col.value : ''"></span>
</template> -->
111
<span v-dompurify-html="!Tools.isEmpty(value) ? value : ''"></span>
</template>
</template>
<script setup lang="ts">
import { Tools } from '@/platform';
const props = defineProps({
value: {
type: [Object, String, Number],

28
io.sc.platform.core.frontend/src/platform/components/grid/GridBody.vue

@ -9,7 +9,7 @@
:row-key="props.rowKeyName"
:grid-row-click="props.rowClick"
:grid-row-db-click="props.rowDbClick"
:after-row-draggable="props.afterRowDraggable"
:after-drag-and-drop="props.afterDragAndDrop"
:get-row="props.getRow"
:url="props.url"
:get-row-component-refs="getRowComponentRefs"
@ -54,7 +54,6 @@
></GridTd>
</template>
</q-tr>
<GridAppendRow v-if="showAppendRowsComputed" :grid="props.grid"></GridAppendRow>
<GridEditToolbar
:grid="props.grid"
:url="props.url"
@ -65,12 +64,13 @@
:set-old-value="setOldValue"
:no-data-tr-colspan="noDataTrColspan"
></GridEditToolbar>
<GridAppendRow v-if="showAppendRowsComputed" :grid="props.grid"></GridAppendRow>
</template>
</template>
<script setup lang="ts">
import { computed, inject, ref, toRaw } from 'vue';
import { Tools, noErrorAxios, NotifyManager, t } from '@/platform';
import { draggableImage, draggableMode, selectedMode } from './ts/grid';
import { dndMode, dndImage, selectedMode, editStatus } from './ts/grid';
import TreeGridRow from './TreeGridRow.vue';
import GridTd from './GridTd.vue';
import GridEditToolbar from './GridEditToolbar.vue';
@ -117,7 +117,7 @@ const props = defineProps({
type: Function,
default: () => {},
},
afterRowDraggable: {
afterDragAndDrop: {
type: Function,
default: () => {},
},
@ -203,10 +203,10 @@ const showAppendRowsComputed = computed(() => {
const draggableComputed = computed(() => {
if (
props.grid.props.draggable &&
typeof props.grid.props.draggable === 'string' &&
!Tools.isEmpty(draggableMode[props.grid.props.draggable]) &&
table.bodyEditStatus === 'none'
props.grid.props.dndMode &&
typeof props.grid.props.dndMode === 'string' &&
!Tools.isEmpty(dndMode[props.grid.props.dndMode]) &&
table.bodyEditStatus === editStatus.none
) {
return true;
}
@ -264,7 +264,7 @@ const updates = (data, callback) => {
//
const onDragStart = (e, scope) => {
const img = new Image();
img.src = draggableImage;
img.src = dndImage;
e.dataTransfer.setDragImage(img, 0, 0);
const currPageIndex = table.rows.findIndex((item) => {
return item[props.rowKeyName] === scope.row[props.rowKeyName];
@ -352,22 +352,26 @@ const onDrop = (e, scope) => {
const updateData = <any>[];
table.rows.forEach((item, index) => {
if (!Tools.isEmpty(item)) {
item[props.grid.props.draggableOrderBy] = currPageStartIndex + index + 1;
item[props.grid.props.dndOrderBy] = currPageStartIndex + index + 1;
updateData.push(toRaw(item));
}
});
if (props.grid.props.draggable === draggableMode.server && updateData.length > 0) {
if (props.grid.props.dndMode === dndMode.server && updateData.length > 0) {
// 访
updates(updateData, () => {});
}
props.afterRowDraggable(updateData);
props.afterDragAndDrop(updateData);
};
const updateTicked = (evt: Event, row: any) => {
if (table.bodyEditStatus === 'none') {
props.getRow(table.rows, row[props.rowKeyName], false)[table.selectedField] = row[table.tickedField];
props.allTickedStatus();
if (!row[table.tickedField]) {
//
table.cellSelected = {};
}
if (props.grid.props.onUpdateTicked) {
props.grid.emit('updateTicked', { grid: props.grid, evt, row });
}

64
io.sc.platform.core.frontend/src/platform/components/grid/GridEditToolbar.vue

@ -24,7 +24,7 @@
color: 'blue-grey',
outline: false,
click: (args) => {
if (table.bodyEditStatus === 'rowEdit') {
if (table.bodyEditStatus === 'rowEdit' || table.bodyEditStatus === 'cellEdit') {
props.setOldValue(args.selected);
} else if (table.bodyEditStatus === 'rowsEdit') {
props.grid.getRows().forEach((item) => {
@ -43,6 +43,7 @@
<script setup lang="ts">
import { computed, inject, ref, toRaw } from 'vue';
import { Tools, NotifyManager, noErrorAxios, t } from '@/platform';
import { editStatus } from './ts/grid';
const props = defineProps({
grid: {
@ -94,32 +95,34 @@ const validate = async (refs) => {
let result = true;
for (let i = 0; i < refs.length; i++) {
const component = refs[i].getComponentRef();
if (!Tools.isEmpty(refs[i].getComponentOneRef)) {
const componentOne = refs[i].getComponentOneRef();
if (!Tools.isEmpty(componentOne) && !Tools.isEmpty(componentOne.validate)) {
const componentOneValidateResult = await componentOne.validate();
if (!componentOneValidateResult) {
result = false;
break;
}
}
}
if (props.grid.props.tree && !Tools.isEmpty(component)) {
const keys = Object.keys(component);
for (let k = 0; k < keys.length; k++) {
if (!Tools.isEmpty(component[keys[k]]?.validate)) {
const treeComponentValidateResult = await component[keys[k]].validate();
if (!treeComponentValidateResult) {
if (component) {
if (!Tools.isEmpty(refs[i].getComponentOneRef)) {
const componentOne = refs[i].getComponentOneRef();
if (!Tools.isEmpty(componentOne) && !Tools.isEmpty(componentOne.validate)) {
const componentOneValidateResult = await componentOne.validate();
if (!componentOneValidateResult) {
result = false;
break;
}
}
}
} else if (!Tools.isEmpty(component.validate)) {
const componentValidateResult = await component.validate();
if (!componentValidateResult) {
result = false;
break;
if (props.grid.props.tree && !Tools.isEmpty(component)) {
const keys = Object.keys(component);
for (let k = 0; k < keys.length; k++) {
if (!Tools.isEmpty(component[keys[k]]?.validate)) {
const treeComponentValidateResult = await component[keys[k]].validate();
if (!treeComponentValidateResult) {
result = false;
break;
}
}
}
} else if (!Tools.isEmpty(component.validate)) {
const componentValidateResult = await component.validate();
if (!componentValidateResult) {
result = false;
break;
}
}
}
}
@ -245,7 +248,7 @@ const rowsSave = (args) => {
localeUpdateFlag = localeUpdate;
},
});
if (localeUpdateFlag && submitFlag) {
if ((localeUpdateFlag && submitFlag) || props.grid.props.localMode) {
// 访
data.forEach((item) => {
props.grid.updateLocalData(item);
@ -270,12 +273,12 @@ const rowsSave = (args) => {
};
const save = async (args) => {
const refs = props.getRowComponentRefs(table.bodyEditStatus === 'rowEdit' ? args.selected[props.rowKeyName] : []);
const refs = props.getRowComponentRefs(table.bodyEditStatus === 'rowEdit' || table.bodyEditStatus === 'cellEdit' ? args.selected[props.rowKeyName] : []);
const result = await validate(refs);
if (!result) {
NotifyManager.error('验证未通过');
} else {
if (table.bodyEditStatus === 'rowEdit') {
if (table.bodyEditStatus === 'rowEdit' || table.bodyEditStatus === 'cellEdit') {
rowSave(args);
} else if (table.bodyEditStatus === 'rowsEdit') {
rowsSave(args);
@ -315,7 +318,16 @@ const updates = (data, callback) => {
};
const showRowEditButtonComputed = computed(() => {
if (table.bodyEditStatus === 'rowEdit' && isSelectedRowComputed.value) {
if (props.grid.props.localMode && table.bodyEditStatus !== editStatus.rows) {
return false;
}
if (table.bodyEditStatus === 'cellEdit') {
const selected = props.grid.getSelectedCell();
if (selected?.colName && selected.colName === table.cellSelected['colName'] && selected.row[props.rowKeyName] === props.row[props.rowKeyName]) {
return true;
}
return false;
} else if (table.bodyEditStatus === 'rowEdit' && isSelectedRowComputed.value) {
return true;
} else if (table.bodyEditStatus === 'rowsEdit' && table.rows.length > 0 && isLastRowComputed.value) {
return true;

94
io.sc.platform.core.frontend/src/platform/components/grid/GridEditor.vue

@ -74,55 +74,69 @@ const save = async () => {
if (submitFlag) {
if (formStatus === 'addTop') {
dialogFormData[props.grid.props.foreignKey] = null;
} else if ((formStatus === 'addChild' || formStatus === 'edit' || formStatus === 'clone') && selected[props.grid.props.foreignKey]) {
} else if (formStatus === 'addChild') {
dialogFormData[props.grid.props.foreignKey] = selected[props.grid.props.primaryKey];
} else if ((formStatus === 'edit' || formStatus === 'clone') && selected[props.grid.props.foreignKey]) {
dialogFormData[props.grid.props.foreignKey] = selected[props.grid.props.foreignKey];
}
if (formStatus === 'edit') {
//
dialogFormData = { ...selected, ...dialogFormData };
}
let requestParams = {
method: getMethod(formStatus),
headers: { 'content-type': 'application/json;charset=utf-8;' },
data: dialogFormData,
url: getUrl(formStatus, selected),
};
dialog.dialogButtons[0].loading = false;
noErrorAxios(requestParams)
.then((resp) => {
dialog.dialogButtons[0].loading = false;
props.grid.emit('afterEditorDataSubmit', { grid: props.grid, data: resp.data });
NotifyManager.info(t('tip.operationSuccess'));
if (closeDialog) {
dialogRef.value.hide();
}
if (props.grid.props.refreshData || !props.grid.props.tree) {
props.grid.refresh();
} else if (resp.data && (formStatus === 'add' || formStatus === 'clone' || formStatus === 'addTop' || formStatus === 'addChild')) {
addData(resp.data);
} else if (resp.data) {
updateData(resp.data);
}
})
.catch((error) => {
const response = error?.response;
const status = response?.status;
const data = response?.data;
if (data?.code === 1001) {
//
if (error.response.data.data) {
dialogFormRef.value.setValidationErrors(error.response.data.data);
if (props.grid.props.localMode) {
if (formStatus === 'add' || formStatus === 'clone' || formStatus === 'addTop' || formStatus === 'addChild') {
addData(dialogFormData);
} else {
updateData(dialogFormData);
}
dialog.dialogButtons[0].loading = false;
if (closeDialog) {
dialogRef.value.hide();
}
} else {
let requestParams = {
method: getMethod(formStatus),
headers: { 'content-type': 'application/json;charset=utf-8;' },
data: dialogFormData,
url: getUrl(formStatus, selected),
};
dialog.dialogButtons[0].loading = false;
noErrorAxios(requestParams)
.then((resp) => {
dialog.dialogButtons[0].loading = false;
props.grid.emit('afterEditorDataSubmit', { grid: props.grid, data: resp.data });
NotifyManager.info(t('tip.operationSuccess'));
if (closeDialog) {
dialogRef.value.hide();
}
if (props.grid.props.refreshData || !props.grid.props.tree) {
props.grid.refresh();
} else if (resp.data && (formStatus === 'add' || formStatus === 'clone' || formStatus === 'addTop' || formStatus === 'addChild')) {
addData(resp.data);
} else if (resp.data) {
updateData(resp.data);
}
} else {
//
if (status === 500) {
NotifyManager.error(t(data?.errorMessageI18nKey));
})
.catch((error) => {
const response = error?.response;
const status = response?.status;
const data = response?.data;
if (data?.code === 1001) {
//
if (error.response.data.data) {
dialogFormRef.value.setValidationErrors(error.response.data.data);
}
} else {
NotifyManager.error(t(status));
//
if (status === 500) {
NotifyManager.error(t(data?.errorMessageI18nKey));
} else {
NotifyManager.error(t(status));
}
}
}
dialog.dialogButtons[0].loading = false;
});
dialog.dialogButtons[0].loading = false;
});
}
} else {
dialog.dialogButtons[0].loading = false;
if (closeDialog) {

2
io.sc.platform.core.frontend/src/platform/components/grid/GridHeader.vue

@ -194,7 +194,7 @@ const allTickedUpdateFun = (value, evt) => {
item[table.selectedField] = false;
});
}
} else if (table.bodyEditStatus === 'rowEdit') {
} else if (table.bodyEditStatus === 'rowEdit' || table.bodyEditStatus === 'cellEdit') {
table.allTicked = null;
} else if (table.bodyEditStatus === 'rowsEdit') {
table.allTicked = false;

159
io.sc.platform.core.frontend/src/platform/components/grid/GridTd.vue

@ -1,56 +1,102 @@
<template>
<q-td
v-if="rowSpanIsFirstComputed"
:key="col.name"
:props="scope"
:title="col.classes?.indexOf('truncate') > -1 && !Tools.isEmpty(value) && typeof value !== 'object' ? value : ''"
:class="props.grid.props.selectedMode === selectedMode.cell ? tdClassComputed : ''"
:rowspan="rowSpanComputed"
@click="
() => {
if (table && props.grid.props.selectedMode === selectedMode.cell) {
table['cellSelected'] = {
row: toRaw(scope.row),
rowKey: scope.row[props.rowKeyName],
primaryKey: scope.row[props.grid.props.primaryKey],
colName: col['name'],
value: value,
};
<template v-if="rowSpanIsFirstComputed">
<q-td
ref="tdRef"
:key="col.name"
:props="scope"
:title="titleComputed"
:class="props.grid.props.selectedMode === selectedMode.cell && (table.bodyEditStatus === 'none' || props.grid.props.localMode) ? tdClassComputed : ''"
:rowspan="rowSpanComputed"
:style="col.type && table.bodyEditStatus !== 'none' ? 'width: ' + (tdWidth - 24) + 'px;' : ''"
@click="
() => {
// 退
if (props.grid.props.localMode && table.bodyEditStatus === editStatus.cell && props.grid.props.selectedMode === selectedMode.cell) {
if (table['cellSelected']['colName'] !== col['name'] || table['cellSelected']['row'][props.rowKeyName] !== props.scope.row[props.rowKeyName]) {
table.bodyEditStatus = 'none';
}
} else if (props.grid.props.localMode && table.bodyEditStatus === editStatus.row && props.grid.props.selectedMode === selectedMode.cell) {
if (table['cellSelected']['row'][props.rowKeyName] !== props.scope.row[props.rowKeyName]) {
table.bodyEditStatus = 'none';
}
}
if (table.bodyEditStatus === 'none' && props.grid.props.selectedMode === selectedMode.cell) {
table['cellSelected'] = {
row: toRaw(scope.row),
rowKey: scope.row[props.rowKeyName],
primaryKey: scope.row[props.grid.props.primaryKey],
colName: col['name'],
value: value,
};
}
}
}
"
>
<template v-if="col.name === '_sortNo_'">
{{ scope.rowIndex + 1 }}
</template>
<template v-if="!Tools.isEmpty(col.type) && ((props.isSelectedRow && table.bodyEditStatus === 'rowEdit') || table.bodyEditStatus === 'rowsEdit')">
<component
:is="col.type"
ref="componentRef"
v-bind="col.attrs"
v-model="props.getRow(table.rows, scope.row[props.rowKeyName], false)['_rowOldValue'][col.name]"
bg-color="light-green-1"
></component>
</template>
<template v-else>
<template v-if="!Tools.isEmpty(value) && typeof value === 'object' && value.componentType && value.bindModelValue">
<component :is="value.componentType" v-bind="value.attrs" v-model="props.getRow(table.rows, scope.row[props.rowKeyName], false)[col.name]"></component>
"
>
<template v-if="col.name === '_sortNo_'">
{{ scope.rowIndex + 1 }}
</template>
<template v-else-if="!Tools.isEmpty(value) && typeof value === 'object' && value.componentType">
<component :is="value.componentType" v-bind="value.attrs"></component>
<template
v-if="
!Tools.isEmpty(col.type) &&
((props.isSelectedRow && table.bodyEditStatus === 'rowEdit') ||
table.bodyEditStatus === 'rowsEdit' ||
(isSelectedCellComputed && table.bodyEditStatus === 'cellEdit'))
"
>
<template v-if="props.grid.props.localMode && table.bodyEditStatus !== editStatus.rows">
<component
:is="col.type"
ref="componentRef"
v-bind="col.attrs"
v-model="props.getRow(table.rows, scope.row[props.rowKeyName], false)[col.name]"
bg-color="light-green-1"
@blur="
() => {
if (props.grid.props.selectedMode === selectedMode.cell && table.bodyEditStatus === editStatus.cell) {
// 退
table.bodyEditStatus = editStatus.none;
table['cellSelected'] = {};
}
}
"
></component>
</template>
<template v-else>
<component
:is="col.type"
ref="componentRef"
v-bind="col.attrs"
v-model="props.getRow(table.rows, scope.row[props.rowKeyName], false)['_rowOldValue'][col.name]"
bg-color="light-green-1"
></component>
</template>
</template>
<template v-else>
<span v-dompurify-html="Tools.isUndefinedOrNull(value) ? '' : value"></span>
<template v-if="!Tools.isEmpty(value) && typeof value === 'object' && value.componentType && value.bindModelValue">
<component
:is="value.componentType"
v-bind="value.attrs"
v-model="props.getRow(table.rows, scope.row[props.rowKeyName], false)[col.name]"
></component>
</template>
<template v-else-if="!Tools.isEmpty(value) && typeof value === 'object' && value.componentType">
<component :is="value.componentType" v-bind="value.attrs"></component>
</template>
<template v-else>
<span v-dompurify-html="Tools.isUndefinedOrNull(value) ? '' : value"></span>
</template>
</template>
</template>
</q-td>
</q-td>
</template>
</template>
<script setup lang="ts">
import { inject, computed, ref, toRaw } from 'vue';
import { inject, computed, ref, toRaw, onMounted } from 'vue';
import { Tools } from '@/platform';
import { selectedMode } from './ts/grid.ts';
import { selectedMode, editStatus } from './ts/grid.ts';
const tdRef = ref();
const componentRef = ref();
const tdWidth = ref(0);
const props = defineProps({
grid: {
type: Object,
@ -91,6 +137,29 @@ const getComponentRef = () => {
return componentRef.value;
};
const isSelectedCellComputed = computed(() => {
const selected = props.grid.getSelectedCell();
if (selected?.colName && props.scope.row[props.rowKeyName] === selected['row'][props.rowKeyName] && props.col['name'] === selected.colName) {
return true;
}
return false;
});
const titleComputed = computed(() => {
if (table['columns'] && table['columns'].length > 0) {
const column = table['columns'].find((item) => item['name'] === props.col.name);
if (column && column['title'] && typeof column['title'] === 'function') {
return column.title({ grid: props.grid, row: toRaw(props.scope.row), value: props.scope.row[props.col.name] });
} else if (column && column['title']) {
return column['title'];
}
}
if (props.col.classes?.indexOf('truncate') > -1 && !Tools.isEmpty(props.value) && typeof props.value !== 'object') {
return props.value;
}
return '';
});
const rowSpanComputed = computed(() => {
if (Object.keys(table.mergeRecords).length > 0) {
const column = table.mergeRecords[props.col['name']];
@ -125,6 +194,12 @@ const tdClassComputed = computed(() => {
return tdClass;
});
onMounted(() => {
if (tdRef.value?.$el) {
tdWidth.value = tdRef.value.$el.clientWidth;
}
});
defineExpose({
getComponentRef,
});

198
io.sc.platform.core.frontend/src/platform/components/grid/GridTop.vue

@ -30,6 +30,7 @@
import { computed, inject, ref, reactive, nextTick, onBeforeMount, toRaw } from 'vue';
import { useQuasar, exportFile } from 'quasar';
import { axios, Tools, t, NotifyManager } from '@/platform';
import { selectedMode, formStatus, editStatus } from './ts/grid';
import GridConfig from './GridConfig.vue';
const $q = useQuasar();
@ -152,13 +153,7 @@ const edit = (selected) => {
if (!selected) {
NotifyManager.warn(t('action.edit.tip'));
} else {
props.grid.getEditorDialog().show();
nextTick(() => {
props.grid.getEditorDialog().setTitle(t('action.edit'));
props.grid.getEditorForm().setStatus('edit');
props.grid.getEditorForm().setData(selected);
props.grid.emit('afterEditorOpen', { grid: props.grid, data: selected });
});
openEditor(t('action.edit'), formStatus.edit, selected);
}
};
@ -167,13 +162,7 @@ const clone = (selected) => {
NotifyManager.warn(t('action.copy.tip'));
} else {
selected[props.grid.props.primaryKey] = undefined;
props.grid.getEditorDialog().show();
nextTick(() => {
props.grid.getEditorDialog().setTitle(t('action.copy'));
props.grid.getEditorForm().setStatus('clone');
props.grid.getEditorForm().setData(selected);
props.grid.emit('afterEditorOpen', { grid: props.grid, data: selected });
});
openEditor(t('action.copy'), formStatus.clone, selected);
}
};
@ -190,11 +179,6 @@ const remove = () => {
ids.push(item[props.grid.props.primaryKey]);
});
}
let requestParams: any = {
method: 'DELETE',
url: props.url.removeDataUrl || props.url.dataUrl,
data: ids,
};
let flag = true;
props.grid.emit('beforeRemove', {
grid: props.grid,
@ -209,20 +193,29 @@ const remove = () => {
},
});
if (flag) {
axios(requestParams)
.then((resp) => {
props.grid.emit('afterRemove', { grid: props.grid, ids: resp?.data });
NotifyManager.info(t('tip.operationSuccess'));
if (props.grid.props.refreshData || !props.grid.props.tree) {
props.grid.refresh();
} else {
props.grid.removeLocalData(resp?.data);
}
})
.catch((error) => {
console.error(error);
console.info('==========error==========', error);
});
if (props.grid.props.localMode) {
props.grid.removeLocalData(ids);
} else {
let requestParams: any = {
method: 'DELETE',
url: props.url.removeDataUrl || props.url.dataUrl,
data: ids,
};
axios(requestParams)
.then((resp) => {
props.grid.emit('afterRemove', { grid: props.grid, ids: resp?.data });
NotifyManager.info(t('tip.operationSuccess'));
if (props.grid.props.refreshData || !props.grid.props.tree) {
props.grid.refresh();
} else {
props.grid.removeLocalData(resp?.data);
}
})
.catch((error) => {
console.error(error);
console.info('==========error==========', error);
});
}
}
};
@ -281,6 +274,17 @@ const hideLoading = () => {
$q.loading.hide();
};
const openEditor = (title, status, data) => {
props.grid.getEditorDialog().show();
nextTick(() => {
props.grid.getEditorDialog().setTitle(title);
props.grid.getEditorForm().setStatus(status);
if (!Tools.isEmpty(data)) {
props.grid.getEditorForm().setData(data);
}
});
};
const buttonObj = reactive({
separator: 'separator',
query: {
@ -333,12 +337,7 @@ const buttonObj = reactive({
labelI18nKey: 'action.addNew',
label: t('action.addNew'),
click: () => {
props.grid.getEditorDialog().show();
nextTick(() => {
props.grid.getEditorDialog().setTitle(t('action.addNew'));
props.grid.getEditorForm().setStatus('add');
props.grid.emit('afterEditorOpen', { grid: props.grid, data: undefined });
});
openEditor(t('action.addNew'), formStatus.add, undefined);
},
},
edit: {
@ -356,11 +355,62 @@ const buttonObj = reactive({
edit(args.selected);
},
},
rowEdit: {
name: 'rowEdit',
inlineCellEdit: {
name: 'inlineCellEdit',
icon: 'border_color',
labelI18nKey: 'action.edit',
label: t('action.edit'),
enableIf: (args) => {
if (Object.keys(table.cellSelected).length > 0 && table.cellSelected['colName']) {
return true;
}
return false;
},
click: () => {
if (props.grid.props.selectedMode !== selectedMode.cell) {
NotifyManager.info('非单元格选择模式,无法使用单元格编辑功能');
return false;
} else if (Object.keys(table.cellSelected).length === 0 || !table.cellSelected['colName']) {
NotifyManager.info('请选择要编辑的单元格');
return false;
} else if (table.columns.findIndex((item) => item['name'] === table.cellSelected['colName'] && item['type']) < 0) {
NotifyManager.info('选择的列未配置编辑时使用的组件类型');
return false;
}
table.bodyEditStatus = editStatus.cell;
},
},
cellEdit: {
name: 'cellEdit',
icon: 'border_color',
labelI18nKey: 'action.edit',
label: t('action.edit'),
enableIf: (args) => {
if (Object.keys(table.cellSelected).length > 0 && table.cellSelected['colName']) {
return true;
}
return false;
},
click: () => {
if (props.grid.props.selectedMode !== selectedMode.cell) {
NotifyManager.info('非单元格选择模式,无法使用单元格编辑功能');
return false;
} else if (Object.keys(table.cellSelected).length === 0 || !table.cellSelected['colName']) {
NotifyManager.info('请选择要编辑的单元格');
return false;
} else if (table.columns.findIndex((item) => item['name'] === table.cellSelected['colName'] && item['type']) < 0) {
NotifyManager.info('选择的列未配置编辑时使用的组件类型');
return false;
}
//
props.grid.getCellEditorDialog().show();
},
},
inlineRowEdit: {
name: 'inlineRowEdit',
icon: 'edit_note',
labelI18nKey: 'action.edit',
label: t('action.edit'),
enableIf: (args) => {
if (args.selected) {
return true;
@ -368,19 +418,27 @@ const buttonObj = reactive({
return false;
},
click: () => {
table.bodyEditStatus = 'rowEdit';
if (table.columns.findIndex((item) => item['type']) < 0) {
NotifyManager.info('未配置编辑时使用的组件类型');
return false;
}
table.bodyEditStatus = editStatus.row;
},
},
rowsEdit: {
name: 'rowsEdit',
inlineRowsEdit: {
name: 'inlineRowsEdit',
icon: 'app_registration',
labelI18nKey: 'action.edit',
label: t('action.edit'),
click: () => {
table.bodyEditStatus = 'rowsEdit';
if (table.columns.findIndex((item) => item['type']) < 0) {
NotifyManager.info('未配置编辑时使用的组件类型');
return false;
}
table.bodyEditStatus = editStatus.rows;
//
props.grid.cleanSelected();
props.grid.cleanTicked();
props.grid.clearSelected();
props.grid.clearTicked();
table.allTicked = false;
},
},
@ -486,12 +544,7 @@ const buttonObj = reactive({
labelI18nKey: 'action.addTop',
label: t('action.addTop'),
click: () => {
props.grid.getEditorDialog().show();
nextTick(() => {
props.grid.getEditorDialog().setTitle(t('action.addTop'));
props.grid.getEditorForm().setStatus('addTop');
props.grid.emit('afterEditorOpen', { grid: props.grid, data: undefined });
});
openEditor(t('action.addTop'), formStatus.addTop, undefined);
},
},
addChild: {
@ -506,12 +559,7 @@ const buttonObj = reactive({
return false;
},
click: () => {
props.grid.getEditorDialog().show();
nextTick(() => {
props.grid.getEditorDialog().setTitle(t('action.addChild'));
props.grid.getEditorForm().setStatus('addChild');
props.grid.emit('afterEditorOpen', { grid: props.grid, data: undefined });
});
openEditor(t('action.addChild'), formStatus.addChild, undefined);
},
},
expand: {
@ -648,9 +696,32 @@ const dbClickOperation = (row) => {
if (!Tools.isEmpty(row) && props.grid.props.dbClickOperation === buttonObj.expand.name) {
row['expand'] = Tools.isEmpty(row['expand']) ? true : !row['expand'];
} else {
buttonClick(buttons_.value);
if (
props.grid.props.dbClickOperation !== buttonObj.separator &&
buttonObj[props.grid.props.dbClickOperation] &&
checkConfigNotContains(buttons_.value) &&
buttonObj[props.grid.props.dbClickOperation]
) {
// dbClickOperation
toolbarRef.value.buttonClick(buttonObj[props.grid.props.dbClickOperation]);
} else {
buttonClick(buttons_.value);
}
}
};
//
const checkConfigNotContains = (arr) => {
let flag = false;
for (let i = 0; i < arr.length; i++) {
if (typeof arr[i] === 'object' && props.grid.props.dbClickOperation === arr[i]['name']) {
flag = true;
break;
} else if (Array.isArray(arr[i]) && arr[i].length > 0) {
flag = checkConfigNotContains(arr[i]);
}
}
return !flag;
};
const getQueryForm = () => {
return formRef.value;
};
@ -666,12 +737,13 @@ defineExpose({
resetLabel,
dbClickOperation,
getQueryForm,
reset: buttonObj.reset.click,
add: buttonObj.add.click,
edit,
rowEdit: buttonObj.rowEdit.click,
rowsEdit: buttonObj.rowsEdit.click,
cellEdit: buttonObj.cellEdit.click,
inlineCellEdit: buttonObj.inlineCellEdit.click,
inlineRowEdit: buttonObj.inlineRowEdit.click,
inlineRowsEdit: buttonObj.inlineRowsEdit.click,
clone: buttonObj.clone.click,
remove: buttonObj.remove.click,
view: buttonObj.view.click,

188
io.sc.platform.core.frontend/src/platform/components/grid/TreeGridRow.vue

@ -12,10 +12,22 @@
@dragstart="draggableComputed ? onDragStart($event, row) : () => {}"
>
<q-td
:ref="(el) => setTdRef(el, cols[0])"
:class="props.grid.props.selectedMode === selectedMode.cell ? firstTdClassComputed : 'nowrap text-nowrap'"
:style="cols[0].type && table.bodyEditStatus !== 'none' ? 'width: ' + (tdWidth[cols[0].name] - 24) + 'px;' : ''"
@click="
() => {
if (table && props.grid.props.selectedMode === selectedMode.cell) {
// 退
if (props.grid.props.localMode && table.bodyEditStatus === editStatus.cell && props.grid.props.selectedMode === selectedMode.cell) {
if (table['cellSelected']['colName'] !== cols[0]['name'] || table['cellSelected']['row'][props.rowKey] !== props.row[props.rowKey]) {
table.bodyEditStatus = 'none';
}
} else if (props.grid.props.localMode && table.bodyEditStatus === editStatus.row && props.grid.props.selectedMode === selectedMode.cell) {
if (table['cellSelected']['row'][props.rowKey] !== props.row[props.rowKey]) {
table.bodyEditStatus = 'none';
}
}
if (table.bodyEditStatus === 'none' && props.grid.props.selectedMode === selectedMode.cell) {
table['cellSelected'] = {
row: toRaw(props.row),
rowKey: props.row[props.rowKey],
@ -57,15 +69,40 @@
<span v-else class="px-1"></span>
<template v-if="cols[0]">
<template
v-if="!Tools.isEmpty(cols[0]['type']) && ((isSelectedRowComputed && table.bodyEditStatus === 'rowEdit') || table.bodyEditStatus === 'rowsEdit')"
v-if="
!Tools.isEmpty(cols[0]['type']) &&
((isSelectedRowComputed && table.bodyEditStatus === 'rowEdit') ||
table.bodyEditStatus === 'rowsEdit' ||
(isSelectedCell(cols[0]) && table.bodyEditStatus === 'cellEdit'))
"
>
<component
:is="cols[0]['type']"
ref="componentOneRef"
v-bind="cols[0]['attrs']"
v-model="props.getRow(table.rows, props.row[props.rowKey], false)['_rowOldValue'][cols[0]['name']]"
bg-color="light-green-1"
></component>
<template v-if="props.grid.props.localMode && table.bodyEditStatus !== editStatus.rows">
<component
:is="cols[0]['type']"
:ref="(el) => setComponentRef(el, props.row, cols[0])"
v-bind="cols[0]['attrs']"
v-model="props.getRow(table.rows, props.row[props.rowKey], false)[cols[0]['name']]"
bg-color="light-green-1"
@blur="
() => {
if (props.grid.props.selectedMode === selectedMode.cell && table.bodyEditStatus === editStatus.cell) {
// 退
table.bodyEditStatus = editStatus.none;
table['cellSelected'] = {};
}
}
"
></component>
</template>
<template v-else>
<component
:is="cols[0]['type']"
:ref="(el) => setComponentRef(el, props.row, cols[0])"
v-bind="cols[0]['attrs']"
v-model="props.getRow(table.rows, props.row[props.rowKey], false)['_rowOldValue'][cols[0]['name']]"
bg-color="light-green-1"
></component>
</template>
</template>
<template v-else>
<template v-if="cols[0].value && typeof cols[0].value === 'object' && cols[0].value.componentType">
@ -81,12 +118,23 @@
<template v-for="(col, index) in cols" :key="col.name">
<q-td
v-if="index > 0"
:ref="(el) => setTdRef(el, col)"
:class="props.grid.props.selectedMode === selectedMode.cell ? tdClassComputed(col) : col.__thClass + ' ' + col.classes"
:title="col.classes?.indexOf('truncate') > -1 && col.value && typeof col.value !== 'object' ? col.value : ''"
:style="col.style"
:title="getTitle(col)"
:style="col.style + (col.type && table.bodyEditStatus !== 'none' ? ';width: ' + (tdWidth[col.name] - 24) + 'px;' : '')"
@click="
() => {
if (table && props.grid.props.selectedMode === selectedMode.cell) {
// 退
if (props.grid.props.localMode && table.bodyEditStatus === editStatus.cell && props.grid.props.selectedMode === selectedMode.cell) {
if (table['cellSelected']['colName'] !== col['name'] || table['cellSelected']['row'][props.rowKey] !== props.row[props.rowKey]) {
table.bodyEditStatus = 'none';
}
} else if (props.grid.props.localMode && table.bodyEditStatus === editStatus.row && props.grid.props.selectedMode === selectedMode.cell) {
if (table['cellSelected']['row'][props.rowKey] !== props.row[props.rowKey]) {
table.bodyEditStatus = 'none';
}
}
if (table.bodyEditStatus === 'none' && props.grid.props.selectedMode === selectedMode.cell) {
table['cellSelected'] = {
row: toRaw(props.row),
rowKey: props.row[props.rowKey],
@ -98,14 +146,41 @@
}
"
>
<template v-if="!Tools.isEmpty(col['type']) && ((isSelectedRowComputed && table.bodyEditStatus === 'rowEdit') || table.bodyEditStatus === 'rowsEdit')">
<component
:is="col['type']"
:ref="(el) => setComponentRef(el, props.row, col)"
v-bind="col['attrs']"
v-model="props.getRow(table.rows, props.row[props.rowKey], false)['_rowOldValue'][col['name']]"
bg-color="light-green-1"
></component>
<template
v-if="
!Tools.isEmpty(col.type) &&
((isSelectedRowComputed && table.bodyEditStatus === 'rowEdit') ||
table.bodyEditStatus === 'rowsEdit' ||
(isSelectedCell(col) && table.bodyEditStatus === 'cellEdit'))
"
>
<template v-if="props.grid.props.localMode && table.bodyEditStatus !== editStatus.rows">
<component
:is="col.type"
:ref="(el) => setComponentRef(el, props.row, col)"
v-bind="col.attrs"
v-model="props.getRow(table.rows, props.row[props.rowKey], false)[col.name]"
bg-color="light-green-1"
@blur="
() => {
if (props.grid.props.selectedMode === selectedMode.cell && table.bodyEditStatus === editStatus.cell) {
// 退
table.bodyEditStatus = editStatus.none;
table['cellSelected'] = {};
}
}
"
></component>
</template>
<template v-else>
<component
:is="col.type"
:ref="(el) => setComponentRef(el, props.row, col)"
v-bind="col.attrs"
v-model="props.getRow(table.rows, props.row[props.rowKey], false)['_rowOldValue'][col.name]"
bg-color="light-green-1"
></component>
</template>
</template>
<template v-else>
<template v-if="col.value && typeof col.value === 'object' && col.value.componentType && col.value.bindModelValue">
@ -146,7 +221,7 @@
:grid="props.grid"
:grid-row-click="props.gridRowClick"
:grid-row-db-click="gridRowDbClick"
:after-row-draggable="props.afterRowDraggable"
:after-drag-and-drop="props.afterDragAndDrop"
:get-row="props.getRow"
:url="props.url"
:get-row-component-refs="props.getRowComponentRefs"
@ -158,15 +233,17 @@
</template>
</template>
<script setup lang="ts">
import { ref, computed, inject, toRaw } from 'vue';
import { ref, computed, inject, toRaw, reactive, onMounted } from 'vue';
import { Tools, NotifyManager } from '@/platform';
import GridEditToolbar from './GridEditToolbar.vue';
import { draggableImage, draggableMode, selectedMode } from './ts/grid';
import { dndImage, dndMode, selectedMode, editStatus } from './ts/grid';
const trRef = ref();
const tdDivRef = ref();
const componentOneRef = ref();
const componentRef = ref({});
const tdRef = ref({});
const tdWidth = ref({});
const props = defineProps({
level: { type: Number, default: 0 },
columnsMap: {
@ -207,7 +284,7 @@ const props = defineProps({
type: Function,
default: () => {},
},
afterRowDraggable: {
afterDragAndDrop: {
type: Function,
default: () => {},
},
@ -265,6 +342,21 @@ const style = {
},
};
const getTitle = (col) => {
if (table['columns'] && table['columns'].length > 0) {
const column = table['columns'].find((item) => item['name'] === col.name);
if (column && column['title'] && typeof column['title'] === 'function') {
return column.title({ grid: props.grid, row: toRaw(props.row), value: props.row[col.name] });
} else if (column && column['title']) {
return column['title'];
}
}
if (col.classes?.indexOf('truncate') > -1 && !Tools.isEmpty(props.row[col.name]) && typeof props.row[col.name] !== 'object') {
return props.row[col.name];
}
return '';
};
const firstTdClassComputed = computed(() => {
const tdClass = <any>['nowrap', 'text-nowrap'];
if (props.grid.props.selectedMode === selectedMode.cell) {
@ -280,7 +372,7 @@ const firstTdClassComputed = computed(() => {
const tdClassComputed = computed(() => {
return (col) => {
let tdClass = col.__thClass + ' ' + col.classes;
if (props.grid.props.cellSelected) {
if (props.grid.props.selectedMode === selectedMode.cell) {
tdClass += ' cellSelected_hover';
}
if (table && table['cellSelected'] && Tools.hasOwnProperty(table['cellSelected'], 'colName')) {
@ -294,10 +386,10 @@ const tdClassComputed = computed(() => {
const draggableComputed = computed(() => {
if (
props.grid.props.draggable &&
typeof props.grid.props.draggable === 'string' &&
!Tools.isEmpty(draggableMode[props.grid.props.draggable]) &&
table.bodyEditStatus === 'none'
props.grid.props.dndMode &&
typeof props.grid.props.dndMode === 'string' &&
!Tools.isEmpty(dndMode[props.grid.props.dndMode]) &&
table.bodyEditStatus === editStatus.none
) {
return true;
}
@ -311,6 +403,13 @@ const isSelectedRowComputed = computed(() => {
}
return false;
});
const isSelectedCell = (col) => {
const selected = props.grid.getSelectedCell();
if (selected?.colName && props.row[props.rowKey] === selected['row'][props.rowKey] && col['name'] === selected.colName) {
return true;
}
return false;
};
const iconComputed = computed(() => {
if (!Tools.isEmpty(props.row['_dragIcon_'])) {
@ -453,7 +552,7 @@ document.addEventListener('drop', function (e) {
//
const onDragStart = (e, dataRow) => {
const img = new Image();
img.src = draggableImage;
img.src = dndImage;
e.dataTransfer.setDragImage(img, 0, 0);
const selecteds = props.grid.getSelectedRows();
if (
@ -656,7 +755,7 @@ const nodeSplice = (e, dragIndex, targetIndex, drag, target, arr) => {
//
const setOrder = (arr) => {
arr.forEach((item, index) => {
item[props.grid.props.draggableOrderBy] = index + 1;
item[props.grid.props.dndOrderBy] = index + 1;
//
updateOrderData.push({ ...toRaw(item), children: null });
});
@ -731,15 +830,25 @@ const onDrop = (e, dataRow) => {
resetOrder(e, table.dragRecords, table.rows, dataRow);
//
if (props.grid.props.draggable === draggableMode.server && updateOrderData?.length > 0) {
if (props.grid.props.dndMode === dndMode.server && updateOrderData?.length > 0) {
props.updates(updateOrderData);
}
props.afterRowDraggable(updateOrderData);
props.afterDragAndDrop(updateOrderData);
};
const click = (evt, row, rowIndex) => {
if (table.bodyEditStatus === 'none' && props.grid.props.selectedMode !== selectedMode.none) {
const selected = props.grid.getSelectedRow();
if (
props.grid.props.localMode &&
table.bodyEditStatus !== editStatus.none &&
props.grid.props.selectedMode === selectedMode.row &&
selected &&
row[props.rowKey] !== selected[props.rowKey]
) {
// 退
table.bodyEditStatus = editStatus.none;
} else if (table.bodyEditStatus === editStatus.none && props.grid.props.selectedMode !== selectedMode.none) {
if (!evt.ctrlKey) {
props.grid.clearSelected();
}
@ -763,6 +872,11 @@ const setComponentRef = (el, row, col) => {
componentRef.value[row[props.rowKey] + '_' + col.name] = el;
}
};
const setTdRef = (el, col) => {
if (el && !tdRef.value[col['name']]) {
tdRef.value[col['name']] = el;
}
};
const getComponentOneRef = () => {
return componentOneRef.value;
@ -771,6 +885,12 @@ const getComponentRef = () => {
return componentRef.value;
};
onMounted(() => {
Object.keys(tdRef.value).forEach((item) => {
tdWidth.value[item] = tdRef.value[item].$el.clientWidth;
});
});
defineExpose({
getComponentRef,
getComponentOneRef,

61
io.sc.platform.core.frontend/src/platform/components/grid/WGrid.vue

@ -14,13 +14,13 @@
separator="cell"
:rows="table.rows"
:columns="columnsComputed"
:rows-per-page-options="pageable && !tree && state.refHeightWidth.middleWidth > 600 ? state.pagination.rowsPerPageOptions : []"
:rows-per-page-options="pageable && !props.localMode && !tree && state.refHeightWidth.middleWidth > 600 ? state.pagination.rowsPerPageOptions : []"
:loading="state.loading"
:class="tableClassComputed"
:table-style="tableHeightComputed"
:row-key="rowKey_"
:visible-columns="visibleColumnsComputed"
:hide-bottom="pageable && !tree ? false : true"
:hide-bottom="pageable && !props.localMode && !tree ? false : true"
@request="onRequest"
@fullscreen="tableFullscreenFun"
>
@ -67,7 +67,7 @@
:row-key-name="rowKey_"
:row-click="rowClick"
:row-db-click="rowDbClick"
:after-row-draggable="afterRowDraggable"
:after-drag-and-drop="afterDragAndDrop"
:get-row="getRow"
:set-old-value="setOldValue"
:no-data-tr-colspan="noDataTrColspanComputed"
@ -106,6 +106,9 @@
:get-row="getRow"
></GridEditor>
<!-- 单元格编辑窗口 -->
<CellEditor ref="cellEditorRef" :grid="instance" :url="url" :get-row="getRow" :row-key-name="rowKey_"></CellEditor>
<!-- 查看抽屉 -->
<GridView ref="viewRef" :grid="instance" :table-columns-map="tableColumnsMap"></GridView>
</div>
@ -122,8 +125,9 @@ import GridHeader from './GridHeader.vue';
import GridBody from './GridBody.vue';
import GridPagination from './GridPagination.vue';
import GridEditor from './GridEditor.vue';
import CellEditor from './CellEditor.vue';
import GridView from './GridView.vue';
import { selectedMode as selectedMode_, getMergeColumns, sortByProperties } from './ts/grid.ts';
import { selectedMode as selectedMode_, getMergeColumns, sortByProperties, editStatus } from './ts/grid.ts';
import { columnDefaultProps } from './ts/grid.ts';
@ -141,16 +145,17 @@ const topRef = ref();
const headerRef = ref();
const bodyRef = ref();
const editorRef = ref();
const cellEditorRef = ref();
const viewRef = ref();
const props = defineProps({
height: { type: Number, default: 0 }, //
title: { type: String, default: '' }, //
autoFetchData: { type: Boolean, default: true }, //
// localMode: { type: Boolean, default: false },// autoFetchData
localMode: { type: Boolean, default: false }, // autoFetchData url
selectedMode: { type: String, default: selectedMode_.row }, //
draggable: { type: String, default: undefined }, // `local``server`
draggableOrderBy: { type: String, default: 'order' }, //
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
@ -292,7 +297,7 @@ const props = defineProps({
type: Function,
default: undefined,
},
onAfterRowDraggable: {
onAfterDragAndDrop: {
type: Function,
default: undefined,
},
@ -320,8 +325,8 @@ const emit = defineEmits<{
(e: 'afterRemove', args: object): void;
//
(e: 'afterEditorOpen', args: object): void;
//
(e: 'afterRowDraggable', args: object): void;
//
(e: 'afterDragAndDrop', args: object): void;
}>();
const rawColumns = ref(props.columns);
@ -388,7 +393,7 @@ const table = reactive({
denseBottom: props.denseBottom !== undefined ? props.denseBottom : false,
rows: <any>[],
inFullscreen: false, //
bodyEditStatus: 'none', // none()rowEdit()rowsEdit()
bodyEditStatus: 'none', // none()cellEdit()rowEdit()rowsEdit()
cellSelected: {}, //
mergeRecords: {}, //
});
@ -568,7 +573,17 @@ const tableFullscreenFun = (value) => {
};
const rowClick = (evt: any, row: any, index: any) => {
if (table.bodyEditStatus === 'none' && props.selectedMode !== selectedMode_.none) {
const selected = getSelectedRow();
if (
props.localMode &&
table.bodyEditStatus !== editStatus.none &&
props.selectedMode === selectedMode_.row &&
selected &&
row[rowKey_] !== selected[rowKey_]
) {
// 退
table.bodyEditStatus = editStatus.none;
} else if (table.bodyEditStatus === editStatus.none && props.selectedMode !== selectedMode_.none) {
// checkboxcheckboxcheckbox
if (!evt.ctrlKey) {
clearSelected();
@ -692,11 +707,11 @@ const buildQueryCriterias = (reqParams) => {
const requestHandler = async (ops) => {
const reqParams: any = {};
if (props.pageable && !props.tree) {
if (props.pageable && !props.tree && !props.localMode) {
reqParams.page = ops.pagination.page;
reqParams.size = ops.pagination.rowsPerPage;
}
reqParams.pageable = props.tree ? false : props.pageable;
reqParams.pageable = props.tree || props.localMode ? false : props.pageable;
if (ops.pagination.sortBy && ops.pagination.sortBy !== '') {
if (ops.pagination.descending) {
reqParams.sortBy = '-' + ops.pagination.sortBy;
@ -886,6 +901,7 @@ const setRowDataExtraProperty = (rows: []) => {
//
table.mergeRecords = {};
sortByProperties(rows, mergeColumns);
console.info('rows=========', rows);
}
if (rows && rows.length > 0) {
rows.forEach((item: any, index) => {
@ -1022,9 +1038,9 @@ const tableClassComputed = computed(() => {
return classArr;
});
const afterRowDraggable = (updateData) => {
if (!Tools.isUndefined(props.onAfterRowDraggable)) {
emit('afterRowDraggable', { grid: instance, data: updateData });
const afterDragAndDrop = (updateData) => {
if (!Tools.isUndefined(props.onAfterDragAndDrop)) {
emit('afterDragAndDrop', { grid: instance, data: updateData });
}
};
@ -1033,7 +1049,7 @@ onMounted(() => {
headerRef.value?.handlerMoreRowColumnTitle();
}
topRef.value?.handlerQueryFormShowField();
if (props.autoFetchData) {
if (props.autoFetchData && (!Tools.isEmpty(props.dataUrl) || !Tools.isEmpty(props.fetchDataUrl))) {
onRequest({
pagination: state.pagination,
});
@ -1221,9 +1237,15 @@ const getQueryForm = () => {
const getEditorDialog = () => {
return editorRef.value?.getDialog();
};
const getCellEditorDialog = () => {
return cellEditorRef.value?.getDialog();
};
const getEditorForm = () => {
return editorRef.value?.getForm();
};
const getCellEditorForm = () => {
return cellEditorRef.value?.getForm();
};
const getViewerDrawer = () => {
return viewRef.value?.getViewerDrawer();
};
@ -1475,6 +1497,7 @@ const removeLocalData = (target) => {
});
state.pagination.rowsNumber = table.rows.length;
}
allTickedStatus();
refreshStyle();
}
};
@ -1732,7 +1755,9 @@ defineExpose({
// =========refAPI=========
getQueryForm,
getEditorDialog,
getCellEditorDialog,
getEditorForm,
getCellEditorForm,
getViewerDrawer,
getViewerPanel,

1
io.sc.platform.core.frontend/src/platform/components/grid/css/grid.css

@ -6,6 +6,7 @@
}
.w-grid .q-table__top {
padding: var(--tableTopPadding) var(--tableTopPadding);
border-bottom: 0px !important;
}
.w-grid .q-table__middle .q-table th {
padding: var(--tableHeaderPadding) 8px;

34
io.sc.platform.core.frontend/src/platform/components/grid/ts/grid.ts

@ -1,11 +1,11 @@
import { Tools, t } from '@/platform';
// 拖拽排序模式
export const draggableMode = {
export const dndMode = {
local: 'local', // 本地排序
server: 'server', // 服务端排序
};
export const draggableImage = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' %3E%3Cpath /%3E%3C/svg%3E`;
export const dndImage = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' %3E%3Cpath /%3E%3C/svg%3E`;
// 选择模式
export const selectedMode = {
@ -14,6 +14,23 @@ export const selectedMode = {
cell: 'cell', // 单元格选择
};
// 表格内容区域快速编辑状态
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 = '';
@ -82,15 +99,14 @@ export const getMergeColumns = (columns) => {
};
export const sortByProperties = (arr, properties) => {
return arr.sort((a, b) => {
for (const prop of properties) {
for (const prop of properties) {
arr.sort((a, b) => {
if (a[prop] < b[prop]) {
return -1;
}
if (a[prop] > b[prop]) {
} else if (a[prop] > b[prop]) {
return 1;
}
}
return 0;
});
return 0;
});
}
};

3
io.sc.platform.core.frontend/src/platform/components/index.ts

@ -15,6 +15,7 @@ import WIcon from './icon/WIcon.vue';
import WPosition from './position/WPosition.vue';
import WText from './text/WText.vue';
import WNumber from './number/WNumber.vue';
import WInteger from './number/WInteger.vue';
import WTextarea from './textarea/WTextarea.vue';
import WSelect from './select/WSelect.vue';
import WInputSelect from './select/WInputSelect.vue';
@ -79,6 +80,7 @@ export default {
app.component('WPosition', WPosition);
app.component('WText', WText);
app.component('WNumber', WNumber);
app.component('WInteger', WInteger);
app.component('WTextarea', WTextarea);
app.component('WSelect', WSelect);
app.component('WInputSelect', WInputSelect);
@ -140,6 +142,7 @@ export {
WPosition,
WText,
WNumber,
WInteger,
WTextarea,
WSelect,
WGridSelect,

72
io.sc.platform.core.frontend/src/platform/components/number/WInteger.vue

@ -0,0 +1,72 @@
<template>
<div v-show="fieldMethodsClass.getShow(props, modelValue)">
<q-input
ref="numberRef"
v-model="modelValue"
:hide-bottom-space="true"
:hide-hint="true"
:outlined="true"
:dense="true"
v-bind="attrs"
type="number"
title=""
:rules="fieldMethodsClass.getRules(props, modelValue, numberRef, [FormValidators.maxPrecision(0)])"
:readonly="fieldMethodsClass.getReadOnly(props, modelValue)"
:disable="fieldMethodsClass.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
>
<template #label><w-label :required="fieldMethodsClass.getRequired(props, modelValue)" :label="attrs.label"></w-label></template>
</q-input>
</div>
</template>
<script setup lang="ts">
import { ref, useAttrs } from 'vue';
import { FormValidators } from '@/platform/components';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
const numberRef = ref();
const attrs = useAttrs();
const modelValue = defineModel<number>();
interface FieldProps extends FormFieldProps {}
const props = withDefaults(defineProps<FieldProps>(), { showIf: true }); // 使withDefaults
class FieldMethods extends FormFieldMethods {
// FieldMethods
updateValue = (value_) => {
const floatValue = parseInt(value_);
if (!isNaN(floatValue)) {
modelValue.value = floatValue;
if (props['onUpdateValue']) {
props['onUpdateValue']({
value: floatValue,
form: props['form'],
});
}
} else {
modelValue.value = undefined;
}
};
validate = () => {
return numberRef.value.validate();
};
setValue = (value_) => {
modelValue.value = value_;
};
getValue = () => {
return modelValue.value;
};
clearValue = () => {
modelValue.value = undefined;
};
}
const fieldMethodsClass = new FieldMethods();
//
defineExpose({
validate: fieldMethodsClass.validate,
setValue: fieldMethodsClass.setValue,
getValue: fieldMethodsClass.getValue,
clearValue: fieldMethodsClass.clearValue,
});
</script>

14
io.sc.platform.core.frontend/src/platform/components/number/WNumber.vue

@ -10,7 +10,7 @@
v-bind="attrs"
type="number"
title=""
:rules="fieldMethodsClass.getRules(props, modelValue, numberRef, [FormValidators.maxPrecision(props.precision)])"
:rules="fieldMethodsClass.getRules(props, modelValue, numberRef, defaultRuleComputed)"
:readonly="fieldMethodsClass.getReadOnly(props, modelValue)"
:disable="fieldMethodsClass.getDisable(props, modelValue)"
@update:model-value="fieldMethodsClass.updateValue"
@ -20,7 +20,8 @@
</div>
</template>
<script setup lang="ts">
import { ref, useAttrs } from 'vue';
import { ref, useAttrs, computed } from 'vue';
import { Tools } from '@/platform';
import { FormValidators } from '@/platform/components';
import { FormFieldProps } from '@/platform/components/form/FormField.ts';
import { FormFieldMethods } from '../form/FormField';
@ -33,7 +34,7 @@ interface FieldProps extends FormFieldProps {
// FieldPropspropsdefineProps
precision?: number;
}
const props = withDefaults(defineProps<FieldProps>(), { precision: 0, showIf: true }); // 使withDefaults
const props = withDefaults(defineProps<FieldProps>(), { precision: undefined, showIf: true }); // 使withDefaults
class FieldMethods extends FormFieldMethods {
// FieldMethods
updateValue = (value_) => {
@ -65,6 +66,13 @@ class FieldMethods extends FormFieldMethods {
}
const fieldMethodsClass = new FieldMethods();
const defaultRuleComputed = computed(() => {
if (!Tools.isEmpty(props.precision)) {
return [FormValidators.maxPrecision(props.precision)];
}
return undefined;
});
//
defineExpose({
validate: fieldMethodsClass.validate,

9
io.sc.platform.core.frontend/src/platform/components/toolbar/WToolbar.vue

@ -476,8 +476,8 @@ const buttonClick = async (button) => {
selectedColName: props.grid.getSelectedCell()['colName'],
});
if (button.afterClick) {
nextTick(() => {
button.afterClick({
nextTick(async () => {
await button.afterClick({
selected: firstSelectedComputed.value,
selecteds: selectedComputed.value,
ticked: firstTickedComputed.value,
@ -486,6 +486,11 @@ const buttonClick = async (button) => {
context: context,
selectedColName: props.grid.getSelectedCell()['colName'],
});
let data = undefined;
if (button?.name !== 'add' && button?.name !== 'addTop' && button?.name !== 'addChild') {
data = firstSelectedComputed.value;
}
props.grid.emit('afterEditorOpen', { grid: props.grid, data: data });
});
}
}

Loading…
Cancel
Save