Browse Source

表格优化提交

main
likunming 10 months ago
parent
commit
d96719dddf
  1. 32
      io.sc.platform.core.frontend/src/platform/components/form/WForm.vue
  2. 172
      io.sc.platform.core.frontend/src/platform/components/grid/WGrid.vue
  3. 76
      io.sc.platform.core.frontend/src/platform/components/toolbar/WToolbar.vue
  4. 2
      io.sc.platform.core.frontend/src/platform/i18n/messages.json
  5. 2
      io.sc.platform.core.frontend/src/platform/i18n/messages_tw_CN.json
  6. 2
      io.sc.platform.core.frontend/src/platform/i18n/messages_zh_CN.json

32
io.sc.platform.core.frontend/src/platform/components/form/WForm.vue

@ -1,8 +1,8 @@
<template>
<div>
<q-form ref="formRef" :autofocus="false" :greedy="true" v-bind="attrs">
<div class="grid" :class="formLayoutComputed">
<template v-for="(field, index) in fields_ as any" :key="String(index)">
<div v-if="fieldsComputed.value.length > 0" class="grid" :class="formLayoutComputed">
<template v-for="(field, index) in fieldsComputed.value as any" :key="String(index)">
<component
:is="fiedType[field.type] || field.type"
v-if="field.name"
@ -42,9 +42,9 @@
</template>
<script setup lang="ts">
import { ref, reactive, watch, computed, toRaw, defineProps, useAttrs, getCurrentInstance } from 'vue';
import { ref, reactive, watch, computed, toRaw, defineProps, useAttrs, getCurrentInstance, nextTick, onUpdated } from 'vue';
import { useQuasar } from 'quasar';
import { VueTools, Tools } from '@/platform';
import { VueTools, Tools, eventBus } from '@/platform';
import { PageStatusEnum } from '@/platform/components/utils';
const $q = useQuasar();
@ -67,6 +67,7 @@ const props = defineProps({
},
});
const localFlag = ref(false);
const formRef = ref();
const formStatus = ref(PageStatusEnum.新增);
const formModel: any = {};
@ -130,6 +131,29 @@ watch(
},
);
// eventBus.on('localeChanged', (local) => {
// nextTick(() => {
// console.info('props.fields====', props.fields);
// });
// });
const fieldsComputed = computed(() => {
localFlag.value;
return fields_;
});
onUpdated(() => {
nextTick(() => {
localFlag.value = !localFlag.value;
fields_ = ref([...props.fields]);
for (const field of fields_.value as any) {
if (field.name) {
formModel[field.name] = defaultValueHandler(field);
formFields[field.name] = field;
}
}
});
});
for (const field of fields_.value as any) {
if (field.name) {
formModel[field.name] = defaultValueHandler(field);

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

@ -6,9 +6,9 @@
v-model:pagination="state.pagination"
flat
binary-state-sort
:no-data-label="state.noDataLabel"
:no-data-label="$t('tip.noData')"
:hide-no-data="true"
:loading-label="state.loadingLabel"
:loading-label="$t('tip.dataLoading')"
v-bind="attrs"
:selection="selectionComputed"
separator="cell"
@ -27,15 +27,15 @@
<template #top="scope">
<q-resize-observer debounce="100" @resize="onResize" />
<div class="col">
<w-form ref="queryFormRef" v-bind="props.queryFormAttrs" :fields="table.queryFormFields" :cols-num="queryFormColsNum"></w-form>
<w-form ref="queryFormRef" v-bind="props.queryFormAttrs" :fields="queryFormFieldsComputed" :cols-num="queryFormColsNum"></w-form>
<div
v-if="title || buttons_.length > 0 || configButton || table.queryFormFields.length > 0"
class="flex flex-nowrap items-end"
:class="table.queryFormFields.length > 0 ? 'pt-2.5' : ''"
>
<div class="flex-none">{{ title }}</div>
<div class="flex-none">{{ $t(title) }}</div>
<div class="flex-1">
<w-toolbar :dense="denseToolbarComputed" v-bind="toolbarConfigure" :buttons="buttons_" :grid="instance"></w-toolbar>
<w-toolbar :dense="denseToolbarComputed" v-bind="toolbarConfigure" :buttons="toolbarButtonsComputed" :grid="instance"></w-toolbar>
</div>
<div v-if="configButton" class="flex-none pl-1">
<q-btn round dense :size="denseToolbarComputed ? '13px' : undefined" icon="manage_accounts" unelevated outline>
@ -74,7 +74,7 @@
</q-th>
</q-tr>
<q-tr v-if="table.rows.length === 0" :style="noDataTrHeightComputed" class="noDataTr">
<q-td :colspan="noDataTrColspanComputed" align="center" valian="middle"><q-icon size="2em" :name="IconEnum.提示" />{{ state.noDataLabel }}</q-td>
<q-td :colspan="noDataTrColspanComputed" align="center" valian="middle"><q-icon size="2em" :name="IconEnum.提示" />{{ $t('tip.noData') }}</q-td>
</q-tr>
</template>
<template v-else>
@ -99,7 +99,7 @@
</template>
</q-tr>
<q-tr v-if="table.rows.length === 0" :style="noDataTrHeightComputed" class="noDataTr">
<q-td :colspan="noDataTrColspanComputed" align="center" valian="middle"><q-icon size="2em" :name="IconEnum.提示" />{{ state.noDataLabel }}</q-td>
<q-td :colspan="noDataTrColspanComputed" align="center" valian="middle"><q-icon size="2em" :name="IconEnum.提示" />{{ $t('tip.noData') }}</q-td>
</q-tr>
</template>
</template>
@ -221,12 +221,7 @@
</div>
</template> -->
</q-table>
<w-dialog
ref="dialogRef"
v-bind="editor.dialog"
:title="dialog.dialogTitle"
:buttons="editor.dialog?.buttons ? [...editor.dialog.buttons, ...dialog.dialogButtons] : dialog.dialogButtons"
>
<w-dialog ref="dialogRef" v-bind="editor.dialog" :title="dialog.dialogTitle" :buttons="dialogButtonsComputed">
<w-form ref="dialogFormRef" v-bind="editor.form" class="pt-1.5 px-1.5"></w-form>
</w-dialog>
<w-drawer ref="drawerRef" :title="$t('action.view')" v-bind="viewer.drawer">
@ -238,9 +233,9 @@
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted, onUpdated, nextTick, toRaw, useAttrs, getCurrentInstance, provide, watchEffect, watch } from 'vue';
import { ref, reactive, computed, onMounted, nextTick, toRaw, useAttrs, getCurrentInstance, provide, watchEffect, watch, onUpdated } from 'vue';
import { useI18n } from 'vue-i18n';
import { axios, Environment, NotifyManager, TreeBuilder, VueTools, Tools } from '@/platform';
import { axios, Environment, NotifyManager, TreeBuilder, VueTools, Tools, eventBus } from '@/platform';
import { useQuasar, getCssVar, exportFile } from 'quasar';
import { IconEnum } from '@/platform/enums';
import { arrayToMap, OperatorTypeEnum, isEmpty, PageStatusEnum } from '@/platform/components/utils';
@ -427,6 +422,7 @@ const dialogFormRef = ref();
const infoRef = ref();
const tableColumns = ref(props.columns);
const tableColumnsMap = ref(arrayToMap('name', tableColumns.value));
const localFlag = ref(false);
const columnStyle = (item: any) => {
let style = '';
if (Tools.hasOwnProperty(item, 'style')) {
@ -481,6 +477,27 @@ watch(
handlerMoreRowColumnTitle();
},
);
eventBus.on('onLocaleChanged', (local) => {
nextTick(() => {
table.columns = extractTableColumns.value;
handlerQueryFormShowField();
localFlag.value = !localFlag.value;
// label
Object.keys(buttonObj).forEach((btn) => {
if (typeof buttonObj[btn] === 'object' && buttonObj[btn].labelI18nKey) {
buttonObj[btn].label = t(buttonObj[btn].labelI18nKey);
}
});
handleToolbarActions();
// label
dialog.dialogButtons[0].label = t(dialog.dialogButtons[0].labelI18nKey);
});
});
// onUpdated(() => {
// nextTick(() => {
// console.info('onupdated.buttonObj', buttonObj);
// });
// });
const queryFormFieldsMap = arrayToMap('name', props.queryFormFields);
const rowKey_ = '_rowKey_'; // UUID
const url = {
@ -502,6 +519,15 @@ const titleScopeHandler = (column: any, scope: any) => {
return undefined;
};
const queryFormFieldsComputed = computed(() => {
localFlag.value;
return table.queryFormFields;
});
const toolbarButtonsComputed = computed(() => {
localFlag.value;
return buttons_;
});
const table = reactive({
tickedField: props.tickedField,
selectedField: props.selectedField,
@ -525,37 +551,13 @@ const table = reactive({
});
provide('table', table);
const expandIcon = computed(() => {
const expandIconComputed = computed(() => {
return table.treeExpand ? 'expand_less' : 'expand_more';
});
const expandLabel = computed(() => {
return table.treeExpand ? '全部收起' : '全部展开';
const expandLabelI18nKeyComputed = computed(() => {
return table.treeExpand ? 'action.expandUp' : 'action.expandDown';
});
/**
* 内置按钮枚举
*/
enum ButtonEnum {
query = 'query', //
moreQuery = 'moreQuery', //
reset = 'reset', //
refresh = 'refresh', //
add = 'add', //
edit = 'edit', //
clone = 'clone', //
remove = 'remove', //
removeAll = 'removeAll', //
view = 'view', //
export = 'export', //
addTop = 'addTop', //
addChild = 'addChild', //
expand = 'expand', // ?
resetDefaultValues = 'resetDefaultValues', //
}
/**
* 内置按钮
*/
const remove = () => {
const ids = <any>[];
if (getTickedRowsComputed.value && getTickedRowsComputed.value.length > 0) {
@ -597,19 +599,21 @@ const resetDefaultValues = () => {
NotifyManager.error(t('tip.operationFailed'));
});
};
const buttonObj = {
const buttonObj = reactive({
separator: 'separator',
query: {
name: ButtonEnum.query,
name: 'query',
icon: IconEnum.查询,
labelI18nKey: 'action.query',
label: t('action.query'),
click: () => {
refresh();
},
},
moreQuery: {
name: ButtonEnum.moreQuery,
name: 'moreQuery',
icon: IconEnum.更多查询,
labelI18nKey: 'action.moreQueryConditions',
label: t('action.moreQueryConditions'),
enableIf: () => {
if (props.queryFormFields.length <= table.queryFormFields.length && !table.moreQueryStatus) {
@ -624,24 +628,27 @@ const buttonObj = {
},
},
reset: {
name: ButtonEnum.reset,
name: 'reset',
icon: IconEnum.重置,
labelI18nKey: 'action.reset',
label: t('action.reset'),
click: () => {
queryFormRef.value.reset();
},
},
refresh: {
name: ButtonEnum.refresh,
name: 'refresh',
icon: IconEnum.刷新,
labelI18nKey: 'action.refresh',
label: t('action.refresh'),
click: () => {
refresh();
},
},
add: {
name: ButtonEnum.add,
name: 'add',
icon: IconEnum.新增,
labelI18nKey: 'action.addNew',
label: t('action.addNew'),
click: () => {
dialog.dialogTitle = t('action.addNew');
@ -653,8 +660,9 @@ const buttonObj = {
},
},
edit: {
name: ButtonEnum.edit,
name: 'edit',
icon: IconEnum.编辑,
labelI18nKey: 'action.edit',
label: t('action.edit'),
enableIf: (args) => {
if (args.selected) {
@ -677,8 +685,9 @@ const buttonObj = {
},
},
clone: {
name: ButtonEnum.clone,
name: 'clone',
icon: 'content_copy',
labelI18nKey: 'action.copy',
label: t('action.copy'),
enableIf: (args) => {
if (args.selected) {
@ -700,8 +709,9 @@ const buttonObj = {
},
},
remove: {
name: ButtonEnum.remove,
name: 'remove',
icon: IconEnum.删除,
labelI18nKey: 'action.remove',
label: t('action.remove'),
enableIf: (args) => {
if (args.ticked) {
@ -727,8 +737,9 @@ const buttonObj = {
},
},
view: {
name: ButtonEnum.view,
name: 'view',
icon: IconEnum.查看,
labelI18nKey: 'action.view',
label: t('action.view'),
enableIf: (args) => {
if (args.selected) {
@ -741,8 +752,9 @@ const buttonObj = {
},
},
export: {
name: ButtonEnum.export,
name: 'export',
icon: 'file_download',
labelI18nKey: 'action.export',
label: t('action.export'),
click: () => {
const content = [tableColumns.value.map((col) => wrapCsvValue(col.label))]
@ -767,8 +779,9 @@ const buttonObj = {
},
},
addTop: {
name: ButtonEnum.addTop,
name: 'addTop',
icon: IconEnum.新增,
labelI18nKey: 'action.addTop',
label: t('action.addTop'),
click: () => {
dialog.dialogTitle = t('action.addTop');
@ -780,8 +793,9 @@ const buttonObj = {
},
},
addChild: {
name: ButtonEnum.addChild,
name: 'addChild',
icon: 'playlist_add',
labelI18nKey: 'action.addChild',
label: t('action.addChild'),
enableIf: (args) => {
if (args.selected) {
@ -799,17 +813,19 @@ const buttonObj = {
},
},
expand: {
name: ButtonEnum.expand,
icon: expandIcon,
label: expandLabel,
name: 'expand',
icon: expandIconComputed,
labelI18nKey: expandLabelI18nKeyComputed,
label: t(expandLabelI18nKeyComputed.value),
click: () => {
expandFun(table.rows, table.treeExpand);
table.treeExpand = !table.treeExpand;
},
},
resetDefaultValues: {
name: ButtonEnum.resetDefaultValues,
name: 'resetDefaultValues',
icon: 'bi-copy',
labelI18nKey: 'action.resetDefaultValues',
label: t('action.resetDefaultValues'),
click: (tips: boolean = true) => {
if (!tips) {
@ -826,7 +842,7 @@ const buttonObj = {
}
},
},
};
});
const expandFun = (arr, treeExpand) => {
arr.forEach((item) => {
if (props.tree && item.children && item.children.length > 0) {
@ -857,18 +873,22 @@ const handleChildrenBtn = (arr) => {
}
return tempArr;
};
props.toolbarActions.forEach((btn: any, index) => {
if (typeof btn === 'string' && buttonObj[btn]) {
buttons_.push(buttonObj[btn]);
} else if (Array.isArray(btn) && btn.length > 0) {
buttons_.push(handleChildrenBtn(btn));
} else if (typeof btn === 'object' && btn.extend && buttonObj[btn.extend]) {
//
buttons_.push({ ...buttonObj[btn.extend], ...btn, _click: buttonObj[btn.extend].click });
} else {
buttons_.push(btn);
}
});
const handleToolbarActions = () => {
buttons_.splice(0, buttons_.length);
props.toolbarActions.forEach((btn: any, index) => {
if (typeof btn === 'string' && buttonObj[btn]) {
buttons_.push(buttonObj[btn]);
} else if (Array.isArray(btn) && btn.length > 0) {
buttons_.push(handleChildrenBtn(btn));
} else if (typeof btn === 'object' && btn.extend && buttonObj[btn.extend]) {
//
buttons_.push({ ...buttonObj[btn.extend], ...btn, _click: buttonObj[btn.extend].click });
} else {
buttons_.push(btn);
}
});
};
handleToolbarActions();
const denseToolbarComputed = computed(() => {
if (table.denseToolbar) {
@ -988,6 +1008,7 @@ const dialog = reactive({
dialogButtons: [
{
icon: IconEnum.提交,
labelI18nKey: 'action.submit',
label: t('action.submit'),
loading: false,
click: () => {
@ -996,6 +1017,12 @@ const dialog = reactive({
},
],
});
const dialogButtonsComputed = computed(() => {
if (props.editor?.dialog?.buttons) {
return [...props.editor.dialog.buttons, ...dialog.dialogButtons];
}
return dialog.dialogButtons;
});
const viewInfo = reactive({
infoArray: <any>[],
@ -1792,7 +1819,6 @@ const stickyHeaderColumn = (time = 500) => {
}
tableRef.value.$el.getElementsByTagName('table')[0].style.setProperty('--tableBodyPadding', bodyPadding);
tableRef.value.$el.getElementsByTagName('table')[0].style.setProperty('--tableBodyHeight', (denseBodyComputed.value ? 24 : 48) + 'px');
if (denseBottomComputed.value && (!Tools.hasOwnProperty(attrs, 'hide-bottom') || attrs['hide-bottom'] === false)) {
if (tableRef.value.$el.getElementsByClassName('q-table__bottom').length > 0) {
tableRef.value.$el.getElementsByClassName('q-table__bottom')[0].style.setProperty('--tableBottomHeight', 33 + 'px');

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

@ -5,7 +5,7 @@
:class="'flex ' + (align === 'left' ? 'justify-start' : align === 'center' ? 'justify-center' : 'justify-end') + ' gap-x-[' + props.xGap + 'px]'"
>
<!-- buttons -->
<template v-for="(btn, index) in baseActions" :key="'button_' + index">
<template v-for="(btn, index) in baseActionsComputed" :key="'button_' + index">
<q-separator v-if="typeof btn.data === 'string' && btn.data === 'separator'" vertical class="class-action-item" />
<q-btn-dropdown
v-else-if="Array.isArray(btn.data) && btn.data.length > 0"
@ -106,7 +106,7 @@
<!-- moreActions -->
<q-btn-dropdown
v-if="moreActions && moreActions.length > 0"
v-if="moreActionsComputed && moreActionsComputed.length > 0"
unelevated
outline
label=""
@ -121,7 +121,7 @@
</div>
</template>
<q-list>
<template v-for="(childrenBtn, childrenIndex) in moreActions" :key="'moreAction_' + childrenIndex">
<template v-for="(childrenBtn, childrenIndex) in moreActionsComputed" :key="'moreAction_' + childrenIndex">
<q-separator v-if="typeof childrenBtn.data === 'string' && childrenBtn.data === 'separator'" />
<ChildrenBtn
v-else-if="Array.isArray(childrenBtn.data) && childrenBtn.data.length > 0"
@ -161,7 +161,8 @@
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed, nextTick } from 'vue';
import { ref, reactive, computed, nextTick, onUpdated } from 'vue';
import { eventBus } from '@/platform';
import { Tools } from '@/platform/utils';
import ChildrenBtn from './ChildrenBtn.vue';
@ -185,6 +186,7 @@ const props = defineProps({
},
});
const containerRef = ref();
const localFlag = ref(false);
// name JSON
const buttonsJson = {};
@ -263,24 +265,29 @@ const handleChildrenSeparator = (arr) => {
}
return tempArr;
};
for (let i = 0; i < props.buttons.length; i++) {
const btn: any = props.buttons[i];
if (i === 0 && typeof btn === 'string' && btn === 'separator') {
//
continue;
}
if (typeof btn === 'string' && btn === 'separator' && buttons_.length > 0 && buttons_[buttons_.length - 1].data === btn) {
//
continue;
}
if (Array.isArray(btn) && btn.length > 0) {
buttons_.push({ data: handleChildrenSeparator(btn) });
} else if (typeof btn === 'string' && btn === 'separator') {
buttons_.push({ data: btn });
} else {
buttons_.push({ data: buttonsJson[btn.name] });
const handleButtons = () => {
buttons_.splice(0, buttons_.length);
for (let i = 0; i < props.buttons.length; i++) {
const btn: any = props.buttons[i];
if (i === 0 && typeof btn === 'string' && btn === 'separator') {
//
continue;
}
if (typeof btn === 'string' && btn === 'separator' && buttons_.length > 0 && buttons_[buttons_.length - 1].data === btn) {
//
continue;
}
if (Array.isArray(btn) && btn.length > 0) {
buttons_.push({ data: handleChildrenSeparator(btn) });
} else if (typeof btn === 'string' && btn === 'separator') {
buttons_.push({ data: btn });
} else {
buttons_.push({ data: buttonsJson[btn.name] });
}
}
}
};
handleButtons();
const baseActions = ref(buttons_);
const moreActions: any = ref([]);
const isActionWidthInitializedRef = ref(false);
@ -289,7 +296,6 @@ const onResize = (size) => {
if (Tools.isUndefinedOrNull(containerRef.value)) {
return;
}
if (!isActionWidthInitializedRef.value) {
const nodes = containerRef.value.getElementsByClassName('class-action-item');
for (let i = 0; i < buttons_.length; i++) {
@ -304,7 +310,6 @@ const onResize = (size) => {
let availableWidth = size.width;
let width = 0;
let index = 0;
for (; index < length; index++) {
if (width + buttons_[index].width > availableWidth) {
availableWidth -= moreActionWidth;
@ -331,8 +336,33 @@ const onResize = (size) => {
baseActions.value = _baseActions;
moreActions.value = _moreActions;
localFlag.value = !localFlag.value;
};
const baseActionsComputed = computed(() => {
localFlag.value;
return baseActions.value;
});
const moreActionsComputed = computed(() => {
localFlag.value;
return moreActions.value;
});
// onUpdated(() => {
// nextTick(() => {
// console.info('buttons=====', props.buttons);
// });
// });
eventBus.on('onLocaleChanged', () => {
nextTick(() => {
extractButton(props.buttons);
handleButtons();
baseActions.value = buttons_;
localFlag.value = !localFlag.value;
isActionWidthInitializedRef.value = false;
});
});
const loadingComputed = computed(() => {
return function (btn) {
if (btn.loadingIf && typeof btn.loadingIf === 'function' && btn.loadingIf(selectedComputed.value, tickedComputed.value, props.grid)) {

2
io.sc.platform.core.frontend/src/platform/i18n/messages.json

@ -141,6 +141,8 @@
"action.export": "Export",
"action.export.failed": "Export Failed",
"action.submit": "Submit",
"action.expandUp": "Expand Up",
"action.expandDown": "Expand Down",
"tip.noData": "No Data",
"tip.dataLoading": "Data Loading",

2
io.sc.platform.core.frontend/src/platform/i18n/messages_tw_CN.json

@ -141,6 +141,8 @@
"action.export": "導出",
"action.export.failed": "導出失敗",
"action.submit": "提交",
"action.expandUp": "全部收起",
"action.expandDown": "全部展开",
"tip.noData": "未查找到任何數據",
"tip.dataLoading": "數據加載中",

2
io.sc.platform.core.frontend/src/platform/i18n/messages_zh_CN.json

@ -141,6 +141,8 @@
"action.export": "导出",
"action.export.failed": "导出失败",
"action.submit": "提交",
"action.expandUp": "全部收起",
"action.expandDown": "全部展开",
"tip.noData": "未查找到任何数据",
"tip.dataLoading": "数据加载中",

Loading…
Cancel
Save