Browse Source

表格优化提交

main
likunming 1 year ago
parent
commit
7d80acd15b
  1. 143
      io.sc.platform.core.frontend/src/platform/components/grid/TreeGridRow.vue
  2. 303
      io.sc.platform.core.frontend/src/platform/components/grid/WGrid.vue
  3. 21
      io.sc.platform.core.frontend/src/platform/components/toolbar/ChildrenBtn.vue
  4. 33
      io.sc.platform.core.frontend/src/platform/components/toolbar/WToolbar.vue
  5. 106
      io.sc.platform.core.frontend/src/views/likm/Grid.vue
  6. 47
      io.sc.platform.core.frontend/src/views/likm/TreeGrid.vue
  7. 2
      io.sc.platform.system/src/main/java/io/sc/platform/system/menu/jpa/entity/MenuEntity.java

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

@ -1,5 +1,9 @@
<template> <template>
<q-tr :class="row.selected ? 'selected' : ''" @click="click($event, row, row.rowIndex)"> <q-tr
:class="row[table.selectedField] ? 'selected' : ''"
@click.stop.prevent="click($event, row, row.rowIndex)"
@dblclick.stop.prevent="dbClick($event, row, row.rowIndex)"
>
<q-td class="nowrap text-nowrap"> <q-td class="nowrap text-nowrap">
<div class="flex flex-nowrap items-center"> <div class="flex flex-nowrap items-center">
<!--层级占位符--> <!--层级占位符-->
@ -10,13 +14,21 @@
flat flat
dense dense
padding="0px 0px" padding="0px 0px"
:icon="state.currRow.expand ? 'bi-dash' : 'bi-plus'" :icon="row.expand ? 'bi-dash' : 'bi-plus'"
@click.stop="expandFun" @click.stop="expandFun(row)"
@dblclick.stop="() => {}"
/> />
<!--展开按钮占位符--> <!--展开按钮占位符-->
<span v-else style="width: 27px"></span> <span v-else style="width: 27px"></span>
<!--选择框--> <!--选择框-->
<q-checkbox v-if="table.checkboxSelection" v-model="state.currRow.selected" flat dense @update:model-value="selectedFun(state.currRow.selected, row)" /> <q-checkbox
v-if="table.checkboxSelection"
v-model="getRow(table.rows, row[props.rowKey], false)[table.tickedField]"
flat
dense
@dblclick.stop="() => {}"
@update:model-value="selectedFun(row[table.tickedField], row, $event)"
/>
<!--图标--> <!--图标-->
<q-icon v-if="typeof iconComputed === 'string'" :name="iconComputed" size="20px" class="px-1"></q-icon> <q-icon v-if="typeof iconComputed === 'string'" :name="iconComputed" size="20px" class="px-1"></q-icon>
<q-icon v-else-if="typeof iconComputed === 'object'" size="20px" v-bind="iconComputed" class="px-1"></q-icon> <q-icon v-else-if="typeof iconComputed === 'object'" size="20px" v-bind="iconComputed" class="px-1"></q-icon>
@ -32,11 +44,7 @@
</div> </div>
</q-td> </q-td>
<template v-for="(col, index) in cols" :key="col.name"> <template v-for="(col, index) in cols" :key="col.name">
<q-td <q-td v-if="index > 0" :title="col.classes?.indexOf('truncate') > -1 && col.value && typeof col.value !== 'object' ? col.value : ''">
v-if="index > 0"
:title="col.classes?.indexOf('truncate') > -1 && col.value && typeof col.value !== 'object' ? col.value : ''"
@click="click($event, row, row.rowIndex)"
>
<div :class="col.__thClass"> <div :class="col.__thClass">
<template v-if="col.value && typeof col.value === 'object' && col.value.componentType"> <template v-if="col.value && typeof col.value === 'object' && col.value.componentType">
<component :is="col.value.componentType" v-bind="col.value.attrs"></component> <component :is="col.value.componentType" v-bind="col.value.attrs"></component>
@ -50,13 +58,14 @@
</q-tr> </q-tr>
<template v-for="child in row.children" :key="child[rowKey]"> <template v-for="child in row.children" :key="child[rowKey]">
<TreeGridRow <TreeGridRow
v-if="state.currRow.expand" v-if="row.expand"
:columns-map="props.columnsMap" :columns-map="props.columnsMap"
:row="child" :row="child"
:cols="childColsHandler(child)" :cols="childColsHandler(child)"
:level="props.level + 1" :level="props.level + 1"
:grid-props="gridProps" :grid-props="gridProps"
:row-key="props.rowKey" :row-key="props.rowKey"
:grid="props.grid"
></TreeGridRow> ></TreeGridRow>
</template> </template>
</template> </template>
@ -89,19 +98,14 @@ const props = defineProps({
}, },
}, },
rowKey: { type: String, default: '_rowKey_' }, rowKey: { type: String, default: '_rowKey_' },
grid: {
type: Object,
default: () => {
return {};
},
},
}); });
const table = inject('table'); const table = inject('table');
const state = reactive({
currRow: {},
});
watch(
() => props.row.selected,
(newVal, oldVal) => {
state.currRow.selected = newVal;
},
);
const iconComputed = computed(() => { const iconComputed = computed(() => {
if (props.gridProps.treeIcon) { if (props.gridProps.treeIcon) {
@ -118,20 +122,6 @@ const iconComputed = computed(() => {
} }
}); });
//
const getCurrRow = (arr) => {
for (let i = 0; i < arr.length; i++) {
let item = arr[i];
if (item[props.rowKey] === props.row[props.rowKey]) {
state.currRow = { expand: false, selected: false, ...item };
break;
} else if (item.children && item.children.length > 0) {
getCurrRow(item.children);
}
}
};
getCurrRow(table.rows);
// key // key
const getRow = (arr, key, parent) => { const getRow = (arr, key, parent) => {
let result = undefined; let result = undefined;
@ -151,60 +141,54 @@ const getRow = (arr, key, parent) => {
}; };
// / // /
const expandFun = () => { const expandFun = (row) => {
if (state.currRow) { row.expand = !row.expand;
state.currRow.expand = !state.currRow.expand;
}
}; };
// checkbox // checkbox
const selectedFun = (value, row) => { const selectedFun = (value, row, event) => {
if (value) { if (value) {
selectedPush(row); getRow(table.rows, row[props.rowKey], false)[table.tickedField] = true;
getRow(table.rows, row[props.rowKey], false).selected = true;
} else { } else {
selectedRemove(row); getRow(table.rows, row[props.rowKey], false)[table.tickedField] = false;
getRow(table.rows, row[props.rowKey], false).selected = false;
} }
selectedChildren(row, value); selectedChildren(row, value);
selectedParent(row, value); selectedParent(row, value);
if (props.gridProps.onUpdateTicked) {
props.gridProps.onUpdateTicked(event, row);
}
}; };
// //
const selectedChildren = (row, selected) => { const selectedChildren = (row, value) => {
if (row.children && row.children.length > 0) { if (row.children && row.children.length > 0) {
for (let child of row.children) { for (let child of row.children) {
child['selected'] = selected; child[table.tickedField] = value;
if (selected) { selectedChildren(child, value);
selectedPush(child);
} else {
selectedRemove(child);
}
selectedChildren(child, selected);
} }
} }
}; };
// //
const selectedParent = (row, selected) => { const selectedParent = (row, value) => {
if (row.parent) { if (row.parent) {
const parent = getRow(table.rows, row.parent, true); const parent = getRow(table.rows, row.parent, true);
if (parent) { if (parent) {
if (selected && childrenSelectedStatus(parent).allSelected) { if (value && childrenSelectedStatus(parent).allSelected) {
parent.selected = true; parent[table.tickedField] = true;
selectedPush(parent); // selectedPush(parent);
} else if (selected) { } else if (value) {
parent.selected = null; parent[table.tickedField] = null;
} else { } else {
selectedRemove(parent); // selectedRemove(parent);
if (childrenSelectedStatus(parent).partSelected) { if (childrenSelectedStatus(parent).partSelected) {
parent.selected = null; parent[table.tickedField] = null;
} else { } else {
parent.selected = false; parent[table.tickedField] = false;
} }
} }
} }
selectedParent(parent, selected); selectedParent(parent, value);
} }
}; };
@ -216,7 +200,7 @@ const childrenSelectedStatus = (row) => {
}; };
if (row.children && row.children.length > 0) { if (row.children && row.children.length > 0) {
for (let i = 0; i < row.children.length; i++) { for (let i = 0; i < row.children.length; i++) {
if (!row.children[i].selected) { if (!row.children[i][table.tickedField]) {
result.allSelected = false; result.allSelected = false;
} else { } else {
result.partSelected = true; result.partSelected = true;
@ -233,23 +217,6 @@ const childrenSelectedStatus = (row) => {
return result; return result;
}; };
//
const selectedPush = (row) => {
// push
if (table.selected.findIndex((item) => item[props.rowKey] === row[props.rowKey]) < 0) {
table.selected.push(row);
}
};
const selectedRemove = (row) => {
// remove
if (table.selected.findIndex((item) => item[props.rowKey] === row[props.rowKey]) > -1) {
table.selected.splice(
table.selected.findIndex((item) => item[props.rowKey] === row[props.rowKey]),
1,
);
}
};
// cols // cols
const childColsHandler = (child) => { const childColsHandler = (child) => {
const cols = <any>[]; const cols = <any>[];
@ -270,13 +237,19 @@ const childColsHandler = (child) => {
}; };
const click = (evt, row, rowIndex) => { const click = (evt, row, rowIndex) => {
table.rows.forEach((item) => { if (!evt.ctrlKey) {
selectedFun(false, item); props.grid.cleanSelected();
}); }
table.selected.splice(0, table.selected.length); row[table.selectedField] = true;
selectedFun(true, row);
if (props.gridProps.onRowClick) { if (props.gridProps.onRowClick) {
props.gridProps.onRowClick(evt, row, rowIndex); props.gridProps.onRowClick(evt, row, rowIndex);
} }
}; };
const dbClick = (evt, row, rowIndex) => {
if (props.gridProps.onRowDbClick) {
props.gridProps.onRowDbClick(evt, row, rowIndex);
} else {
props.grid.view();
}
};
</script> </script>

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

@ -3,7 +3,6 @@
<q-table <q-table
ref="tableRef" ref="tableRef"
v-model:pagination="state.pagination" v-model:pagination="state.pagination"
v-model:selected="table.selected"
flat flat
binary-state-sort binary-state-sort
:no-data-label="state.noDataLabel" :no-data-label="state.noDataLabel"
@ -29,15 +28,7 @@
<div class="pt-2.5 flex flex-nowrap items-end"> <div class="pt-2.5 flex flex-nowrap items-end">
<div class="flex-none">{{ title }}</div> <div class="flex-none">{{ title }}</div>
<div class="flex-1"> <div class="flex-1">
<w-toolbar <w-toolbar :dense="denseToolbarComputed" v-bind="toolbarConfigure" :buttons="buttons_" :grid="instance"></w-toolbar>
:dense="denseToolbarComputed"
v-bind="toolbarConfigure"
:buttons="buttons_"
:grid-props="{
selected: getSelectedRowsComputed,
grid: instance,
}"
></w-toolbar>
</div> </div>
<div v-if="configButton" class="flex-none pl-1"> <div v-if="configButton" class="flex-none pl-1">
<q-btn round dense :size="denseToolbarComputed ? '13px' : '14px'" :icon="IconEnum.设置" unelevated outline> <q-btn round dense :size="denseToolbarComputed ? '13px' : '14px'" :icon="IconEnum.设置" unelevated outline>
@ -102,11 +93,17 @@
</template> </template>
<template #body="scope"> <template #body="scope">
<template v-if="tree"> <template v-if="tree">
<TreeGridRow :columns-map="tableColumnsMap" :row="scope.row" :cols="scope.cols" :grid-props="props" :row-key="rowKey_"></TreeGridRow> <TreeGridRow :columns-map="tableColumnsMap" :row="scope.row" :cols="scope.cols" :grid-props="props" :row-key="rowKey_" :grid="instance"></TreeGridRow>
</template> </template>
<q-tr v-else :props="scope" @click="rowClick($event, scope.row, scope.rowIndex)" @dblclick="rowDbClick($el, scope.row, scope.rowIndex)"> <q-tr
v-else
:class="scope.row[table.selectedField] ? 'selected' : ''"
:props="scope"
@click.stop="rowClick($event, scope.row, scope.rowIndex)"
@dblclick.stop="rowDbClick($event, scope.row, scope.rowIndex)"
>
<q-td v-if="table.checkboxSelection" class="text-center" style="padding: 0; width: 50px"> <q-td v-if="table.checkboxSelection" class="text-center" style="padding: 0; width: 50px">
<q-checkbox v-model="scope.selected" flat :dense="denseBodyComputed" /> <q-checkbox v-model="scope.row[table.tickedField]" flat :dense="denseBodyComputed" @update:model-value="updateTicked($event, scope.row)" />
</q-td> </q-td>
<template v-if="draggable"> <template v-if="draggable">
<q-td <q-td
@ -247,10 +244,12 @@ const props = defineProps({
sortNo: { type: Boolean, default: false }, // sortNo: { type: Boolean, default: false }, //
stickyNum: { type: Number, default: 0 }, // 1-10el stickyNum: { type: Number, default: 0 }, // 1-10el
checkboxSelection: { type: Boolean, default: true }, // checkbox checkboxSelection: { type: Boolean, default: true }, // checkbox
tickedField: { type: String, default: 'ticked' }, // checkbox
selectedField: { type: String, default: 'selected' }, //
tree: { type: Boolean, default: false }, // tree: { type: Boolean, default: false }, //
treeIcon: { type: Function, default: undefined }, // treeIcon: { type: Function, default: undefined }, //
treeExpand: { type: Boolean, default: false }, // treeDefaultExpandAll: { type: Boolean, default: false }, //
treeExpandChildren: { type: Boolean, default: false }, // treeTickStrategy: { type: String, default: 'leaf' }, // strictleaf
treeRelationship: { type: String, default: 'parent' }, // parent, childrenparentchildren treeRelationship: { type: String, default: 'parent' }, // parent, childrenparentchildren
primaryKey: { type: String, default: 'id' }, // APIRestCrudControllerupdate primaryKey: { type: String, default: 'id' }, // APIRestCrudControllerupdate
foreignKey: { type: String, default: 'parent' }, // foreignKey: { type: String, default: 'parent' }, //
@ -327,26 +326,24 @@ const props = defineProps({
}, },
onRowClick: { onRowClick: {
type: Function, type: Function,
default: null, default: undefined,
}, // 使 },
onRowDbClick: { onRowDbClick: {
type: Function, type: Function,
default: null, default: undefined,
}, // 使 },
onUpdateTicked: {
type: Function,
default: undefined,
},
}); });
const emit = defineEmits<{ const emit = defineEmits<{
( //
e: 'rowClick', // (e: 'rowClick', evt: Event, row: any, index: number): void;
evt: Event, // JS //
row: any, // , (e: 'rowDbClick', evt: Event, row: any, index: number): void;
index: number, // , // checkbox
): void; (e: 'updateTicked', evt: Event, row: any): void;
(
e: 'rowDbClick', //
evt: Event, // JS
row: any, // ,
index: number, // ,
): void;
( (
e: 'requestDataBefore', // e: 'requestDataBefore', //
requestParams: URLSearchParams | any, // requestParams: URLSearchParams | any, //
@ -395,9 +392,7 @@ const dialogFormRef = ref();
const infoRef = ref(); const infoRef = ref();
const tableColumnsMap = arrayToMap('name', props.columns); const tableColumnsMap = arrayToMap('name', props.columns);
const queryFormFieldsMap = arrayToMap('name', props.queryFormFields); const queryFormFieldsMap = arrayToMap('name', props.queryFormFields);
const rowKey_ = '_rowKey_'; const rowKey_ = '_rowKey_'; // UUID
const selected_ = '_selected_';
const ticked_ = '_ticked_';
const url = { const url = {
dataUrl: props.dataUrl, dataUrl: props.dataUrl,
fetchDataUrl: props.fetchDataUrl, fetchDataUrl: props.fetchDataUrl,
@ -406,6 +401,47 @@ const url = {
removeDataUrl: props.removeDataUrl, removeDataUrl: props.removeDataUrl,
}; };
const extractTableColumns = extractTableColumnsProps(props);
// props
const titleScopeHandler = (column: any, scope: any) => {
if (extractTableColumns && extractTableColumns.length > 0) {
const i = extractTableColumns.findIndex((item) => item.name === column.name);
if (i > -1) {
return scope;
}
}
return undefined;
};
const table = reactive({
tickedField: props.tickedField,
selectedField: props.selectedField,
spaceHeight: 4,
gridConfig: false,
stickyNum: props.stickyNum,
columns: extractTableColumns,
treeExpand: props.treeDefaultExpandAll,
checkboxSelection: props.checkboxSelection,
sortNo: props.sortNo,
dense: props.dense !== undefined ? props.dense : false,
denseToolbar: props.denseToolbar !== undefined ? props.denseToolbar : false,
denseHeader: props.denseHeader !== undefined ? props.denseHeader : false,
denseBody: props.denseBody !== undefined ? props.denseBody : false,
denseBottom: props.denseBottom !== undefined ? props.denseBottom : false,
queryFormFields: [],
moreQueryStatus: false, //
rows: <any>[],
inFullscreen: false, //
});
provide('table', table);
const expandIcon = computed(() => {
return table.treeExpand ? 'expand_less' : 'expand_more';
});
const expandLabel = computed(() => {
return table.treeExpand ? '收起所有节点' : '展开所有节点';
});
/** /**
* 内置按钮枚举 * 内置按钮枚举
*/ */
@ -421,6 +457,9 @@ enum ButtonEnum {
removeAll = 'removeAll', // removeAll = 'removeAll', //
view = 'view', // view = 'view', //
export = 'export', // export = 'export', //
addTop = 'addTop', //
addChild = 'addChild', //
expand = 'expand', // ?
} }
/** /**
* 内置按钮 * 内置按钮
@ -431,7 +470,7 @@ const buttonObj = {
name: ButtonEnum.query, name: ButtonEnum.query,
icon: IconEnum.查询, icon: IconEnum.查询,
label: '查询', label: '查询',
click: (selected, context) => { click: () => {
refresh(); refresh();
}, },
}, },
@ -446,7 +485,7 @@ const buttonObj = {
return true; return true;
} }
}, },
click: (selected, context) => { click: () => {
table.moreQueryStatus = !table.moreQueryStatus; table.moreQueryStatus = !table.moreQueryStatus;
handlerQueryFormShowField(); handlerQueryFormShowField();
}, },
@ -455,7 +494,7 @@ const buttonObj = {
name: ButtonEnum.reset, name: ButtonEnum.reset,
icon: IconEnum.重置, icon: IconEnum.重置,
label: '重置', label: '重置',
click: (selected, context) => { click: () => {
queryFormRef.value.reset(); queryFormRef.value.reset();
}, },
}, },
@ -463,7 +502,7 @@ const buttonObj = {
name: ButtonEnum.refresh, name: ButtonEnum.refresh,
icon: IconEnum.刷新, icon: IconEnum.刷新,
label: '刷新', label: '刷新',
click: (selected, context) => { click: () => {
refresh(); refresh();
}, },
}, },
@ -471,7 +510,7 @@ const buttonObj = {
name: ButtonEnum.add, name: ButtonEnum.add,
icon: IconEnum.新增, icon: IconEnum.新增,
label: '新增', label: '新增',
click: (selected, context) => { click: () => {
dialog.dialogTitle = '新增'; dialog.dialogTitle = '新增';
dialogRef.value.show(); dialogRef.value.show();
nextTick(() => { nextTick(() => {
@ -484,13 +523,13 @@ const buttonObj = {
name: ButtonEnum.edit, name: ButtonEnum.edit,
icon: IconEnum.编辑, icon: IconEnum.编辑,
label: '编辑', label: '编辑',
enableIf: (selected) => { enableIf: (selected, ticked, grid) => {
if (selected && selected.length > 0) { if (selected && selected.length > 0) {
return true; return true;
} }
return false; return false;
}, },
click: (selected, context) => { click: (selected) => {
if (!selected || selected.length <= 0) { if (!selected || selected.length <= 0) {
NotifyManager.warn('请选择要编辑的记录'); NotifyManager.warn('请选择要编辑的记录');
} else { } else {
@ -514,7 +553,7 @@ const buttonObj = {
} }
return false; return false;
}, },
click: (selected, context) => { click: (selected) => {
if (!selected || selected.length <= 0) { if (!selected || selected.length <= 0) {
NotifyManager.warn('请选择要复制的记录'); NotifyManager.warn('请选择要复制的记录');
} else { } else {
@ -531,8 +570,10 @@ const buttonObj = {
name: ButtonEnum.remove, name: ButtonEnum.remove,
icon: IconEnum.删除, icon: IconEnum.删除,
label: '删除', label: '删除',
enableIf: (selected) => { enableIf: (selected, ticked, grid) => {
if (selected && selected.length > 0) { if (ticked && ticked.length > 0) {
return true;
} else if (selected && selected.length > 0) {
return true; return true;
} }
return false; return false;
@ -553,7 +594,6 @@ const buttonObj = {
}; };
axios(requestParams) axios(requestParams)
.then((resp) => { .then((resp) => {
table.selected = [];
NotifyManager.info('操作成功'); NotifyManager.info('操作成功');
onRequest({ pagination: state.pagination }); onRequest({ pagination: state.pagination });
}) })
@ -576,7 +616,7 @@ const buttonObj = {
return false; return false;
}, },
click: (selected, context) => { click: (selected, context) => {
onView(); view();
}, },
}, },
export: { export: {
@ -605,6 +645,55 @@ const buttonObj = {
} }
}, },
}, },
addTop: {
name: ButtonEnum.addTop,
icon: IconEnum.新增,
label: '新增顶级节点',
click: (selected, context) => {
dialog.dialogTitle = '新增顶级节点';
dialogRef.value.show();
nextTick(() => {
dialogFormRef.value.setStatus('addTop');
emit('addDialogOpenAfter');
});
},
},
addChild: {
name: ButtonEnum.addChild,
icon: 'playlist_add',
label: '新增子节点',
enableIf: (selected) => {
if (selected && selected.length > 0) {
return true;
}
return false;
},
click: (selected, context) => {
dialog.dialogTitle = '新增子节点';
dialogRef.value.show();
nextTick(() => {
dialogFormRef.value.setStatus('addChild');
emit('addDialogOpenAfter');
});
},
},
expand: {
name: ButtonEnum.expand,
icon: expandIcon.value,
label: expandLabel.value,
click: (selected, context) => {
expandFun(table.rows, table.treeExpand);
table.treeExpand = !table.treeExpand;
},
},
};
const expandFun = (arr, treeExpand) => {
arr.forEach((item) => {
if (props.tree && item.children && item.children.length > 0) {
item.expand = !treeExpand;
expandFun(item.children, treeExpand);
}
});
}; };
// toobar // toobar
@ -641,39 +730,6 @@ props.toolbarActions.forEach((btn, index) => {
} }
}); });
const extractTableColumns = extractTableColumnsProps(props);
// props
const titleScopeHandler = (column: any, scope: any) => {
if (extractTableColumns && extractTableColumns.length > 0) {
const i = extractTableColumns.findIndex((item) => item.name === column.name);
if (i > -1) {
return scope;
}
}
return undefined;
};
const table = reactive({
selected: <any>[],
ticked: <any>[],
spaceHeight: 4,
gridConfig: false,
stickyNum: props.stickyNum,
columns: extractTableColumns,
checkboxSelection: props.checkboxSelection,
sortNo: props.sortNo,
dense: props.dense !== undefined ? props.dense : false,
denseToolbar: props.denseToolbar !== undefined ? props.denseToolbar : false,
denseHeader: props.denseHeader !== undefined ? props.denseHeader : false,
denseBody: props.denseBody !== undefined ? props.denseBody : false,
denseBottom: props.denseBottom !== undefined ? props.denseBottom : false,
queryFormFields: [],
moreQueryStatus: false, //
rows: <any>[],
inFullscreen: false, //
});
provide('table', table);
const denseToolbarComputed = computed(() => { const denseToolbarComputed = computed(() => {
if (table.denseToolbar) { if (table.denseToolbar) {
return true; return true;
@ -996,20 +1052,28 @@ const tableFullscreenFun = (value) => {
}; };
const rowClick = (evt: any, row: any, index: any) => { const rowClick = (evt: any, row: any, index: any) => {
// checkboxcheckboxcheckbox
if (!evt.ctrlKey) { if (!evt.ctrlKey) {
table.selected = []; cleanSelected();
cleanTicked();
} }
table.selected.push(row); row[table.selectedField] = true;
row[table.tickedField] = true;
if (props.onRowClick) { if (props.onRowClick) {
emit('rowClick', evt, row, index); emit('rowClick', evt, row, index);
} }
}; };
const rowDbClick = (evt, row, index) => { const rowDbClick = (evt, row, index) => {
if (props.onRowDbClick) { if (props.onRowDbClick) {
emit('rowDbClick', evt, row, index); emit('rowDbClick', evt, row, index);
} else { } else {
onView(); view();
}
};
const updateTicked = (evt: Event, row: any) => {
row[table.selectedField] = row[table.tickedField];
if (props.onUpdateTicked) {
emit('updateTicked', evt, row);
} }
}; };
@ -1158,9 +1222,13 @@ const onRequest = async (ops: any) => {
const addRowKey = (rows: []) => { const addRowKey = (rows: []) => {
if (rows && rows.length > 0) { if (rows && rows.length > 0) {
rows.forEach((item) => { rows.forEach((item: any) => {
item[rowKey_] = Tools.uuid(); item[rowKey_] = Tools.uuid();
item['_']; item[table.tickedField] = item[table.tickedField] || false;
item[table.selectedField] = item[table.selectedField] || false;
if (props.tree) {
item['expand'] = false;
}
if (props.tree && item.children && item.children.length > 0) { if (props.tree && item.children && item.children.length > 0) {
addRowKey(item.children); addRowKey(item.children);
} }
@ -1174,7 +1242,7 @@ const save = async () => {
const validate = await dialogFormRef.value.validate(); const validate = await dialogFormRef.value.validate();
if (validate) { if (validate) {
let dialogFormData = dialogFormRef.value.getData(); let dialogFormData = dialogFormRef.value.getData();
if (getSelectedRowsComputed.value && getSelectedRowsComputed.value.length > 0) { if (formStatus === PageStatusEnum.编辑 && getSelectedRowsComputed.value && getSelectedRowsComputed.value.length > 0) {
dialogFormData[props.primaryKey] = getSelectedRowsComputed.value[0][props.primaryKey]; dialogFormData[props.primaryKey] = getSelectedRowsComputed.value[0][props.primaryKey];
} }
let submitFlag = true; let submitFlag = true;
@ -1186,12 +1254,17 @@ const save = async () => {
} }
}); });
if (submitFlag) { if (submitFlag) {
if (formStatus === 'addTop') {
dialogFormData[props.foreignKey] = null;
} else if (formStatus === 'addChild') {
dialogFormData[props.foreignKey] = getSelectedRowsComputed.value[0][props.primaryKey];
}
let requestParams = { let requestParams = {
method: formStatus === PageStatusEnum.新增 ? 'POST' : 'PUT', method: formStatus === PageStatusEnum.新增 || formStatus === 'addTop' || formStatus === 'addChild' ? 'POST' : 'PUT',
headers: { 'content-type': 'application/json;charset=utf-8;' }, headers: { 'content-type': 'application/json;charset=utf-8;' },
data: dialogFormData, data: dialogFormData,
url: url:
formStatus === PageStatusEnum.新增 formStatus === PageStatusEnum.新增 || formStatus === 'addTop' || formStatus === 'addChild'
? url.addDataUrl ? url.addDataUrl
? url.addDataUrl ? url.addDataUrl
: url.dataUrl : url.dataUrl
@ -1205,7 +1278,6 @@ const save = async () => {
NotifyManager.info('操作成功'); NotifyManager.info('操作成功');
dialogRef.value.hide(); dialogRef.value.hide();
onRequest({ pagination: state.pagination }); onRequest({ pagination: state.pagination });
table.selected = [];
}) })
.catch((error) => { .catch((error) => {
console.info('error====', error); console.info('error====', error);
@ -1219,7 +1291,7 @@ const save = async () => {
dialog.dialogButtons[0].loading = false; dialog.dialogButtons[0].loading = false;
} }
}; };
const onView = () => { const view = () => {
if (!getSelectedRowsComputed.value || getSelectedRowsComputed.value.length <= 0) { if (!getSelectedRowsComputed.value || getSelectedRowsComputed.value.length <= 0) {
NotifyManager.warn('请选择要查看的记录'); NotifyManager.warn('请选择要查看的记录');
} else { } else {
@ -1320,34 +1392,34 @@ const addRow = (row, index) => {
stickyHeaderColumn(); stickyHeaderColumn();
}; };
const getSelectedRowsHandler = (arr, selectedRows) => { const getSelectRowsByFieldName = (arr, selectedRows, fieldName) => {
arr.forEach((item) => { arr.forEach((item) => {
if (table.selected.findIndex((tmp) => tmp[rowKey_] === item[rowKey_]) > -1) { if (item[fieldName]) {
selectedRows.push(item); selectedRows.push(item);
} }
if (props.tree && item.children && item.children.length > 0) { if (props.tree && item.children && item.children.length > 0) {
getSelectedRowsHandler(item.children, selectedRows); getSelectRowsByFieldName(item.children, selectedRows, fieldName);
} }
}); });
}; };
const getSelectedRowsComputed = computed(() => { const getSelectedRowsComputed = computed(() => {
const selectedRows = <any>[]; return getSelectedRows();
getSelectedRowsHandler(table.rows, selectedRows);
return selectedRows;
}); });
const getTickedRowsComputed = computed(() => {
// return getTickedRows();
});
//
const getSelectedRows = () => { const getSelectedRows = () => {
if (props.tree) { const selectedRows = [];
return getSelectedRowsComputed.value; getSelectRowsByFieldName(table.rows, selectedRows, table.selectedField);
} else { return toRaw(selectedRows);
return getSelectedRowsComputed.value;
}
}; };
// // checkbox
const getTickedRows = () => { const getTickedRows = () => {
return getSelectedRowsComputed.value; const tickedRows = [];
getSelectRowsByFieldName(table.rows, tickedRows, table.tickedField);
return toRaw(tickedRows);
}; };
const getRows = () => { const getRows = () => {
@ -1915,6 +1987,24 @@ const setEditDataUrl = (url_) => {
const setRemoveDataUrl = (url_) => { const setRemoveDataUrl = (url_) => {
url.removeDataUrl = url_; url.removeDataUrl = url_;
}; };
//
const cleanSelected = (arr = table.rows) => {
arr.forEach((item) => {
item[table.selectedField] = false;
if (props.tree && item.children && item.children.length > 0) {
cleanSelected(item.children);
}
});
};
// checkbox
const cleanTicked = (arr = table.rows) => {
arr.forEach((item) => {
item[table.tickedField] = false;
if (props.tree && item.children && item.children.length > 0) {
cleanTicked(item.children);
}
});
};
defineExpose({ defineExpose({
getSelectedRows, getSelectedRows,
@ -1938,6 +2028,9 @@ defineExpose({
setEditDataUrl, setEditDataUrl,
setRemoveDataUrl, setRemoveDataUrl,
stickyHeaderColumn, stickyHeaderColumn,
cleanSelected,
cleanTicked,
view,
}); });
const instance = getCurrentInstance(); const instance = getCurrentInstance();

21
io.sc.platform.core.frontend/src/platform/components/toolbar/ChildrenBtn.vue

@ -1,5 +1,5 @@
<template> <template>
<q-item clickable :dense="dense" :disable="button[0].enableIf ? !button[0].enableIf(gridProps.selected, gridProps.grid) : false"> <q-item clickable :dense="dense" :disable="button[0].enableIf ? !button[0].enableIf(selectedComputed, tickedComputed, grid) : false">
<q-item-section> <q-item-section>
<q-item-label><q-icon v-if="button[0].icon" :name="button[0].icon" left size="20px"></q-icon> {{ button[0].label }}</q-item-label> <q-item-label><q-icon v-if="button[0].icon" :name="button[0].icon" left size="20px"></q-icon> {{ button[0].label }}</q-item-label>
</q-item-section> </q-item-section>
@ -16,14 +16,14 @@
v-else-if="Array.isArray(childrenBtn) && childrenBtn.length > 0" v-else-if="Array.isArray(childrenBtn) && childrenBtn.length > 0"
:dense="dense" :dense="dense"
:button="childrenBtn" :button="childrenBtn"
:grid-props="gridProps" :grid="grid"
:button-click="buttonClick" :button-click="buttonClick"
></ChildrenBtn> ></ChildrenBtn>
<template v-else> <template v-else>
<q-item <q-item
v-close-popup v-close-popup
clickable clickable
:disable="childrenBtn.enableIf ? !childrenBtn.enableIf(gridProps.selected, gridProps.grid) : false" :disable="childrenBtn.enableIf ? !childrenBtn.enableIf(selectedComputed, tickedComputed, grid) : false"
:dense="dense" :dense="dense"
@click="buttonClick(childrenBtn)" @click="buttonClick(childrenBtn)"
> >
@ -38,6 +38,7 @@
</q-item> </q-item>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue';
const props = defineProps({ const props = defineProps({
button: { button: {
type: Object, type: Object,
@ -45,13 +46,10 @@ const props = defineProps({
return []; return [];
}, },
}, },
gridProps: { grid: {
type: Object, type: Object,
default: () => { default: () => {
return { return {};
selected: [],
grid: undefined,
};
}, },
}, },
buttonClick: { buttonClick: {
@ -62,4 +60,11 @@ const props = defineProps({
}, },
dense: { type: Boolean, default: false }, dense: { type: Boolean, default: false },
}); });
const selectedComputed = computed(() => {
return props.grid.getSelectedRows();
});
const tickedComputed = computed(() => {
return props.grid.getTickedRows();
});
</script> </script>

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

@ -14,7 +14,7 @@
:dense="dense" :dense="dense"
v-bind="btn.data[0]" v-bind="btn.data[0]"
:split="btn.data[0].click ? true : false" :split="btn.data[0].click ? true : false"
:disable="btn.data[0].enableIf ? !btn.data[0].enableIf(gridProps.selected, gridProps.grid) : false" :disable="btn.data[0].enableIf ? !btn.data[0].enableIf(selectedComputed, tickedComputed, grid) : false"
class="class-action-item" class="class-action-item"
@click="buttonClick(btn.data[0])" @click="buttonClick(btn.data[0])"
> >
@ -26,7 +26,7 @@
<ChildrenBtn <ChildrenBtn
v-else-if="Array.isArray(childrenBtn) && childrenBtn.length > 0" v-else-if="Array.isArray(childrenBtn) && childrenBtn.length > 0"
:button="childrenBtn" :button="childrenBtn"
:grid-props="gridProps" :grid="grid"
:button-click="buttonClick" :button-click="buttonClick"
:dense="dense" :dense="dense"
></ChildrenBtn> ></ChildrenBtn>
@ -35,7 +35,7 @@
v-close-popup v-close-popup
clickable clickable
:dense="dense" :dense="dense"
:disable="childrenBtn.enableIf ? !childrenBtn.enableIf(gridProps.selected) : false" :disable="childrenBtn.enableIf ? !childrenBtn.enableIf(selectedComputed, tickedComputed, grid) : false"
@click="buttonClick(childrenBtn)" @click="buttonClick(childrenBtn)"
> >
<q-item-section> <q-item-section>
@ -48,7 +48,7 @@
</q-btn-dropdown> </q-btn-dropdown>
<q-btn <q-btn
v-else v-else
:disable="btn.data.enableIf ? !btn.data.enableIf(gridProps.selected, gridProps.grid) : false" :disable="btn.data.enableIf ? !btn.data.enableIf(selectedComputed, tickedComputed, grid) : false"
no-wrap no-wrap
outline outline
:dense="dense" :dense="dense"
@ -66,7 +66,7 @@
<ChildrenBtn <ChildrenBtn
v-else-if="Array.isArray(childrenBtn.data) && childrenBtn.data.length > 0" v-else-if="Array.isArray(childrenBtn.data) && childrenBtn.data.length > 0"
:button="childrenBtn.data" :button="childrenBtn.data"
:grid-props="gridProps" :grid="grid"
:button-click="buttonClick" :button-click="buttonClick"
:dense="dense" :dense="dense"
></ChildrenBtn> ></ChildrenBtn>
@ -75,7 +75,7 @@
v-close-popup v-close-popup
clickable clickable
:dense="dense" :dense="dense"
:disable="childrenBtn.data.enableIf ? !childrenBtn.data.enableIf(gridProps.selected, gridProps.grid) : false" :disable="childrenBtn.data.enableIf ? !childrenBtn.data.enableIf(selectedComputed, tickedComputed, grid) : false"
@click="buttonClick(childrenBtn.data)" @click="buttonClick(childrenBtn.data)"
> >
<q-item-section> <q-item-section>
@ -92,7 +92,7 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, nextTick } from 'vue'; import { ref, reactive, computed, nextTick } from 'vue';
import { Tools } from '@/platform/utils'; import { Tools } from '@/platform/utils';
import ChildrenBtn from './ChildrenBtn.vue'; import ChildrenBtn from './ChildrenBtn.vue';
@ -107,13 +107,10 @@ const props = defineProps({
return []; return [];
}, },
}, },
gridProps: { grid: {
type: Object, type: Object,
default: () => { default: () => {
return { return {};
selected: [],
grid: undefined,
};
}, },
}, },
}); });
@ -266,17 +263,23 @@ const onResize = (size) => {
moreActions.value = _moreActions; moreActions.value = _moreActions;
}; };
const selectedComputed = computed(() => {
return props.grid.getSelectedRows();
});
const tickedComputed = computed(() => {
return props.grid.getTickedRows();
});
const buttonClick = async (button) => { const buttonClick = async (button) => {
let beforeResult = true; let beforeResult = true;
const context = {}; const context = {};
if (button.beforeClick) { if (button.beforeClick) {
beforeResult = await button.beforeClick(props.gridProps.selected, context, props.gridProps.grid); beforeResult = await button.beforeClick(selectedComputed.value, tickedComputed.value, context, props.grid);
} }
if (beforeResult && button.click) { if (beforeResult && button.click) {
await button.click(props.gridProps.selected, context, button._click, props.gridProps.grid); await button.click(selectedComputed.value, tickedComputed.value, context, button._click, props.grid);
if (button.afterClick) { if (button.afterClick) {
nextTick(() => { nextTick(() => {
button.afterClick(props.gridProps.selected, context, props.gridProps.grid); button.afterClick(selectedComputed.value, tickedComputed.value, context, props.grid);
}); });
} }
} }

106
io.sc.platform.core.frontend/src/views/likm/Grid.vue

@ -4,16 +4,16 @@
:title="testGrid.title" :title="testGrid.title"
draggable draggable
:data-url="testGrid.tableDataUrl" :data-url="testGrid.tableDataUrl"
:checkbox-selection="false" :checkbox-selection="true"
selection="multiple" selection="multiple"
:columns="testGrid.tableColumns" :columns="testGrid.tableColumns"
:toolbar-actions="testGrid.toolbar" :toolbar-actions="testGrid.toolbar"
:query-form-fields="testGrid.queryForm" :query-form-fields="testGrid.queryForm"
:editor="testGrid.addEdit" :editor="testGrid.addEdit"
:viewer="testGrid.view" :viewer="testGrid.view"
@selection="selection" @update-ticked="updateTicked"
@update:selected="aaa"
@row-click="rowClick" @row-click="rowClick"
@row-db-click="rowDbClick"
></w-grid> ></w-grid>
</div> </div>
</template> </template>
@ -24,36 +24,34 @@ import { axios, Environment } from '@/platform';
import EnableIcon from '@/platform/components/grid/EnableIcon.vue'; import EnableIcon from '@/platform/components/grid/EnableIcon.vue';
import { IconEnum } from '@/platform/enums'; import { IconEnum } from '@/platform/enums';
const selection = (details) => { const updateTicked = (event, row) => {
// console.info('details====', details); console.info('grid.updateTicked.event====', event);
}; console.info('grid.updateTicked.row====', row);
const aaa = (newSelected) => {
console.info('newSelected========', newSelected);
}; };
const startY = ref(0); const startY = ref(0);
const endY = ref(0); const endY = ref(0);
const rowClick = (evt, row, index) => { const rowClick = (evt, row, index) => {
if (startY.value === 0) { // if (startY.value === 0) {
startY.value = evt.clientY; // startY.value = evt.clientY;
} else if (evt.shiftKey && endY.value === 0) { // } else if (evt.shiftKey && endY.value === 0) {
endY.value = evt.clientY; // endY.value = evt.clientY;
} // }
console.info('startY====', startY.value); // console.info('startY====', startY.value);
console.info('endY====', endY.value); // console.info('endY====', endY.value);
// startY endY // // startY endY
if (startY.value > 0 && endY.value > 0) { // if (startY.value > 0 && endY.value > 0) {
console.info('添加到选择中,同时清空 startY 与 endY'); // console.info(' startY endY');
startY.value = 0; // startY.value = 0;
endY.value = 0; // endY.value = 0;
} // }
// startY.value = 0; // startY.value = 0;
// endY.value = 0; // endY.value = 0;
console.info('grid.rowClick.row====', row);
}; };
const rowDbClick = (evt, row, index) => { const rowDbClick = (evt, row, index) => {
// console.info('row1====', row); console.info('grid.rowDbClick.row====', row);
}; };
const testGrid = reactive({ const testGrid = reactive({
@ -130,37 +128,37 @@ const testGrid = reactive({
// { label: '', name: 'lastModifyDate', type: 'w-date' }, // { label: '', name: 'lastModifyDate', type: 'w-date' },
], ],
tableColumns: [ tableColumns: [
{ // {
name: 'info', // name: 'info',
label: '用户信息', // label: '',
hidden: true, // hidden: true,
childrenColumns: [ // childrenColumns: [
{ name: 'loginName', label: '登录名', align: 'right' }, // { name: 'loginName', label: '', align: 'right' },
{ name: 'userName', label: '用户名' }, // { name: 'userName', label: '' },
], // ],
}, // },
{ // {
name: 'lxxx', // name: 'lxxx',
label: '联系方式', // label: '',
childrenColumns: [ // childrenColumns: [
{ name: 'email', label: '邮箱地址' }, // { name: 'email', label: '' },
{ // {
name: 'tx', // name: 'tx',
label: '通讯', // label: '',
childrenColumns: [ // childrenColumns: [
{ name: 'phone', label: '电话' }, // { name: 'phone', label: '' },
{ name: 'mobile', label: '手机号' }, // { name: 'mobile', label: '' },
], // ],
}, // },
{ name: 'qq', label: 'QQ' }, // { name: 'qq', label: 'QQ' },
], // ],
}, // },
// { name: 'loginName', label: '', align: 'right' }, { name: 'loginName', label: '登录名', align: 'right' },
// { name: 'userName', label: '' }, { name: 'userName', label: '用户名' },
// { name: 'email', label: '' }, { name: 'email', label: '邮箱地址' },
// { name: 'phone', label: '' }, { name: 'phone', label: '电话' },
// { name: 'mobile', label: '' }, { name: 'mobile', label: '手机号' },
// { name: 'qq', label: 'QQ' }, { name: 'qq', label: 'QQ' },
{ name: 'description', label: '描述', width: 400 }, { name: 'description', label: '描述', width: 400 },
{ {
name: 'enable', name: 'enable',

47
io.sc.platform.core.frontend/src/views/likm/TreeGrid.vue

@ -11,11 +11,11 @@
:data-url="testGrid.dataUrl" :data-url="testGrid.dataUrl"
:fetch-data-url="testGrid.fetchDataUrl" :fetch-data-url="testGrid.fetchDataUrl"
:columns="testGrid.tableColumns" :columns="testGrid.tableColumns"
:editor="testGrid.editor"
:toolbar-actions="testGrid.toolbar" :toolbar-actions="testGrid.toolbar"
:query-form-fields="testGrid.queryFormFields" :query-form-fields="testGrid.queryFormFields"
:query-form-cols-num="3" :query-form-cols-num="3"
@selection="selection" @update-ticked="updateTicked"
@row-click="rowClick"
></w-grid> ></w-grid>
</div> </div>
</template> </template>
@ -29,15 +29,22 @@ import { IconEnum } from '@/platform/enums';
const { t } = useI18n(); const { t } = useI18n();
const gridRef = ref(); const updateTicked = (event, row) => {
const rowClick = (evt, row, rowIndex) => { console.info('treeGrid.updateTicked.event====', event);
// console.info('evt========', evt); console.info('treeGrid.updateTicked.row====', row);
// console.info('row========', row);
// console.info('rowIndex========', rowIndex);
}; };
const selection = (details) => {
// console.info('details====', details); const startY = ref(0);
const endY = ref(0);
const rowClick = (evt, row, index) => {
console.info('grid.rowClick.row====', row);
};
const rowDbClick = (evt, row, index) => {
console.info('grid.rowDbClick.row====', index);
}; };
const gridRef = ref();
const testGrid = { const testGrid = {
title: '菜单列表', title: '菜单列表',
dataUrl: Environment.apiContextPath('api/system/menu'), dataUrl: Environment.apiContextPath('api/system/menu'),
@ -70,6 +77,9 @@ const testGrid = {
// gridRefs.addEditFormRef.setFieldValue('userName', ''); // gridRefs.addEditFormRef.setFieldValue('userName', '');
}, },
}, },
'addTop',
'addChild',
'expand',
[ [
{ {
name: 'op', name: 'op',
@ -95,7 +105,7 @@ const testGrid = {
{ {
name: 'name', name: 'name',
label: '菜单名称', label: '菜单名称',
width: 500, // width: 500,
format: (val, row) => { format: (val, row) => {
return t(row.name); return t(row.name);
}, },
@ -139,7 +149,11 @@ const testGrid = {
// { name: 'lastModifyDate', label: '' }, // { name: 'lastModifyDate', label: '' },
// ], // ],
// }, // },
{ name: 'type', label: '菜单类型', width: 500 }, {
name: 'type',
label: '菜单类型',
// , width: 500
},
{ name: 'order', label: '排序号' }, { name: 'order', label: '排序号' },
{ {
name: 'enable', name: 'enable',
@ -160,6 +174,17 @@ const testGrid = {
{ name: 'lastModifier', label: '最后修改人' }, { name: 'lastModifier', label: '最后修改人' },
{ name: 'lastModifyDate', label: '最后修改时间' }, { name: 'lastModifyDate', label: '最后修改时间' },
], ],
editor: {
form: {
fields: [
{ label: '菜单名称', name: 'name', type: 'text' },
{ label: '菜单类型', name: 'type', type: 'text' },
{ label: '菜单图标', name: 'icon', type: 'text' },
{ label: '排序号', name: 'order', type: 'number' },
{ label: '是否可用', name: 'enable', type: 'checkbox' },
],
},
},
}; };
onMounted(() => { onMounted(() => {

2
io.sc.platform.system/src/main/java/io/sc/platform/system/menu/jpa/entity/MenuEntity.java

@ -30,7 +30,7 @@ import java.util.Objects;
@DiscriminatorColumn(name="TYPE_",discriminatorType=DiscriminatorType.STRING,length=20) @DiscriminatorColumn(name="TYPE_",discriminatorType=DiscriminatorType.STRING,length=20)
@Table(name="SYS_MENU") @Table(name="SYS_MENU")
@JsonIgnoreProperties(ignoreUnknown=true) @JsonIgnoreProperties(ignoreUnknown=true)
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type",visible=true) @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type",visible=true,defaultImpl =MenuGroupEntity.class )
@JsonSubTypes({ @JsonSubTypes({
@JsonSubTypes.Type(value = MenuGroupEntity.class, name = "GROUP"), @JsonSubTypes.Type(value = MenuGroupEntity.class, name = "GROUP"),
@JsonSubTypes.Type(value = MenuRouteEntity.class, name = "ROUTE"), @JsonSubTypes.Type(value = MenuRouteEntity.class, name = "ROUTE"),

Loading…
Cancel
Save