<script lang="ts" setup>
|
import type { ActionItem, BasicColumn } from '@jnpf/ui/vxeTable';
|
|
import type { FieldListItem, FieldPageQuery } from '#/api/x/eln/fields';
|
|
import { ref } from 'vue';
|
|
import { useMessage } from '@jnpf/hooks';
|
import { useDrawer } from '@jnpf/ui/drawer';
|
import { BasicForm, useForm } from '@jnpf/ui/form';
|
import { BasicModal, useModal } from '@jnpf/ui/modal';
|
import { BasicVxeTable, TableAction, useVxeTable } from '@jnpf/ui/vxeTable';
|
|
import { DatabaseOutlined } from '@ant-design/icons-vue';
|
|
import { disableField, enableField, getFieldPage, promoteFieldToGlobal } from '#/api/x/eln/fields';
|
import { onlineUtils } from '#/utils/jnpf';
|
|
import FieldForm from './FieldForm.vue';
|
import { fieldActions } from './fieldRules';
|
import VersionDrawer from './VersionDrawer.vue';
|
|
defineOptions({ name: 'ElnFieldCatalog' });
|
|
type FieldChange = 'disable' | 'enable' | 'promoteGlobal';
|
|
const { createMessage } = useMessage();
|
const [registerFieldForm, { openModal: openFieldForm }] = useModal();
|
const [registerReasonModal, { closeModal: closeReasonModal, openModal: openReasonModal }] = useModal();
|
const [registerVersionDrawer, { openDrawer: openVersionDrawer }] = useDrawer();
|
|
const reason = ref('');
|
const pendingChange = ref<{ field: FieldListItem; type: FieldChange }>();
|
|
const columns: BasicColumn[] = [
|
{ dataIndex: 'ziduanBianma', minWidth: 210, title: '字段编码', slots: { default: 'ziduanBianma' } },
|
{ dataIndex: 'ziduanMingcheng', minWidth: 150, title: '字段名称' },
|
{ dataIndex: 'zuoyongFanwei', title: '作用域', width: 100, slots: { default: 'zuoyongFanwei' } },
|
{ dataIndex: 'mubanBianhao', minWidth: 140, title: '归属模板', slots: { default: 'mubanBianhao' } },
|
{ dataIndex: 'dangqianBanbenHao', title: '当前版本', width: 100, slots: { default: 'dangqianBanbenHao' } },
|
{ dataIndex: 'zuixinFabuBanbenHao', title: '最新发布', width: 100, slots: { default: 'zuixinFabuBanbenHao' } },
|
{ dataIndex: 'bizEnabled', title: '启用状态', width: 100, slots: { default: 'bizEnabled' } },
|
{ align: 'right', dataIndex: 'mubanYinyongShu', title: '模板引用数', width: 110 },
|
{ dataIndex: 'gengxinShijian', format: 'date|YYYY-MM-DD HH:mm:ss', title: '更新时间', width: 165 },
|
];
|
|
const [registerSearchForm, { getFieldsValue, resetFields }] = useForm({
|
baseColProps: { lg: 8, md: 12, sm: 12, xl: 6, xs: 24 },
|
compact: true,
|
labelWidth: 76,
|
schemas: [
|
{
|
component: 'Input',
|
componentProps: { placeholder: '字段编码或名称', submitOnPressEnter: true },
|
field: 'keyword',
|
label: '关键字',
|
},
|
{
|
component: 'Select',
|
componentProps: {
|
allowClear: true,
|
options: [
|
{ fullName: '全局字段', id: 'GLOBAL' },
|
{ fullName: '模板私有字段', id: 'TEMPLATE' },
|
],
|
placeholder: '全部',
|
},
|
field: 'zuoyongFanwei',
|
label: '作用域',
|
},
|
{
|
component: 'Input',
|
componentProps: { allowClear: true, placeholder: '请输入模板编号', submitOnPressEnter: true },
|
field: 'mubanBianhao',
|
label: '归属模板',
|
},
|
{
|
component: 'Select',
|
componentProps: {
|
allowClear: true,
|
options: [
|
{ fullName: '启用', id: 'enabled' },
|
{ fullName: '停用', id: 'disabled' },
|
],
|
placeholder: '全部',
|
},
|
field: 'bizEnabled',
|
label: '状态',
|
},
|
],
|
showActionButtonGroup: true,
|
showAdvancedButton: false,
|
});
|
|
const [registerTable, { reload }] = useVxeTable({
|
actionColumn: { dataIndex: 'action', fixed: 'right', title: '操作', width: 220 },
|
api: fetchFieldPage,
|
beforeFetch: normalizeQuery,
|
columns,
|
immediate: true,
|
pagination: {
|
pageSize: 20,
|
pageSizeOptions: ['20', '50', '100'],
|
showQuickJumper: true,
|
showSizeChanger: true,
|
showTotal: (total) => `共 ${total} 个字段`,
|
},
|
rowKey: 'id',
|
showIndexColumn: false,
|
});
|
|
function normalizeQuery(raw: Record<string, unknown>): FieldPageQuery {
|
const query: Record<string, unknown> = { ...raw };
|
for (const key of ['keyword', 'mubanBianhao']) {
|
if (typeof query[key] === 'string') query[key] = query[key].trim();
|
if (!query[key]) delete query[key];
|
}
|
for (const key of ['bizEnabled', 'zuoyongFanwei']) {
|
if (!query[key]) delete query[key];
|
}
|
query.currentPage = Math.max(1, Number(query.currentPage) || 1);
|
query.pageSize = Math.max(1, Number(query.pageSize) || 20);
|
return query as unknown as FieldPageQuery;
|
}
|
|
async function fetchFieldPage(query: FieldPageQuery) {
|
const response = await getFieldPage(query);
|
return { data: response.data };
|
}
|
|
function handleSearch() {
|
reload({ page: 1, searchInfo: getFieldsValue() });
|
}
|
|
function openLimsSources() {
|
onlineUtils.route('/eln/sources', { title: 'LIMS 数据项' });
|
}
|
|
async function handleReset() {
|
await resetFields();
|
reload({ page: 1, searchInfo: {} });
|
}
|
|
function getTableActions(field: FieldListItem): ActionItem[] {
|
const actions = fieldActions({ enabled: field.bizEnabled === 'enabled', scope: field.zuoyongFanwei });
|
return actions.map((action): ActionItem => {
|
if (action === 'versions') {
|
return { label: '详情', onClick: () => openVersionDrawer(true, { fieldId: field.id }) };
|
}
|
if (action === 'promoteGlobal') {
|
return {
|
label: '升级为全局',
|
onClick: () => requestFieldChange(field, action),
|
};
|
}
|
if (action === 'enable') {
|
return { color: 'success', label: '启用', onClick: () => requestFieldChange(field, action) };
|
}
|
return { color: 'error', label: '停用', onClick: () => requestFieldChange(field, action) };
|
});
|
}
|
|
function requestFieldChange(field: FieldListItem, type: FieldChange) {
|
reason.value = '';
|
pendingChange.value = { field, type };
|
openReasonModal(true);
|
}
|
|
async function confirmFieldChange() {
|
const change = pendingChange.value;
|
const actualReason = reason.value.trim();
|
if (!change || !actualReason) {
|
createMessage.warning('请输入操作原因');
|
return;
|
}
|
if (change.type === 'promoteGlobal') await promoteFieldToGlobal(change.field.id, actualReason);
|
else if (change.type === 'enable') await enableField(change.field.id, actualReason);
|
else await disableField(change.field.id, actualReason);
|
createMessage.success(change.type === 'promoteGlobal' ? '已升级为全局字段' : change.type === 'enable' ? '字段已启用' : '字段已停用');
|
closeReasonModal();
|
reload();
|
}
|
|
function reasonTitle() {
|
if (pendingChange.value?.type === 'promoteGlobal') return '升级为全局字段';
|
return pendingChange.value?.type === 'enable' ? '启用字段' : '停用字段';
|
}
|
</script>
|
|
<template>
|
<div class="jnpf-content-wrapper">
|
<div class="jnpf-content-wrapper-center">
|
<div class="jnpf-content-wrapper-search-box">
|
<BasicForm @register="registerSearchForm" @reset="handleReset" @submit="handleSearch" />
|
</div>
|
<div class="jnpf-content-wrapper-content">
|
<BasicVxeTable @register="registerTable">
|
<template #tableTitle>
|
<a-button type="primary" pre-icon="icon-ym icon-ym-btn-add" @click="openFieldForm(true, {})"> 新增全局字段 </a-button>
|
<a-button data-test="open-lims-sources" @click="openLimsSources">
|
<template #icon><DatabaseOutlined /></template>
|
LIMS 数据项
|
</a-button>
|
</template>
|
<template #ziduanBianma="{ record }">
|
<span :title="record.ziduanBianma">{{ record.ziduanBianma }}</span>
|
</template>
|
<template #zuoyongFanwei="{ record }">
|
<a-tag :color="record.zuoyongFanwei === 'GLOBAL' ? 'blue' : 'default'">
|
{{ record.zuoyongFanwei === 'GLOBAL' ? '全局' : '模板私有' }}
|
</a-tag>
|
</template>
|
<template #mubanBianhao="{ record }">
|
{{ record.zuoyongFanwei === 'TEMPLATE' ? record.mubanBianhao || '-' : '-' }}
|
</template>
|
<template #dangqianBanbenHao="{ record }">
|
{{ record.dangqianBanbenHao ? `V${record.dangqianBanbenHao}` : '-' }}
|
</template>
|
<template #zuixinFabuBanbenHao="{ record }">
|
{{ record.zuixinFabuBanbenHao ? `V${record.zuixinFabuBanbenHao}` : '-' }}
|
</template>
|
<template #bizEnabled="{ record }">
|
<a-tag :color="record.bizEnabled === 'enabled' ? 'success' : 'default'">
|
{{ record.bizEnabled === 'enabled' ? '启用' : '停用' }}
|
</a-tag>
|
</template>
|
<template #action="{ record }">
|
<TableAction :actions="getTableActions(record)" />
|
</template>
|
</BasicVxeTable>
|
</div>
|
</div>
|
|
<FieldForm @register="registerFieldForm" @reload="reload" />
|
<VersionDrawer @register="registerVersionDrawer" @reload="reload" />
|
<BasicModal @register="registerReasonModal" :title="reasonTitle()" destroy-on-close @ok="confirmFieldChange">
|
<a-form-item label="操作原因" required>
|
<a-textarea v-model:value="reason" :maxlength="500" :rows="4" placeholder="请输入操作原因" show-count />
|
</a-form-item>
|
<a-alert
|
v-if="pendingChange?.type === 'promoteGlobal'"
|
message="字段编码和历史版本保持不变,模板归属将清空,字段可供所有模板引用。"
|
show-icon
|
type="warning" />
|
</BasicModal>
|
</div>
|
</template>
|
|
<style lang="scss" scoped>
|
:deep(.jnpf-vxe-table) {
|
min-width: 0;
|
}
|
</style>
|