Browse Source

修复以下问题:

1、树拖拽会出现清除孩子的bug
2、w-grid 分组,需增加 group-by-title 属性,用于生成分组标题的函数
3、w-info 增加labelWidth与valueWidth属性
main
likunming 4 days ago
parent
commit
94ef288b30
  1. 4
      io.sc.platform.core.frontend/src/platform/components/date/WDateRange.vue
  2. 2
      io.sc.platform.core.frontend/src/platform/components/form/FormField.ts
  3. 2
      io.sc.platform.core.frontend/src/platform/components/grid/extra/group/GroupTr.vue
  4. 5
      io.sc.platform.core.frontend/src/platform/components/grid/ts/Init.ts
  5. 9
      io.sc.platform.core.frontend/src/platform/components/grid/ts/computed/ComputedManager.ts
  6. 49
      io.sc.platform.core.frontend/src/platform/components/grid/ts/function/DragAndDrop.ts
  7. 24
      io.sc.platform.core.frontend/src/platform/components/grid/ts/function/RowData.ts
  8. 4
      io.sc.platform.core.frontend/src/platform/components/grid/ts/types/PropsType.ts
  9. 25
      io.sc.platform.core.frontend/src/platform/components/panel/WInfoPanel.vue
  10. 112
      io.sc.platform.core.frontend/src/views/likm/Grid.vue
  11. 79
      io.sc.platform.core.frontend/src/views/likm/QuasarGrid.vue
  12. 26
      io.sc.platform.core.frontend/src/views/likm/TreeGrid.vue

4
io.sc.platform.core.frontend/src/platform/components/date/WDateRange.vue

