Browse Source

表格优化提交

main
likunming 1 year ago
parent
commit
765d7a264b
  1. 79
      io.sc.platform.core.frontend/src/platform/components/grid/TreeGridRow.vue
  2. 71
      io.sc.platform.core.frontend/src/platform/components/grid/WGrid.vue
  3. 8
      io.sc.platform.core.frontend/src/platform/components/toolbar/WToolbar.vue
  4. 25
      io.sc.platform.core.frontend/src/views/likm/Grid.vue
  5. 26
      io.sc.platform.core.frontend/src/views/likm/TreeGrid.vue

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

@ -1,6 +1,6 @@
<template> <template>
<q-tr :class="row.selected ? 'selected' : ''"> <q-tr :class="row.selected ? 'selected' : ''" @click="click($event, row, row.rowIndex)">
<q-td :style="tdWidthComputed" class="nowrap text-nowrap"> <q-td class="nowrap text-nowrap">
<div class="flex flex-nowrap items-center"> <div class="flex flex-nowrap items-center">
<!--层级占位符--> <!--层级占位符-->
<span :style="`width:${27 * props.level}px;`"></span> <span :style="`width:${27 * props.level}px;`"></span>
@ -8,11 +8,10 @@
<q-btn <q-btn
v-if="row.children && row.children.length > 0" v-if="row.children && row.children.length > 0"
flat flat
size="16px"
dense dense
padding="0px 0px" padding="0px 0px"
:icon="state.currRow.expand ? 'bi-dash' : 'bi-plus'" :icon="state.currRow.expand ? 'bi-dash' : 'bi-plus'"
@click="expandFun" @click.stop="expandFun"
/> />
<!--展开按钮占位符--> <!--展开按钮占位符-->
<span v-else style="width: 27px"></span> <span v-else style="width: 27px"></span>
@ -21,26 +20,35 @@
v-if="gridProps.checkboxSelection" v-if="gridProps.checkboxSelection"
v-model="state.currRow.selected" v-model="state.currRow.selected"
flat flat
size="40px"
dense dense
@update:model-value="selectedFun(state.currRow.selected, state.currRow)" @update:model-value="selectedFun(state.currRow.selected, state.currRow)"
/> />
<!--文件夹图标--> <!--图标-->
<q-icon v-if="row.children && row.children.length > 0" :name="'folder'" color="amber" 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 name="note" size="20px" class="px-1"></q-icon> <span v-else class="px-1"></span>
</div> <template v-if="cols[0]">
</q-td> <template v-if="cols[0].value && typeof cols[0].value === 'object' && cols[0].value.componentType">
<q-td v-for="col in cols" :key="col.name" :title="col.classes?.indexOf('truncate') > -1 ? col.value : ''" @click="click($el, row, row.rowIndex)"> <component :is="cols[0].value.componentType" v-bind="cols[0].value.attrs"></component>
<div class="flex flex-nowrap items-center"> </template>
<template v-if="col.value && typeof col.value === 'object' && col.value.componentType"> <template v-else>
<component :is="col.value.componentType" v-bind="col.value.attrs"></component> <span v-dompurify-html="cols[0].value || ''"></span>
</template> </template>
<template v-else>
<span v-dompurify-html="col.value || ''"></span>
</template> </template>
</div> </div>
</q-td> </q-td>
<template v-for="(col, index) in cols" :key="col.name">
<q-td v-if="index > 0" :title="col.classes?.indexOf('truncate') > -1 ? col.value : ''" @click="click($event, row, row.rowIndex)">
<div class="flex flex-nowrap items-center">
<template v-if="col.value && typeof col.value === 'object' && col.value.componentType">
<component :is="col.value.componentType" v-bind="col.value.attrs"></component>
</template>
<template v-else>
<span v-dompurify-html="col.value || ''"></span>
</template>
</div>
</q-td>
</template>
</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
@ -83,7 +91,7 @@ const props = defineProps({
return {}; return {};
}, },
}, },
rowKey: { type: String, default: 'rowKey_' }, rowKey: { type: String, default: '_rowKey_' },
selection: { type: String, default: 'single' }, selection: { type: String, default: 'single' },
}); });
@ -92,12 +100,6 @@ const state = reactive({
currRow: {}, currRow: {},
}); });
const tdWidthComputed = computed(() => {
let width = props.gridProps.checkboxSelection ? 28 * (3 + props.level) : 28 * (2 + props.level);
const padding = 8 * 3;
return { width: width + padding + 'px', minWidth: width + padding + 'px' };
});
watch( watch(
() => props.row.selected, () => props.row.selected,
(newVal, oldVal) => { (newVal, oldVal) => {
@ -105,6 +107,21 @@ watch(
}, },
); );
const iconComputed = computed(() => {
if (props.gridProps.treeIcon) {
return props.gridProps.treeIcon(props.row);
} else {
if (props.row.children && props.row.children.length > 0) {
return {
name: 'folder',
color: 'amber',
};
} else {
return 'note';
}
}
});
// //
const getCurrRow = (arr) => { const getCurrRow = (arr) => {
for (let i = 0; i < arr.length; i++) { for (let i = 0; i < arr.length; i++) {
@ -124,7 +141,7 @@ const getRow = (arr, key, parent) => {
let result = undefined; let result = undefined;
for (let i = 0; i < arr.length; i++) { for (let i = 0; i < arr.length; i++) {
let item = arr[i]; let item = arr[i];
if (parent ? item[props.gridProps.treePrimaryField] === key : item[props.rowKey] === key) { if (parent ? item[props.gridProps.primaryKey] === key : item[props.rowKey] === key) {
result = item; result = item;
break; break;
} else if (item.children && item.children.length > 0) { } else if (item.children && item.children.length > 0) {
@ -242,7 +259,13 @@ const childColsHandler = (child) => {
const cols = <any>[]; const cols = <any>[];
props.cols.forEach((col) => { props.cols.forEach((col) => {
if (props.columnsMap.get(col.name) && props.columnsMap.get(col.name).format) { if (props.columnsMap.get(col.name) && props.columnsMap.get(col.name).format) {
cols.push({ ...col, value: props.columnsMap.get(col.name).format(child[col.name]) }); let value = child[col.name];
try {
value = props.columnsMap.get(col.name).format(child[col.name], child);
} catch (error) {
console.error('format error!');
}
cols.push({ ...col, value: value });
} else { } else {
cols.push({ ...col, value: child[col.name] }); cols.push({ ...col, value: child[col.name] });
} }
@ -250,7 +273,7 @@ const childColsHandler = (child) => {
return cols; return cols;
}; };
const click = (el, row, rowIndex) => { const click = (evt, row, rowIndex) => {
table.rows.forEach((item) => { table.rows.forEach((item) => {
selectedFun(false, item); selectedFun(false, item);
}); });

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

@ -56,7 +56,7 @@
:rowspan="columnTitleState.columnTitleRowNum" :rowspan="columnTitleState.columnTitleRowNum"
:style="moreColumnTitleTableSelectionStyle" :style="moreColumnTitleTableSelectionStyle"
> >
<q-checkbox v-model="scope.selected" flat /> <q-checkbox v-model="scope.selected" flat :dense="denseHeader || attrs.dense ? true : false" />
</q-th> </q-th>
<q-th v-if="rIndex === 0 && props.sortNo" :rowspan="columnTitleState.columnTitleRowNum" :style="moreColumnTitleTableSortNoStyle"> 序号 </q-th> <q-th v-if="rIndex === 0 && props.sortNo" :rowspan="columnTitleState.columnTitleRowNum" :style="moreColumnTitleTableSortNoStyle"> 序号 </q-th>
<q-th <q-th
@ -79,9 +79,9 @@
:style="props.tree ? '' : 'padding: 0; width: 50px'" :style="props.tree ? '' : 'padding: 0; width: 50px'"
auto-width auto-width
> >
<q-checkbox v-model="scope.selected" flat <q-checkbox v-model="scope.selected" flat :dense="denseHeader || attrs.dense ? true : false"
/></q-th> /></q-th>
<q-th v-else-if="props.checkboxSelection || props.tree"></q-th> <q-th v-else-if="props.checkboxSelection && !props.tree"></q-th>
<q-th v-for="col in scope.cols" :key="col.name" :props="scope" :style="col.style" :class="col.classes"> <q-th v-for="col in scope.cols" :key="col.name" :props="scope" :style="col.style" :class="col.classes">
{{ col.label }} {{ col.label }}
</q-th> </q-th>
@ -99,9 +99,9 @@
:selection="selectionComputed" :selection="selectionComputed"
></TreeGridRow> ></TreeGridRow>
</template> </template>
<q-tr v-else :props="scope" @click="rowClick($el, scope.row, scope.rowIndex)" @dblclick="rowDbClick($el, scope.row, scope.rowIndex)"> <q-tr v-else :props="scope" @click="rowClick($event, scope.row, scope.rowIndex)" @dblclick="rowDbClick($el, scope.row, scope.rowIndex)">
<q-td v-if="props.checkboxSelection" class="text-center" style="padding: 0; width: 50px"> <q-td v-if="props.checkboxSelection" class="text-center" style="padding: 0; width: 50px">
<q-checkbox v-model="scope.selected" flat /> <q-checkbox v-model="scope.selected" flat :dense="denseBody || attrs.dense ? true : false" />
</q-td> </q-td>
<template v-if="draggable"> <template v-if="draggable">
<q-td <q-td
@ -233,11 +233,12 @@ const props = defineProps({
leftColumnStickyNumber: { type: Number, default: 0 }, // 1-10el leftColumnStickyNumber: { type: Number, default: 0 }, // 1-10el
checkboxSelection: { type: Boolean, default: true }, // checkbox checkboxSelection: { type: Boolean, default: true }, // checkbox
tree: { type: Boolean, default: false }, // tree: { type: Boolean, default: false }, //
treeRelationship: { type: String, default: 'parent' }, // parent, children treeIcon: { type: Function, default: undefined }, //
treePrimaryField: { type: String, default: 'id' }, //
treeRelationshipField: { type: String, default: 'parent' }, //
treeExpand: { type: Boolean, default: false }, // treeExpand: { type: Boolean, default: false }, //
treeExpandChildren: { type: Boolean, default: false }, // treeExpandChildren: { type: Boolean, default: false }, //
treeRelationship: { type: String, default: 'parent' }, // parent, childrenparentchildren
primaryKey: { type: String, default: 'id' }, // APIRestCrudControllerupdate
foreignKey: { type: String, default: 'parent' }, //
queryCriteria: { queryCriteria: {
// //
type: Object, type: Object,
@ -379,7 +380,9 @@ 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_';
const selected_ = '_selected_';
const ticked_ = '_ticked_';
const url = { const url = {
dataUrl: props.dataUrl, dataUrl: props.dataUrl,
fetchDataUrl: props.fetchDataUrl, fetchDataUrl: props.fetchDataUrl,
@ -531,7 +534,7 @@ const buttonObj = {
}).onOk(() => { }).onOk(() => {
let requestParams: any = { let requestParams: any = {
method: 'DELETE', method: 'DELETE',
url: (url.removeDataUrl || url.dataUrl) + '/' + selected[0][rowKey_], url: (url.removeDataUrl || url.dataUrl) + '/' + selected[0][props.primaryKey],
}; };
axios(requestParams) axios(requestParams)
.then((resp) => { .then((resp) => {
@ -637,6 +640,7 @@ const titleScopeHandler = (column: any, scope: any) => {
const table = reactive({ const table = reactive({
selected: <any>[], selected: <any>[],
ticked: <any>[],
spaceHeight: 4, spaceHeight: 4,
headerCellHeight: props.denseHeader || attrs.dense ? 24 : 48, headerCellHeight: props.denseHeader || attrs.dense ? 24 : 48,
bodyCellHeight: attrs.dense ? 24 : 48, bodyCellHeight: attrs.dense ? 24 : 48,
@ -919,7 +923,9 @@ const tableFullscreenFun = (value) => {
}; };
const rowClick = (evt: any, row: any, index: any) => { const rowClick = (evt: any, row: any, index: any) => {
table.selected = []; if (!evt.ctrlKey) {
table.selected = [];
}
table.selected.push(row); table.selected.push(row);
if (props.onRowClick) { if (props.onRowClick) {
emit('rowClick', evt, row, index); emit('rowClick', evt, row, index);
@ -1057,7 +1063,7 @@ const onRequest = async (ops: any) => {
table.pagination.rowsNumber = responseData.length; table.pagination.rowsNumber = responseData.length;
table.pagination.rowsPerPage = 0; table.pagination.rowsPerPage = 0;
if (props.treeRelationship === 'parent') { if (props.treeRelationship === 'parent') {
const treeRows = TreeBuilder.build(responseData, props.treeRelationshipField, props.treePrimaryField); const treeRows = TreeBuilder.build(responseData, props.foreignKey, props.primaryKey);
table.rows = treeRows; table.rows = treeRows;
} else { } else {
table.rows = responseData; table.rows = responseData;
@ -1066,7 +1072,7 @@ const onRequest = async (ops: any) => {
table.pagination.rowsNumber = responseData.content.length; table.pagination.rowsNumber = responseData.content.length;
table.pagination.rowsPerPage = 0; table.pagination.rowsPerPage = 0;
if (props.treeRelationship === 'parent') { if (props.treeRelationship === 'parent') {
const treeRows = TreeBuilder.build(responseData.content, props.treeRelationshipField, props.treePrimaryField); const treeRows = TreeBuilder.build(responseData.content, props.foreignKey, props.primaryKey);
table.rows = treeRows; table.rows = treeRows;
} else { } else {
table.rows = responseData.content; table.rows = responseData.content;
@ -1081,6 +1087,7 @@ const addRowKey = (rows: []) => {
if (rows && rows.length > 0) { if (rows && rows.length > 0) {
rows.forEach((item) => { rows.forEach((item) => {
item[rowKey_] = Tools.uuid(); item[rowKey_] = Tools.uuid();
item['_'];
if (props.tree && item.children && item.children.length > 0) { if (props.tree && item.children && item.children.length > 0) {
addRowKey(item.children); addRowKey(item.children);
} }
@ -1095,7 +1102,7 @@ const save = async () => {
if (validate) { if (validate) {
let dialogFormData = dialogFormRef.value.getData(); let dialogFormData = dialogFormRef.value.getData();
if (getSelectedRowsComputed.value && getSelectedRowsComputed.value.length > 0) { if (getSelectedRowsComputed.value && getSelectedRowsComputed.value.length > 0) {
dialogFormData[rowKey_] = getSelectedRowsComputed.value[0][rowKey_]; dialogFormData[props.primaryKey] = getSelectedRowsComputed.value[0][props.primaryKey];
} }
let submitFlag = true; let submitFlag = true;
emit('addEditDataSubmitBefore', dialogFormData, (handlerRequestParams: any | boolean) => { emit('addEditDataSubmitBefore', dialogFormData, (handlerRequestParams: any | boolean) => {
@ -1115,7 +1122,7 @@ const save = async () => {
? url.addDataUrl ? url.addDataUrl
? url.addDataUrl ? url.addDataUrl
: url.dataUrl : url.dataUrl
: (url.editDataUrl ? url.editDataUrl : url.dataUrl) + '/' + getSelectedRowsComputed.value[0][rowKey_], : (url.editDataUrl ? url.editDataUrl : url.dataUrl) + '/' + getSelectedRowsComputed.value[0][props.primaryKey],
}; };
dialog.dialogButtons[0].loading = false; dialog.dialogButtons[0].loading = false;
axios(requestParams) axios(requestParams)
@ -1147,11 +1154,22 @@ const onView = () => {
if (props.viewer.panel.fields && props.viewer.panel.fields.length > 0) { if (props.viewer.panel.fields && props.viewer.panel.fields.length > 0) {
for (let item of props.viewer.panel.fields) { for (let item of props.viewer.panel.fields) {
if (item.format) { if (item.format) {
viewInfo.infoArray.push({ label: item.label, value: item.format(getSelectedRowsComputed.value[0][item.name]) }); let value = getSelectedRowsComputed.value[0][item.name];
try {
value = item.format(getSelectedRowsComputed.value[0][item.name], getSelectedRowsComputed.value[0]);
} catch (error) {
console.error('format error!');
}
viewInfo.infoArray.push({ label: item.label, value: value });
} else { } else {
let value = null; let value = null;
if (tableColumnsMap.get(item.name) && tableColumnsMap.get(item.name).format) { if (tableColumnsMap.get(item.name) && tableColumnsMap.get(item.name).format) {
value = tableColumnsMap.get(item.name).format(getSelectedRowsComputed.value[0][item.name]); value = getSelectedRowsComputed.value[0][item.name];
try {
value = tableColumnsMap.get(item.name).format(getSelectedRowsComputed.value[0][item.name], getSelectedRowsComputed.value[0]);
} catch (error) {
console.error('format error!');
}
} else { } else {
value = getSelectedRowsComputed.value[0][item.name]; value = getSelectedRowsComputed.value[0][item.name];
} }
@ -1161,7 +1179,13 @@ const onView = () => {
} else { } else {
for (let item of tableColumnsMap) { for (let item of tableColumnsMap) {
if (item[1].format) { if (item[1].format) {
viewInfo.infoArray.push({ label: item[1].label, value: item[1].format(getSelectedRowsComputed.value[0][item[0]]) }); let value = getSelectedRowsComputed.value[0][item[0]];
try {
value = item[1].format(getSelectedRowsComputed.value[0][item[0]], getSelectedRowsComputed.value[0]);
} catch (error) {
console.error('format error!');
}
viewInfo.infoArray.push({ label: item[1].label, value: value });
} else { } else {
viewInfo.infoArray.push({ label: item[1].label, value: getSelectedRowsComputed.value[0][item[0]] }); viewInfo.infoArray.push({ label: item[1].label, value: getSelectedRowsComputed.value[0][item[0]] });
} }
@ -1240,8 +1264,16 @@ const getSelectedRowsComputed = computed(() => {
return selectedRows; return selectedRows;
}); });
// //
const getSelectedRows = () => { const getSelectedRows = () => {
if (props.tree) {
return getSelectedRowsComputed.value;
} else {
return getSelectedRowsComputed.value;
}
};
//
const getTickedRows = () => {
return getSelectedRowsComputed.value; return getSelectedRowsComputed.value;
}; };
@ -1785,6 +1817,7 @@ const setRemoveDataUrl = (url_) => {
defineExpose({ defineExpose({
getSelectedRows, getSelectedRows,
getTickedRows,
getRows, getRows,
refresh, refresh,
replaceRows, replaceRows,

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

@ -236,8 +236,12 @@ const onResize = (size) => {
availableWidth -= moreActionWidth; availableWidth -= moreActionWidth;
while (width > availableWidth) { while (width > availableWidth) {
index--; index--;
width -= buttons_[index].width; if (index >= 0) {
_baseActions.pop(); width -= buttons_[index].width;
_baseActions.pop();
} else {
break;
}
} }
break; break;
} else { } else {

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

@ -3,8 +3,9 @@
<w-grid <w-grid
:title="testGrid.title" :title="testGrid.title"
draggable draggable
:dense="true"
: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"
@ -12,6 +13,7 @@
:editor="testGrid.addEdit" :editor="testGrid.addEdit"
:viewer="testGrid.view" :viewer="testGrid.view"
@selection="selection" @selection="selection"
@row-click="rowClick"
></w-grid> ></w-grid>
</div> </div>
</template> </template>
@ -25,8 +27,27 @@ import { IconEnum } from '@/platform/enums';
const selection = (details) => { const selection = (details) => {
// console.info('details====', details); // console.info('details====', details);
}; };
const startY = ref(0);
const endY = ref(0);
const rowClick = (evt, row, index) => { const rowClick = (evt, row, index) => {
// console.info('row====', row); if (startY.value === 0) {
startY.value = evt.clientY;
} else if (evt.shiftKey && endY.value === 0) {
endY.value = evt.clientY;
}
console.info('startY====', startY.value);
console.info('endY====', endY.value);
// startY endY
if (startY.value > 0 && endY.value > 0) {
console.info('添加到选择中,同时清空 startY 与 endY');
startY.value = 0;
endY.value = 0;
}
// startY.value = 0;
// endY.value = 0;
}; };
const rowDbClick = (evt, row, index) => { const rowDbClick = (evt, row, index) => {
// console.info('row1====', row); // console.info('row1====', row);

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

@ -1,4 +1,4 @@
<!-- <template> <template>
<div> <div>
<w-grid <w-grid
ref="gridRef" ref="gridRef"
@ -6,7 +6,9 @@
draggable draggable
sort-no sort-no
tree tree
:checkbox-selection="false" :dense="true"
:tree-icon="(row) => {}"
:checkbox-selection="true"
:data-url="testGrid.dataUrl" :data-url="testGrid.dataUrl"
:fetch-data-url="testGrid.fetchDataUrl" :fetch-data-url="testGrid.fetchDataUrl"
:columns="testGrid.tableColumns" :columns="testGrid.tableColumns"
@ -88,27 +90,27 @@ const testGrid = {
{ {
name: 'name', name: 'name',
label: '菜单名称', label: '菜单名称',
width: 200, width: '50%',
format: (val, row) => { format: (val, row) => {
return t(val); return t(row.name);
}, },
}, },
{ {
name: 'icon', name: 'icon',
label: '图标', label: '图标',
width: 200, width: '50%',
format: (val, row) => { format: (val, row) => {
return { return {
componentType: 'q-icon', componentType: 'q-icon',
attrs: { attrs: {
name: val, name: val,
size: 'sm', size: 'xs',
}, },
}; };
}, },
}, },
{ name: 'type', label: '菜单类型', width: 200 }, { name: 'type', label: '菜单类型' },
{ name: 'order', label: '排序号', width: 200 }, { name: 'order', label: '排序号' },
{ {
name: 'enable', name: 'enable',
label: '是否可用', label: '是否可用',
@ -119,7 +121,7 @@ const testGrid = {
attrs: { attrs: {
name: val ? IconEnum.是状态 : IconEnum.否状态, name: val ? IconEnum.是状态 : IconEnum.否状态,
color: val ? 'green' : 'red', color: val ? 'green' : 'red',
size: 'sm', size: 'xs',
}, },
}; };
}, },
@ -132,9 +134,9 @@ const testGrid = {
onMounted(() => { onMounted(() => {
// testGridRef.value.replaceRowsFun([{ typeName: '', typeDesc: '', lastModifier: 'admin', lastModifyDate: '2023-12-26' }]); // testGridRef.value.replaceRowsFun([{ typeName: '', typeDesc: '', lastModifier: 'admin', lastModifyDate: '2023-12-26' }]);
}); });
</script> --> </script>
<template> <!-- <template>
<div> <div>
<w-grid <w-grid
ref="gridRef" ref="gridRef"
@ -244,7 +246,7 @@ const testGrid = {
onMounted(() => { onMounted(() => {
// testGridRef.value.replaceRowsFun([{ typeName: '', typeDesc: '', lastModifier: 'admin', lastModifyDate: '2023-12-26' }]); // testGridRef.value.replaceRowsFun([{ typeName: '', typeDesc: '', lastModifier: 'admin', lastModifyDate: '2023-12-26' }]);
}); });
</script> </script> -->
<!-- <template> <!-- <template>
<div> <div>

Loading…
Cancel
Save