feat: 明细表中的组织架构选择器支持将文本表示存储在独立字段,以改善显示性能

This commit is contained in:
gaoyunqi
2024-05-16 15:49:49 +08:00
parent 2e07279924
commit 92fd90f35d
5 changed files with 249 additions and 208 deletions

View File

@ -422,6 +422,13 @@
</template> </template>
</a-input> </a-input>
</a-form-item> </a-form-item>
<template v-if="data.isSubFormChild">
<a-form-item v-if="hasKey('sepTextField')" label="文本存储字段">
<a-select v-model:value="data.options.sepTextField" :allow-clear="true" :placeholder="t('请选择文本字段')" size="mini">
<a-select-option v-for="fItem in findTableFields(data)" :key="fItem.key" :value="fItem.bindField">{{ fItem.label }}</a-select-option>
</a-select>
</a-form-item>
</template>
<!-- 响应式布局只对主表字段生效 --> <!-- 响应式布局只对主表字段生效 -->
<template v-if="!data.isSubFormChild"> <template v-if="!data.isSubFormChild">
<a-form-item v-if="hasKey('responsive')" label="响应式"> <a-form-item v-if="hasKey('responsive')" label="响应式">
@ -1560,6 +1567,18 @@
}); });
} }
function findTableFields(obj) {
if (!props.widgetForm?.list) {
return [];
}
const bTable = obj.bindTable;
const pTable = props.widgetForm.list.find((item) => item.type === 'form' && item.bindTable === bTable);
if (!pTable) {
return [];
}
return pTable.children;
}
return { return {
data, data,
hasKey, hasKey,
@ -1617,7 +1636,8 @@
handleIsSave, handleIsSave,
buttonTableOptions, buttonTableOptions,
t, t,
imageUrl imageUrl,
findTableFields
}; };
} }
}); });

View File