@ -67,13 +67,13 @@ class FieldMethods extends FormFieldMethods {
return modelValue.value;
};
clearValue = () => {
modelValue.value = undefined;
modelValue.value = { from: '', to: '' };
};
}
const fieldMethodsClass = new FieldMethods();
const displayValueComputed = computed(() => {
if (modelValue.value) {
if (modelValue.value && modelValue.value['from']) {
return modelValue.value['from'] + ' 至 ' + modelValue.value['to'];
}
return '';

2
io.sc.platform.core.frontend/src/platform/components/form/FormField.ts

@ -111,6 +111,8 @@ export const getDefaultValue = (field) => {
return [];
} else if (field.type === 'w-code-mirror' || field.type === 'w-date') {
return '';
} else if (field.type === 'w-date-range') {
return { from: '', to: '' };
}
return undefined;
};

2
io.sc.platform.core.frontend/src/platform/components/grid/extra/group/GroupTr.vue

@ -11,7 +11,7 @@
:icon="record['expand'] ? 'remove' : 'add'"
@click.stop="changeExpand(record)"
/>
<span class="ml-3 font-medium">{{ record['groupName'] }}</span>
<span class="ml-3 font-medium">{{ record['groupTitle'] }}</span>
</q-td>
</q-tr>
<template v-if="record['expand']">

5
io.sc.platform.core.frontend/src/platform/components/grid/ts/Init.ts

@ -58,6 +58,11 @@ export class Init {
return [];
},
},
groupByTitle: {
// 分组显示内容,仅 alone 模式有效,支持字符串 `format` 使用分组字段列定义中配置的format进行内容显示处理,函数支持自定义显示内容
type: [String, Function],
default: undefined,
},
selectableIf: { type: Function, default: undefined }, // 数据行可选规则函数,不配置都可选,配置后根据返回的布尔结果判定是否可选。
treeTickableIf: { type: Function, default: undefined }, // 树表格可勾选规则函数,不配置都可勾选,配置后根据返回的布尔结果判定是否可勾选。
appendRows: {

9
io.sc.platform.core.frontend/src/platform/components/grid/ts/computed/ComputedManager.ts

@ -70,7 +70,14 @@ export class ComputedManager extends Base {
*/
mergeGroupByField = computed(() => {
if (typeof this.props.groupByField === 'string') {
[this.props.groupByField];
return [this.props.groupByField];
}
return this.props.groupByField;
});
aloneGroupByField = computed(() => {
if (Array.isArray(this.props.groupByField)) {
return this.props.groupByField[0];
}
return this.props.groupByField;
});

49
io.sc.platform.core.frontend/src/platform/components/grid/ts/function/DragAndDrop.ts

@ -1,6 +1,6 @@
import { Tools } from '@/platform';
import { toRaw, Ref, ref } from 'vue';
import { Constant, PropsType, TableType } from '../index';
import { Constant, GridTools, PropsType, TableType } from '../index';
import { Base } from '../Base';
/**
@ -202,11 +202,37 @@ export class DragAndDrop extends Base {
} else {
scope.row.children = [item];
}
this.updateOrderData.push(item);
this.updateOrderData.push(GridTools.removeExtraFields(item));
});
} else {
/**
* <
* parent属性hibernate代理对象class判断为非同一个class
*
*/
const waitHandleData = <any>[];
this.table.store.dragRecords.forEach((item) => {
const parentId = item[this.props.foreignKey];
if (!Tools.isEmpty(parentId)) {
const tmpParent = this.tools?.dataFM.getRow(this.table.rows, parentId, true, this.props.primaryKey);
if (parentId === scope.row[this.props.primaryKey] || tmpParent[this.props.foreignKey] === scope.row[this.props.foreignKey]) {
const cascadeData = {};
cascadeData['node'] = item[this.props.primaryKey];
cascadeData['order'] = item[this.props.dndOrderBy];
cascadeData['parent'] = parentId;
cascadeData['parentOrder'] = tmpParent[this.props.dndOrderBy];
waitHandleData.push(cascadeData);
}
}
});
// 将拖动的数据及其子节点放到目标数据位置,并将目标数据父节点下的子节点重新排序。
this.resetOrder(e, this.table.store.dragRecords, this.table.rows, scope.row, trMiddleHeight);
// 拖拽完成后重新将数据排序,避免出现子节点数据在其parent前进行处理导致出现hibernate代理对象,然后其父的其他子节点全部被删除的情况。
if (waitHandleData.length > 0) {
this.updateDataResetOrder(waitHandleData);
}
}
// 请求后端更新排序
@ -217,6 +243,22 @@ export class DragAndDrop extends Base {
this.tools?.em.afterDragAndDrop(this.updateOrderData);
}
private updateDataResetOrder(waitHandleData: any) {
this.updateOrderData.sort((a, b) => {
const aFindIndex = waitHandleData.findIndex((item) => item['node'] === a[this.props.primaryKey]);
const bFindIndex = waitHandleData.findIndex((item) => item['node'] === b[this.props.primaryKey]);
if (aFindIndex > -1 && bFindIndex > -1) {
return 0;
} else if (aFindIndex > -1) {
return 1;
} else if (bFindIndex > -1) {
return -1;
} else {
return 0;
}
});
}
// 重新设置树的排序
private resetOrder(e, dragRecords, arr, targetData, trMiddleHeight: number) {
for (let i = 0; i < arr.length; i++) {
@ -299,7 +341,8 @@ export class DragAndDrop extends Base {
arr.forEach((item, index) => {
item[this.props.dndOrderBy] = index + 1;
// 添加至待更新集合中
this.updateOrderData.push({ ...toRaw(item), children: null });
const data = GridTools.removeExtraFields(toRaw(item));
this.updateOrderData.push({ ...data, children: null });
});
}

24
io.sc.platform.core.frontend/src/platform/components/grid/ts/function/RowData.ts

@ -144,6 +144,13 @@ export class RowData extends Base {
this.arrayOrderBy(rows, groupByFields, ops);
}
this.rowDataExtraPropertyHandle(rows, lazyloadNoChildren);
// 设置 alone 分组下的 title
if (this.props.groupMode === Constant.GROUP_MODE.ALONE && this.table.configStore.aloneGroupRecords.length > 0) {
this.table.configStore.aloneGroupRecords.forEach((item) => {
const groupTitle = this.getGroupByTitle(item['groupName'], item['rows']);
item['groupTitle'] = groupTitle;
});
}
}
/**
@ -318,6 +325,23 @@ export class RowData extends Base {
}
}
private getGroupByTitle(groupName: string, rows: any) {
if (this.props.groupByTitle && typeof this.props.groupByTitle === 'string' && this.props.groupByTitle === 'format') {
// 使用分组字段的列配置定义的format格式化显示值
const column = this.table.columns.find((item) => item.name === this.tools?.cm.aloneGroupByField.value);
if (column?.format) {
return column.format(groupName, rows[0]);
}
} else if (this.props.groupByTitle && typeof this.props.groupByTitle === 'function') {
return this.props.groupByTitle({
grid: this.instance,
groupName: groupName,
rows: rows,
});
}
return groupName;
}
/**
*
* @param rowData

4
io.sc.platform.core.frontend/src/platform/components/grid/ts/types/PropsType.ts

@ -159,6 +159,10 @@ export type PropsType = {
* alone
*/
groupByField: string | Array<any>,
/**
* alone `format` 使format进行内容显示处理
*/
groupByTitle: string | Function,
/**
*
*/

25
io.sc.platform.core.frontend/src/platform/components/panel/WInfoPanel.vue

@ -1,12 +1,12 @@
<template>
<div>
<q-markup-table separator="cell" flat bordered wrap-cells v-bind="attrs">
<q-markup-table separator="cell" flat bordered v-bind="attrs">
<tbody>
<template v-for="(trItem, trIndex) in tableComputed" :key="trIndex">
<tr class="q-tr--no-hover">
<template v-for="(tdItem, tdIndex) in trItem as any" :key="tdIndex">
<td :class="labelAlignClassComputed">{{ tdItem.label }}</td>
<td :class="valueAlignClassComputed">
<td :class="labelAlignClassComputed" :style="labelWidthComputed">{{ tdItem.label }}</td>
<td :class="valueAlignClassComputed" :style="valueWidthComputed">
<template v-if="tdItem.value && typeof tdItem.value === 'object' && tdItem.value.componentType && tdItem.value.bindModelValue">
<component :is="tdItem.value.componentType" v-bind="tdItem.value.attrs" v-model="tdItem.originalValue"></component>
</template>
@ -38,6 +38,8 @@ const props = defineProps({
columnNum: { type: Number, default: 1 },
labelAlign: { type: String, default: 'left' },
valueAlign: { type: String, default: 'left' },
labelWidth: { type: [Number, String], default: undefined },
valueWidth: { type: [Number, String], default: undefined },
info: {
type: Array,
default: () => {
@ -46,6 +48,23 @@ const props = defineProps({
},
});
const labelWidthComputed = computed(() => {
if (props.labelWidth && typeof props.labelWidth === 'number') {
return 'min-width: ' + props.labelWidth + 'px; width: ' + props.labelWidth + 'px; max-width: ' + props.labelWidth + 'px;';
} else if (props.labelWidth && typeof props.labelWidth === 'string') {
return 'min-width: ' + props.labelWidth + '; width: ' + props.labelWidth + '; max-width: ' + props.labelWidth + ';';
}
return props.labelWidth;
});
const valueWidthComputed = computed(() => {
if (props.valueWidth && typeof props.valueWidth === 'number') {
return 'min-width: ' + props.valueWidth + 'px; width: ' + props.valueWidth + 'px; max-width: ' + props.valueWidth + 'px;';
} else if (props.labelWidth && typeof props.labelWidth === 'string') {
return 'min-width: ' + props.valueWidth + '; width: ' + props.valueWidth + '; max-width: ' + props.valueWidth + ';';
}
return props.valueWidth;
});
const tableComputed = computed(() => {
const table = <any>[];
let tmp = <any>[];

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

@ -1,70 +1,76 @@
<template>
<div class="h-full">
<w-grid
ref="gridRef"
title="示例列表"
:data-url="Environment.apiContextPath('/api/system/application')"
db-click-operation="testAdd"
selected-mode="cell"
:cell-selected="true"
:toolbar-actions="[
'add',
:title="$t('menu.developer.springboot.bean')"
:checkbox-selection="false"
:fetch-data-url="Environment.apiContextPath('/api/developer/springboot/beans')"
:pageable="false"
:toolbar-actions="['refresh', 'separator', 'view', 'separator', 'export']"
group-mode="alone"
group-by-field="scope"
:columns="[
{ width: 500, name: 'name', label: $t('name') },
{ width: 400, name: 'type', label: $t('className'), format: Formater.simpleClassName() },
{
extend: 'add',
name: 'testAdd',
click: (args) => {
const cell = args.grid.getSelectedCell();
console.info('cell=========', args);
width: 100,
name: 'scope',
label: $t('scope'),
sortable: false,
format: (val) => {
if (val === 'singleton') {
return '测试';
} else if (val === 'prototype') {
return '测试2';
}
return val;
},
},
[
'remove',
]"
:viewer="{
panel: {
columnNum: 2,
labelWidth: 200,
valueWidth: '100%',
fields: [
{ width: 100, name: 'name', label: $t('name') },
{ width: 100, name: 'context', label: $t('context') },
{
extend: 'edit',
name: 'testEdit',
click: (args) => {
console.info('testEdit======', args);
width: 100,
name: 'scope',
label: $t('scope'),
},
{ width: 100, name: 'type', label: $t('className'), format: Formater.none() },
{ width: 100, name: 'resource', label: $t('resource') },
{
width: 100,
name: 'aliases',
label: $t('aliases'),
format: (aliases) => {
aliases = aliases || [];
let result = '';
for (const aliase of aliases) {
result += aliase + '<br/>';
}
return result;
},
},
],
'query',
]"
:query-form-cols-num="12"
:query-form-fields="[{ colSpan: 3, name: 'code', label: '编码', type: 'w-text' }]"
:columns="[
{ name: 'code', label: '编码', type: 'w-text' },
{ name: 'name', label: '名称', type: 'w-text' },
{
name: 'enable',
label: '状态',
format: Formater.enableTag(),
width: 100,
name: 'dependencies',
label: $t('dependencies'),
format: (dependencies) => {
dependencies = dependencies || [];
let result = '';
for (const dependency of dependencies) {
result += dependency + '<br/>';
}
return result;
},
{ name: 'lastModifier', label: '最后修改人', align: 'center' },
{ name: 'lastModifyDate', label: '最后修改日期', align: 'center' },
]"
:editor="{
dialog: {
width: '80%',
height: '80%',
},
form: {
colsNum: 2,
fields: [
{ name: 'code', label: '编码', type: 'w-text' },
{ name: 'name', label: '名称', type: 'w-text' },
],
},
}"
@row-click="rowClick"
>
</w-grid>
</div>
></w-grid>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Environment, Formater } from '@/platform';
const gridRef = ref();
const rowClick = () => {};
import { Environment, Formater, $t } from '@/platform';
</script>

79
io.sc.platform.core.frontend/src/views/likm/QuasarGrid.vue

@ -1,69 +1,50 @@
<template>
<div class="q-pa-md">
<q-table v-model:selected="selected" title="Treats" row-key="name" selection="multiple" :rows="rows" :columns="columns">
<template #body="scope">
<q-tr ref="trRef" class="selected" :props="scope">
<q-td class="text-center" style="padding: 0; width: 50px">
<q-checkbox v-model="scope.row['selected']" flat @update:model-value="updateTicked($event, scope.row)" />
</q-td>
<q-td v-for="col in scope.cols" :key="col.name" :props="scope">
<GridFormat :value="col.value"></GridFormat>
</q-td>
</q-tr>
</template>
</q-table>
<q-table
v-model:selected="selected"
title="bean"
row-key="name"
selection="multiple"
:rows="rows"
:columns="columns"
:pagination="{
rowsPerPage: 0,
}"
></q-table>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Tools } from '@/platform';
import GridFormat from './GridForamt.vue';
import { ref, onMounted } from 'vue';
import { Tools, axios, Environment } from '@/platform';
const selected = ref([]);
const updateTicked = (event, row) => {
row['selected'] = !row['selected'];
};
const columns = [
{
name: 'name',
required: true,
label: 'Dessert (100g serving)',
label: '名称',
field: 'field',
align: 'left',
field: (row) => row.name,
sortable: true,
},
{ name: 'calories', align: 'center', label: 'Calories', field: 'calories', sortable: true },
{
name: 'fat',
label: 'Fat (g)',
field: 'fat',
sortable: true,
format: (val, row) => {
console.info('format.val====', val);
return val;
},
name: 'type',
label: '类型',
field: 'type',
align: 'left',
},
{ name: 'carbs', label: 'Carbs (g)', field: 'carbs' },
{ name: 'protein', label: 'Protein (g)', field: 'protein' },
{ name: 'sodium', label: 'Sodium (mg)', field: 'sodium' },
{ name: 'calcium', label: 'Calcium (%)', field: 'calcium', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10) },
{ name: 'iron', label: 'Iron (%)', field: 'iron', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10) },
];
const rows = [
{
name: 'Frozen Yogurt',
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
sodium: 87,
calcium: '14%',
iron: '1%',
selected: false,
name: 'scope',
label: '作用域',
field: 'scope',
align: 'left',
},
];
const rows = ref([]);
onMounted(async () => {
const data = await axios.get(Environment.apiContextPath('/api/developer/springboot/beans'));
rows.value = data.data;
});
</script>

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

@ -3,10 +3,11 @@
<w-grid
ref="gridRef"
title="树形表格示例"
:data-url="Environment.apiContextPath('api/system/menu')"
:fetch-data-url="Environment.apiContextPath('api/system/menu/allMenus')"
:tree="true"
db-click-operation="expand"
draggable="local"
dnd-mode="server"
:columns="[
{
name: 'name',
@ -36,17 +37,18 @@
{ name: 'type', label: '菜单类型' },
{ name: 'order', label: '排序号' },
]"
:toolbar-actions="['expand']"
@row-click="
(args) => {
console.info('args1=======', args);
}
"
@row-db-click="
(args) => {
console.info('args2=======', args);
}
"
:editor="{
form: {
fields: [
{
label: '名称',
name: 'name',
type: 'w-text',
},
],
},
}"
:toolbar-actions="['refresh', 'addTop', 'addChild', 'expand']"
></w-grid>
</div>
</template>

Loading…
Cancel
Save