<script lang="ts" setup>
|
import type { ActionItem, SorterResult, TableActionType } from '@jnpf/ui/vxeTable';
|
|
import { computed, nextTick, onMounted, reactive, ref, toRefs, unref } from 'vue';
|
|
import { useMessage, usePermission } from '@jnpf/hooks';
|
import { BasicForm, useForm } from '@jnpf/ui/form';
|
import { BasicVxeTable, TableAction, useVxeTable } from '@jnpf/ui/vxeTable';
|
import { getScriptFunc, thousandsFormat } from '@jnpf/utils';
|
|
import { useAccessStore } from '@vben/stores';
|
|
import { cloneDeep, omit } from 'lodash-es';
|
|
import { batchDelete, getConfigData, getModelList } from '#/api/onlineDev/visualDev';
|
import { getDataInterfaceRes } from '#/api/systemData/dataInterface';
|
import { dyOptionsList, overflowInvalidList, slotsList, systemComponentsList } from '#/components/FormGenerator/src/helper/config';
|
import { getSearchFormSchemas } from '#/components/FormGenerator/src/helper/transform';
|
import { $t } from '#/locales';
|
import { useBaseStore } from '#/store';
|
import { onlineUtils } from '#/utils/jnpf';
|
|
import Detail from '../detail/index.vue';
|
import Form from '../Form.vue';
|
|
interface State {
|
config: any;
|
columnData: any;
|
formConf: any;
|
headerBtnsList: any[];
|
columnBtnsList: any[];
|
columnOptions: any[];
|
columns: any[];
|
cacheList: any[];
|
columnSettingList: any[];
|
searchSchemas: any[];
|
customRow: any;
|
tabActiveKey: any;
|
tabList: any[];
|
allColumns: any[];
|
}
|
|
const props = defineProps(['config', 'detailFormData', 'isPreview', 'isDataManage']);
|
const emit = defineEmits(['openDetail', 'openForm']);
|
const { hasBtnP } = usePermission();
|
const accessStore = useAccessStore();
|
const { permissionList } = accessStore;
|
const { createMessage, createConfirm } = useMessage();
|
const baseStore = useBaseStore();
|
const formRef = ref<any>(null);
|
const tableRef = ref<Nullable<TableActionType>>(null);
|
const detailRef = ref<any>(null);
|
const searchInfo = reactive({
|
modelId: '',
|
menuId: '',
|
queryJson: '',
|
superQueryJson: '',
|
extraQueryJson: '',
|
});
|
const state = reactive<State>({
|
config: {},
|
columnData: {},
|
formConf: {},
|
headerBtnsList: [],
|
columnBtnsList: [],
|
columnOptions: [],
|
columns: [],
|
cacheList: [],
|
columnSettingList: [],
|
searchSchemas: [],
|
customRow: null,
|
tabActiveKey: '',
|
tabList: [],
|
allColumns: [],
|
});
|
const { columnData, allColumns } = toRefs(state);
|
const [registerSearchForm, { updateSchema, submit: searchFormSubmit }] = useForm({
|
baseColProps: { span: 6 },
|
showActionButtonGroup: true,
|
showAdvancedButton: true,
|
compact: true,
|
});
|
const [registerTable, { reload, setLoading, clearSelectedRowKeys, redoHeight }] = useVxeTable({
|
api: getModelList,
|
immediate: false,
|
tableSetting: { setting: false, redo: !props.isPreview },
|
afterFetch: (data) => {
|
state.cacheList = cloneDeep(data);
|
return data;
|
},
|
});
|
defineExpose({ reload });
|
|
const getPagination = computed(() => {
|
if ([3, 5].includes(state.columnData.type) || !state.columnData.hasPage) return false;
|
return { pageSize: state.columnData.pageSize };
|
});
|
const getChildTableStyle = computed(() => (state.columnData.type == 3 || state.columnData.type == 5 ? 1 : state.columnData.childTableStyle));
|
const getColumns = computed(() => state.columns);
|
const getSearchList = computed(() => {
|
return cloneDeep(state.searchSchemas).map((o) => ({ ...o, show: true }));
|
});
|
const getRowKey = computed(() => (state.config.webType == 4 && state.columnData.viewKey ? state.columnData.viewKey : 'id'));
|
const getTableBindValue = computed(() => {
|
// type,format 跟vxe-table 有冲突
|
const columns = unref(getColumns).map((o) => ({ ...omit(o, ['type', 'format']) }));
|
const defaultSortConfig = (state.columnData.defaultSortConfig || []).map((o) => (o.sort === 'desc' ? '-' : '') + o.field);
|
const data: any = {
|
pagination: unref(getPagination),
|
searchInfo: unref(searchInfo),
|
defSort: { sidx: defaultSortConfig.join(',') },
|
sortFn: (sortInfo: SorterResult | SorterResult[]) => {
|
if (Array.isArray(sortInfo)) {
|
const sortList = sortInfo.map((o) => (o.order === 'desc' ? '-' : '') + o.field);
|
return { sidx: sortList.join(',') };
|
} else {
|
const { field, order } = sortInfo;
|
return field && order ? { sidx: (order === 'desc' ? '-' : '') + field } : {};
|
}
|
},
|
columns,
|
clearSelectOnPageChange: true,
|
showOverflow: false,
|
bordered: unref(getChildTableStyle) != 2 || !!state.columnData.complexHeaderList?.length,
|
showFooter: state.columnData.showSummary && [1, 2, 4].includes(state.columnData.type),
|
columnConfig: {
|
resizable: !!state.columnData.resizable,
|
},
|
rowKey: unref(getRowKey),
|
};
|
// 列表合计
|
if (state.columnData.showSummary) data.footerMethod = footerMethod;
|
// 计算按钮长度
|
if (state.columnBtnsList.length) {
|
const columnBtnsLen = state.columnBtnsList.length;
|
data.actionColumn = {
|
width: columnBtnsLen * 50,
|
title: $t('component.table.action'),
|
dataIndex: 'action',
|
fixed: 'right',
|
};
|
}
|
return data;
|
});
|
const getSummaryColumn = computed(() => {
|
let defaultColumns = unref(getColumns);
|
// 处理列固定
|
if (state.columnSettingList?.length) {
|
for (const defaultColumn of defaultColumns) {
|
inner: for (let j = 0; j < state.columnSettingList.length; j++) {
|
if (defaultColumn.dataIndex === state.columnSettingList[j].dataIndex) {
|
defaultColumn.fixed = state.columnSettingList[j].fixed;
|
defaultColumn.visible = state.columnSettingList[j].visible;
|
break inner;
|
}
|
}
|
}
|
defaultColumns = defaultColumns.filter((o) => o.visible);
|
}
|
const columns: any[] = [];
|
for (const e of defaultColumns) {
|
if (e.jnpfKey === 'table' || e.jnpfKey === 'complexHeader') {
|
if (e.children?.length) columns.push(...e.children);
|
} else {
|
columns.push(e);
|
}
|
if (e.fixed && e.children?.length) {
|
for (let j = 0; j < e.children.length; j++) {
|
e.children[j].fixed = e.fixed;
|
}
|
}
|
}
|
const leftFixedList = columns.filter((o) => o.fixed === 'left');
|
const rightFixedList = columns.filter((o) => o.fixed === 'right');
|
const noFixedList = columns.filter((o) => o.fixed !== 'left' && o.fixed !== 'right');
|
return [...leftFixedList, ...noFixedList, ...rightFixedList];
|
});
|
// 列表合计
|
function footerMethod({ columns, data }) {
|
const isSummary = (key) => state.columnData.summaryField.includes(key);
|
const useThousands = (key) => unref(getSummaryColumn).some((o) => o.__vModel__ === key && o.thousands);
|
return [
|
columns.map((column, columnIndex) => {
|
if (columnIndex === 0) return '合计';
|
if (!data.length) return '';
|
let sumVal = data.reduce((sum, d) => sum + getCmpValOfRow(d, column.property), 0);
|
if (!isSummary(column.property)) sumVal = '';
|
sumVal = Number.isNaN(sumVal) ? '' : sumVal;
|
const realVal = sumVal && !Number.isInteger(sumVal) ? Number(sumVal).toFixed(2) : sumVal;
|
sumVal = useThousands(column.property) ? thousandsFormat(realVal) : realVal;
|
return sumVal;
|
}),
|
];
|
}
|
function getCmpValOfRow(row, key) {
|
const isSummary = (key) => state.columnData.summaryField.includes(key);
|
if (!state.columnData.summaryField.length || !isSummary(key)) return 0;
|
const target = row[key];
|
if (!target) return 0;
|
const data = Number.isNaN(target) ? 0 : Number(target);
|
return data;
|
}
|
function getTableActions(record, index): ActionItem[] {
|
const list = state.columnBtnsList.map((o) => {
|
const item: ActionItem = {
|
label: o.labelI18nCode ? $t(o.labelI18nCode, o.label) : o.label,
|
onClick: columnBtnsHandle.bind(null, o.value, record),
|
};
|
if (o.value === 'remove') item.color = 'error';
|
if (state.config.enableFlow) {
|
if (o.value === 'edit') item.disabled = ![0, 8, 9].includes(record.flowState);
|
if (o.value === 'remove') item.disabled = ![0, 9].includes(record.flowState);
|
if (o.value === 'detail') item.disabled = !record.flowState;
|
} else {
|
if (o?.event?.enableFunc) {
|
const parameter = { row: record, rowIndex: index, onlineUtils };
|
const func: any = getScriptFunc(o.event.enableFunc);
|
item.disabled = (func && !func(parameter)) || false;
|
}
|
}
|
return item;
|
});
|
return list;
|
}
|
function addHandle() {
|
const data = {
|
id: '',
|
formConf: transferExtraList(state.formConf),
|
modelId: searchInfo.modelId,
|
isPreview: props.isPreview,
|
useFormPermission: state.columnData.useFormPermission,
|
showMoreBtn: ![3, 5].includes(state.columnData.type),
|
menuId: searchInfo.menuId,
|
allList: state.cacheList,
|
};
|
emit('openForm', data);
|
}
|
// 顶部按钮点击事件
|
function headBtnsHandle(key) {
|
if (key === 'add') return addHandle();
|
}
|
// 行按钮点击事件
|
function columnBtnsHandle(key, record) {
|
if (key === 'edit') return updateHandle(record);
|
if (key === 'detail') return goDetail(record);
|
if (key == 'remove') handleDelete(record.id);
|
}
|
// 编辑
|
function updateHandle(record) {
|
const data = {
|
id: record.id,
|
formConf: state.formConf,
|
modelId: searchInfo.modelId,
|
isPreview: props.isPreview,
|
useFormPermission: state.columnData.useFormPermission,
|
showMoreBtn: ![3, 5].includes(state.columnData.type),
|
menuId: searchInfo.menuId,
|
allList: state.cacheList,
|
};
|
emit('openForm', data);
|
}
|
// 查看详情
|
function goDetail(record) {
|
const data = {
|
id: record.id,
|
formConf: state.formConf,
|
modelId: searchInfo.modelId,
|
menuId: searchInfo.menuId,
|
useFormPermission: state.columnData.useFormPermission,
|
title: state.config.fullName,
|
hideExtra: true,
|
};
|
emit('openDetail', data);
|
}
|
function handleDelete(id) {
|
createConfirm({
|
iconType: 'warning',
|
title: $t('common.tipTitle'),
|
content: $t('common.delTip'),
|
onOk: () => {
|
const query = { ids: [id], flowId: state.config.flowId || '' };
|
batchDelete(searchInfo.modelId, query).then((res) => {
|
createMessage.success(res.msg);
|
reload();
|
});
|
},
|
});
|
}
|
function init() {
|
state.config = {
|
modelId: searchInfo.modelId,
|
isPreview: props.isPreview,
|
...props.config.extraConfig,
|
};
|
searchInfo.modelId = state.config.id;
|
searchInfo.menuId = props.config.targetFormId;
|
const obj = {};
|
obj[props.config.targetField] = props.detailFormData[`${props.config.currentField}_jnpfId`];
|
searchInfo.extraQueryJson = JSON.stringify(obj) === '{}' ? '' : JSON.stringify(obj);
|
if (!state.config.columnData || (state.config.webType != '4' && !state.config.formData)) return;
|
state.columnData = JSON.parse(state.config.columnData);
|
state.formConf = state.config.formData ? JSON.parse(state.config.formData) : {};
|
const columnBtnsList = state.columnData.columnBtnsList || [];
|
getHeaderBtnsList(state.columnData.btnsList.filter((o) => o.value === 'add') || []);
|
getColumnBtnsList(columnBtnsList);
|
state.columnOptions = state.columnData.columnOptions || [];
|
if (!unref(getPagination)) (searchInfo as any).pageSize = 1000000;
|
setLoading(true);
|
getSearchSchemas();
|
getColumnList();
|
if (props.isPreview) return setLoading(false);
|
nextTick(() => {
|
unref(getSearchList)?.length ? searchFormSubmit() : reload({ page: 1 });
|
});
|
}
|
function getHeaderBtnsList(btnsList) {
|
btnsList = btnsList.filter((o) => o.show || !Reflect.has(o, 'show'));
|
if (props.isPreview || props.isDataManage || !state.columnData.useBtnPermission) return (state.headerBtnsList = btnsList);
|
// 过滤权限
|
const btns: any[] = [];
|
for (const element of btnsList) {
|
if (hasBtnP(`btn_${element.value}`, true, searchInfo.menuId)) btns.push(element);
|
}
|
state.headerBtnsList = btns;
|
}
|
function getColumnBtnsList(columnBtnsList) {
|
columnBtnsList = columnBtnsList.filter((o) => o.show || !Reflect.has(o, 'show'));
|
let btns: any[] = [];
|
if (props.isPreview || props.isDataManage || !state.columnData.useBtnPermission) {
|
btns = columnBtnsList;
|
} else {
|
// 过滤权限
|
const list = permissionList.find((o) => o.modelId === searchInfo.menuId);
|
const perBtnList = list && list.button ? list.button : [];
|
for (const element of columnBtnsList) {
|
inner: for (const element_ of perBtnList) {
|
if (`btn_${element.value}` === element_.enCode) {
|
btns.push(element);
|
break inner;
|
}
|
}
|
}
|
}
|
state.columnBtnsList = btns;
|
}
|
function getSearchSchemas() {
|
const schemas = getSearchFormSchemas(state.columnData.searchList);
|
schemas.forEach((cur) => {
|
const config = cur.__config__;
|
if (dyOptionsList.includes(config.jnpfKey)) {
|
if (config.dataType === 'dictionary' && config.dictionaryType) {
|
baseStore.getDicDataSelector(config.dictionaryType).then((res) => {
|
updateSchema([{ field: cur.field, componentProps: { options: res } }]);
|
});
|
}
|
if (config.dataType === 'dynamic' && config.propsUrl) {
|
const query = { paramList: config.templateJson || [] };
|
getDataInterfaceRes(config.propsUrl, query).then((res) => {
|
const data = Array.isArray(res.data) ? res.data : [];
|
updateSchema([{ field: cur.field, componentProps: { options: data } }]);
|
});
|
}
|
}
|
if ((Array.isArray(cur.value) && cur.value.length) || cur.value || cur.value === 0 || cur.value === false) cur.defaultValue = cur.value;
|
});
|
state.searchSchemas = schemas;
|
}
|
// 获取列
|
function getColumnList() {
|
let columnList: any[] = [];
|
if (props.isPreview || props.isDataManage || !state.columnData.useColumnPermission) {
|
columnList = state.columnData.columnList;
|
} else {
|
// 过滤权限
|
const list = permissionList.find((o) => o.modelId === searchInfo.menuId);
|
const perColumnList = list && list.column ? list.column : [];
|
for (let i = 0; i < state.columnData.columnList.length; i++) {
|
inner: for (const element of perColumnList) {
|
if (state.columnData.columnList[i].prop === element.enCode) {
|
columnList.push(state.columnData.columnList[i]);
|
break inner;
|
}
|
}
|
}
|
}
|
let columns = columnList.map((o) => {
|
const item: any = {
|
...o,
|
placeholder: state.columnData.type == 4 && o.placeholderI18nCode ? $t(o.placeholderI18nCode, o.placeholder) : o.placeholder,
|
title: o.labelI18nCode ? $t(o.labelI18nCode, o.label) : o.label,
|
dataIndex: o.prop,
|
align: o.align,
|
fixed: o.fixed == 'none' ? '' : o.fixed,
|
sorter: o.sortable ? { multiple: 1 } : o.sortable,
|
width: o.width || '',
|
minWidth: o.width || 100,
|
};
|
// 部分控件加插槽;
|
if ((state.columnData.type === 4 || slotsList.includes(item.jnpfKey)) && !item.dataIndex.includes('-')) {
|
item.slots = { default: item.prop };
|
}
|
item.showOverflow = !!(state.columnData.showOverflow && !overflowInvalidList.includes(item.jnpfKey) && state.columnData.type !== 4);
|
return item;
|
});
|
state.allColumns = columns;
|
if (state.columnData.type !== 3 && state.columnData.type !== 5) columns = getComplexColumns(columns);
|
state.columns = columns.filter((o) => !o.prop.includes('-'));
|
}
|
function getComplexColumns(columns) {
|
let complexHeaderList: any[] = state.columnData.complexHeaderList || [];
|
if (!complexHeaderList.length) return columns;
|
const childColumns: any[] = [];
|
const firstChildColumns: string[] = [];
|
for (const e of complexHeaderList) {
|
e.label = e.fullName;
|
e.labelI18nCode = e.fullNameI18nCode;
|
e.title = e.fullNameI18nCode ? $t(e.fullNameI18nCode, e.fullName) : e.fullName;
|
e.dataIndex = e.id;
|
e.prop = e.id;
|
e.children = [];
|
e.jnpfKey = 'complexHeader';
|
if (e.childColumns?.length) {
|
childColumns.push(...e.childColumns);
|
for (let k = 0; k < e.childColumns.length; k++) {
|
const item = e.childColumns[k];
|
for (const o of columns) {
|
if (o.prop == item && o.fixed !== 'left' && o.fixed !== 'right') e.children.push({ ...o });
|
}
|
}
|
}
|
if (e.children.length) firstChildColumns.push(e.children[0].prop);
|
}
|
complexHeaderList = complexHeaderList.filter((o) => o.children.length);
|
const list: any[] = [];
|
for (const e of columns) {
|
if (!childColumns.includes(e.prop) || e.fixed === 'left' || e.fixed === 'right') {
|
list.push(e);
|
} else {
|
if (firstChildColumns.includes(e.prop)) {
|
const item = complexHeaderList.find((o) => o.childColumns.includes(e.prop));
|
list.push(item);
|
}
|
}
|
}
|
return list;
|
}
|
// 关联表单查看详情
|
function toDetail(modelId, id, propsValue) {
|
if (!id) return;
|
getConfigData(modelId).then((res) => {
|
if (!res.data || !res.data.formData) return;
|
const formConf = JSON.parse(res.data.formData);
|
formConf.popupType = 'general';
|
formConf.customBtns = [];
|
formConf.hasPrintBtn = false;
|
const data = { id, formConf, modelId, propsValue };
|
detailRef.value?.init(data);
|
});
|
}
|
function handleColumnChange(data) {
|
state.columnSettingList = data;
|
}
|
function handleSearchSubmit(data) {
|
if (props.isPreview) return;
|
clearSelectedRowKeys();
|
const obj = {};
|
for (const [key, value] of Object.entries(data)) {
|
if (value || value === 0 || value === false) {
|
if (Array.isArray(value)) {
|
if (value.length) obj[key] = value;
|
} else {
|
obj[key] = value;
|
}
|
}
|
}
|
searchInfo.queryJson = JSON.stringify(obj) === '{}' ? '' : JSON.stringify(obj);
|
reload({ page: 1 });
|
}
|
function handleSearchReset() {
|
searchFormSubmit();
|
}
|
function transferExtraList(formConf) {
|
if (!props.config?.formOptions?.length) return formConf;
|
const loop = (list) => {
|
for (const item of list) {
|
if (item?.__config__?.children && Array.isArray(item.__config__.children)) {
|
loop(item.__config__.children);
|
}
|
if (item.__vModel__) {
|
for (let j = 0; j < props.config?.formOptions.length; j++) {
|
const element = props.config?.formOptions[j];
|
if (element.targetField == item.__vModel__) {
|
item.__config__.defaultValue = props.detailFormData[`${element.currentField}_jnpfId`];
|
item.__config__.defaultCurrent = false;
|
}
|
}
|
}
|
}
|
};
|
loop(formConf.fields);
|
return formConf;
|
}
|
|
onMounted(() => init());
|
</script>
|
<template>
|
<div class="jnpf-content-wrapper">
|
<div class="jnpf-content-wrapper-center">
|
<div class="jnpf-content-wrapper-search-box" v-if="getSearchList?.length">
|
<BasicForm
|
@register="registerSearchForm"
|
:schemas="getSearchList"
|
@advanced-change="redoHeight"
|
@submit="handleSearchSubmit"
|
@reset="handleSearchReset"
|
class="search-form" />
|
</div>
|
<div class="jnpf-content-wrapper-content bg-white">
|
<BasicVxeTable @register="registerTable" v-bind="getTableBindValue" ref="tableRef" @columns-change="handleColumnChange">
|
<template #tableTitle>
|
<a-button
|
v-for="item in state.headerBtnsList"
|
:key="item.value"
|
:type="item.value === 'add' ? 'primary' : 'link'"
|
:pre-icon="item.icon"
|
@click="headBtnsHandle(item.value)">
|
{{ item.labelI18nCode ? $t(item.labelI18nCode, item.label) : item.label }}
|
</a-button>
|
</template>
|
<template #[column.prop]="{ record }" v-for="column in allColumns" :key="column.prop">
|
<template v-if="column.jnpfKey === 'inputNumber'">
|
<jnpf-input-number v-model:value="record[column.prop]" :precision="column.precision" :thousands="column.thousands" disabled detailed />
|
</template>
|
<template v-else-if="column.jnpfKey === 'calculate'">
|
<jnpf-calculate
|
v-model:value="record[column.prop]"
|
:is-storage="column.isStorage"
|
:precision="column.precision"
|
:thousands="column.thousands"
|
:type="column.type"
|
:date-cal-config="column.dateCalConfig"
|
:round-type="column.roundType"
|
detailed />
|
</template>
|
<template v-else-if="column.jnpfKey === 'sign'">
|
<jnpf-sign v-model:value="record[column.prop]" detailed />
|
</template>
|
<template v-else-if="column.jnpfKey === 'signature'">
|
<jnpf-signature v-model:value="record[column.prop]" detailed />
|
</template>
|
<template v-else-if="column.jnpfKey === 'rate'">
|
<jnpf-rate v-model:value="record[column.prop]" :count="column.count" :allow-half="column.allowHalf" disabled />
|
</template>
|
<template v-else-if="column.jnpfKey === 'slider'">
|
<jnpf-slider v-model:value="record[column.prop]" :min="column.min" :max="column.max" :step="column.step" disabled />
|
</template>
|
<template v-else-if="column.jnpfKey === 'uploadImg'">
|
<jnpf-upload-img v-model:value="record[column.prop]" disabled detailed simple v-if="record[column.prop]?.length" />
|
</template>
|
<template v-else-if="column.jnpfKey === 'uploadFile'">
|
<jnpf-upload-file v-model:value="record[column.prop]" disabled detailed simple v-if="record[column.prop]?.length" />
|
</template>
|
<template v-else-if="column.jnpfKey === 'input'">
|
<jnpf-input
|
v-model:value="record[column.prop]"
|
:use-mask="column.useMask"
|
:mask-config="column.maskConfig"
|
:show-overflow="columnData.showOverflow"
|
detailed />
|
</template>
|
<template v-else-if="column.jnpfKey === 'relationForm'">
|
<p class="link-text" @click="toDetail(column.modelId, record[`${column.prop}_id`], column.propsValue)">
|
{{ record[column.prop] || record[column.prop] }}
|
</p>
|
</template>
|
<template v-else-if="systemComponentsList.includes(column.jnpfKey)">
|
{{ record[`${column.prop}_name`] || record[column.prop] }}
|
</template>
|
<template v-else>
|
{{ record[column.prop] }}
|
</template>
|
</template>
|
<template #action="{ record, index }">
|
<TableAction :actions="getTableActions(record, index)" />
|
</template>
|
</BasicVxeTable>
|
</div>
|
</div>
|
<Form ref="formRef" @reload="reload" />
|
<Detail ref="detailRef" />
|
</div>
|
</template>
|