@ -1164,6 +1164,7 @@ export const infoComponents = [
labelFixWidth: 120, labelFixWidth: 120,
parentNode: '', parentNode: '',
responsive: false, responsive: false,
sepTextField: '',
span: '', span: '',
width: '100%', width: '100%',
defaultValue: undefined, defaultValue: undefined,
@ -1184,6 +1185,7 @@ export const infoComponents = [
labelWidthMode: 'fix', labelWidthMode: 'fix',
labelFixWidth: 120, labelFixWidth: 120,
responsive: false, responsive: false,
sepTextField: '',
span: '', span: '',
width: '100%', width: '100%',
defaultValue: '', defaultValue: '',

View File

@ -1,7 +1,6 @@
<template> <template>
<div style="width: 100%" @click="show" class="depart-select"> <div class="depart-select" style="width: 100%" @click="show">
<a-input v-model:value="departNames" :bordered="bordered" :disabled="disabled" :placeholder="placeholder" <a-input v-model:value="departNames" :bordered="bordered" :disabled="disabled" :placeholder="placeholder" :size="size" readonly>
:size="size" readonly>
<template v-if="prefix" #prefix> <template v-if="prefix" #prefix>
<Icon :icon="prefix" /> <Icon :icon="prefix" />
</template> </template>
@ -9,37 +8,39 @@
<Icon :icon="suffix" /> <Icon :icon="suffix" />
</template> </template>
</a-input> </a-input>
<ModalPanel :visible="visible" :width="800" :title="t('选择部门')" @submit="submit" @close="close" class="depart-select-dialog"> <ModalPanel :title="t('选择部门')" :visible="visible" :width="800" class="depart-select-dialog" @close="close" @submit="submit">
<div class="choose-dep-box"> <div class="choose-dep-box">
<div class="choose-dep"> <div class="choose-dep">
<a-spin class="loading-box" :spinning="loading" /> <a-spin :spinning="loading" class="loading-box" />
<SelectDepartmentTreeV2 v-if="visible" @changeValue="departChange" :value="props.value" <SelectDepartmentTreeV2 v-if="visible" :parentNode="parentNode" :value="props.value" @changeValue="departChange" @queryCompleted="queryCompleted"></SelectDepartmentTreeV2>
@queryCompleted="queryCompleted" :parentNode="parentNode"></SelectDepartmentTreeV2>
</div> </div>
<div class="choosen-dep"> <div class="choosen-dep">
<div class="choosen-item" v-for="item in selectedNodes"> <div v-for="item in selectedNodes" class="choosen-item">
<div class="choosen-label">{{ item.name }}</div> <div class="choosen-label">{{ item.name }}</div>
<close-outlined class="close" @click="deleteItem" /> <close-outlined class="close" @click="deleteItem" />
</div> </div>
</div> </div>
</div> </div>
</ModalPanel> </ModalPanel>
</div> </div>
</template> </template>
<script lang="ts" setup> <script setup>
import { onMounted, ref, watch } from 'vue'; import { onMounted, ref, watch } from 'vue';
import SelectDepartmentTreeV2 from './SelectDepartmentTreeV2.vue' import SelectDepartmentTreeV2 from './SelectDepartmentTreeV2.vue';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { ModalPanel } from '/@/components/ModalPanel/index'; import { ModalPanel } from '/@/components/ModalPanel/index';
const { t } = useI18n();
const emits = defineEmits(['change', 'changeNames', 'close', 'options-change', 'update:value']);
import { getDepartmentTrees } from '/@/api/system/department';
import { CloseOutlined } from '@ant-design/icons-vue';
const visible = ref(false)
const departNames = ref<string>();
const props = defineProps({ const { t } = useI18n();
const emits = defineEmits(['change', 'changeNames', 'close', 'options-change', 'update:value']);
import { getDepartmentTrees } from '/@/api/system/department';
import { CloseOutlined } from '@ant-design/icons-vue';
import { camelCaseString } from '/@/utils/event/design';
const visible = ref(false);
const departNames = ref();
const valChanged = ref(false);
const props = defineProps({
value: String, value: String,
prefix: String, prefix: String,
suffix: String, suffix: String,
@ -59,43 +60,52 @@ const props = defineProps({
componentProps: { componentProps: {
type: Object, type: Object,
default: {} default: {}
} },
}); sepTextField: '', // 将文字描述存在独立字段,增加首次渲染速度
const selectedNodes = ref([]) row: Object
const loading = ref(true) });
const selectedNodes = ref([]);
const loading = ref(true);
// Embedded in the form, just use the hook binding to perform form verification // Embedded in the form, just use the hook binding to perform form verification
onMounted(() => { onMounted(() => {});
});
watch( watch(
() => props.value, () => props.value,
(val: any) => { (val) => {
if (val) { if (val) {
getDefaultList(val) if (props.sepTextField && !valChanged.value) {
departNames.value = props.row[camelCaseString(props.sepTextField, true)];
} else {
getDefaultList(val);
}
} }
}, },
{ {
immediate: true, immediate: true
}, }
); );
async function getDefaultList(id) {
async function getDefaultList(id) {
let param = { let param = {
id: id, id: id,
code: '', code: '',
name: '', name: '',
parentNode: false parentNode: false
} };
let list = resetTreeList(await getDepartmentTrees(param)) let list = resetTreeList(await getDepartmentTrees(param));
selectedNodes.value = list selectedNodes.value = list;
let nameList = selectedNodes.value.map(item => item.name) let nameList = selectedNodes.value.map((item) => item.name);
const names = nameList.join(',') const names = nameList.join(',');
departNames.value = names; departNames.value = names;
// handleSelectedNodes() if (props.sepTextField) {
} props.row[camelCaseString(props.sepTextField, true)] = names;
function resetTreeList(list) { }
const result = list.map(item => { }
function resetTreeList(list) {
const result = list.map((item) => {
return { return {
...item, ...item,
...{ ...{
@ -103,53 +113,64 @@ function resetTreeList(list) {
children: resetTreeList(item.children), children: resetTreeList(item.children),
title: item.name title: item.name
} }
};
});
return result;
} }
})
return result function queryCompleted() {
} loading.value = false;
function queryCompleted() { }
loading.value = false
} function departChange(nodes) {
function departChange(nodes) { selectedNodes.value = nodes;
selectedNodes.value = nodes valChanged.value = true;
} }
function show() {
visible.value = true function show() {
loading.value = true visible.value = true;
loading.value = true;
if (props.value) { if (props.value) {
getDefaultList(props.value) getDefaultList(props.value);
} }
} }
function handleSelectedNodes() {
let nameList = selectedNodes.value.map(item => item.name) function handleSelectedNodes() {
let idList = selectedNodes.value.map(item => item.id) let nameList = selectedNodes.value.map((item) => item.name);
const names = nameList.join(',') let idList = selectedNodes.value.map((item) => item.id);
const ids = idList.join(',') const names = nameList.join(',');
const ids = idList.join(',');
emits('update:value', ids); emits('update:value', ids);
departNames.value = names; departNames.value = names;
} if (props.sepTextField) {
function deleteItem() { props.row[camelCaseString(props.sepTextField, true)] = names;
selectedNodes.value = [] }
} }
function submit() {
handleSelectedNodes() function deleteItem() {
selectedNodes.value = [];
}
function submit() {
handleSelectedNodes();
close(); close();
} }
function close() {
function close() {
visible.value = false; visible.value = false;
selectedNodes.value = [] selectedNodes.value = [];
emits('close'); emits('close');
} }
</script> </script>
<style lang="less"> <style lang="less">
.depart-select-dialog { .depart-select-dialog {
.content { .content {
margin-bottom: 0; margin-bottom: 0;
} }
} }
</style> </style>
<style lang="less" scoped> <style lang="less" scoped>
.choose-dep-box { .choose-dep-box {
display: flex; display: flex;
height: 95%; height: 95%;
@ -189,7 +210,7 @@ function close() {
} }
.close { .close {
visibility: hidden visibility: hidden;
} }
} }
@ -197,9 +218,9 @@ function close() {
background-color: #eaeaea; background-color: #eaeaea;
.close { .close {
visibility: visible visibility: visible;
}
} }
} }
} }
}
</style> </style>

View File

@ -9,7 +9,7 @@
{{ t('新增') }} {{ t('新增') }}
</a-button> </a-button>
</div> </div>
<a-table :bordered="showFormBorder" :columns="headColums.length > 0 ? headColums : columns" :data-source="data" :pagination="showPagination ? { defaultPageSize: 10 } : false" :scroll="{ x: 'max-content' }"> <a-table :bordered="showFormBorder" :columns="headColums.length > 0 ? headColums : columns" :data-source="addDataKey(data)" :pagination="showPagination ? { defaultPageSize: 10 } : false" :scroll="{ x: 'max-content' }">
<template #summary> <template #summary>
<a-table-summary-row v-if="columns.some((x) => x.componentProps?.subTotal)"> <a-table-summary-row v-if="columns.some((x) => x.componentProps?.subTotal)">
<a-table-summary-cell v-for="(column, idx) in columns" :key="idx"> <a-table-summary-cell v-for="(column, idx) in columns" :key="idx">
@ -66,6 +66,8 @@
:bordered="showComponentBorder" :bordered="showComponentBorder"
:index="index" :index="index"
:mainKey="mainKey" :mainKey="mainKey"
:key="column.dataIndex + record['_key_']"
:row="record"
v-bind="getComponentsProps(column.componentProps, column.dataIndex, record, index)" v-bind="getComponentsProps(column.componentProps, column.dataIndex, record, index)"
@blur="onFieldBlur(column, record, index)" @blur="onFieldBlur(column, record, index)"
@change="onFieldChange(column, record, index)" @change="onFieldChange(column, record, index)"
@ -225,6 +227,15 @@
}); });
} }
function addDataKey(rows) {
rows.forEach((row) => {
if (!row['_key_']) {
row['_key_'] = Math.random();
}
});
return rows;
}
onMounted(() => { onMounted(() => {
data.value = cloneDeep(props.value); data.value = cloneDeep(props.value);
@ -261,23 +272,7 @@
watch( watch(
() => props.value, () => props.value,
(v) => { (v) => {
// console.log('watch props.value', props.value);
data.value = v; data.value = v;
// const rangeComponents = props.columns.filter((column) =>
// column.componentType?.includes('Range'),
// );
// if (rangeComponents.length && formModel) {
// rangeComponents.forEach((item) => {
// data.value.forEach((x) => {
// if (x[(item.dataIndex as string)?.split(',')[0]]) {
// x[item.dataIndex as string] = [
// x[(item.dataIndex as string)?.split(',')[0]],
// x[(item.dataIndex as string)?.split(',')[1]],
// ];
// }
// });
// });
// }
//要保证在预加载之后在emit 不然预加载数据不会绑定到表单数据中 //要保证在预加载之后在emit 不然预加载数据不会绑定到表单数据中
emit('change', unref(data)); emit('change', unref(data));

View File

@ -27,8 +27,11 @@ export function changeToPinyin(label: string, isUpper?: boolean) {
/* 如果没有下划线,不需要处理 /* 如果没有下划线,不需要处理
如果有下划线,用下划线切割,第一个下划线左边的全部小写,后面的首字母大写,首字母后面的全部小写 */ 如果有下划线,用下划线切割,第一个下划线左边的全部小写,后面的首字母大写,首字母后面的全部小写 */
export function camelCaseString(string: string) { export function camelCaseString(string: string, skipNoUnderline = false) {
if (!string) return; if (!string) return;
if (skipNoUnderline && string.indexOf('_') < 0) {
return string;
}
const stringLower = string.toLowerCase(); const stringLower = string.toLowerCase();
const len = stringLower.length; const len = stringLower.length;
let str = ''; let str = '';