<script lang="ts" setup>
|
import type { BasicColumn } from '@jnpf/ui/vxeTable';
|
|
import type { SourcePath } from '#/api/x/eln/sources';
|
|
import { computed, onMounted, ref } from 'vue';
|
|
import { useDrawer } from '@jnpf/ui/drawer';
|
import { BasicForm, useForm } from '@jnpf/ui/form';
|
import { BasicVxeTable, useVxeTable } from '@jnpf/ui/vxeTable';
|
|
import { getSourcePaths } from '#/api/x/eln/sources';
|
|
import SourcePathDrawer from './SourcePathDrawer.vue';
|
|
defineOptions({ name: 'ElnSourcePathCatalog' });
|
|
type StatusFilter = 'ALL' | SourcePath['status'];
|
|
const paths = ref<SourcePath[]>([]);
|
const errorMessage = ref('');
|
const operationMessage = ref('');
|
const searchText = ref('');
|
const statusFilter = ref<StatusFilter>('ALL');
|
|
const statusOptions = [
|
{ label: '全部状态', value: 'ALL' },
|
{ label: '草稿', value: 'DRAFT' },
|
{ label: '已发布', value: 'PUBLISHED' },
|
];
|
const filteredPaths = computed(() => {
|
const keyword = searchText.value.trim().toLowerCase();
|
return [...paths.value]
|
.filter(
|
(item) =>
|
(statusFilter.value === 'ALL' || item.status === statusFilter.value) &&
|
(!keyword || [item.displayName, item.path, item.scope].some((value) => value?.toLowerCase().includes(keyword))),
|
)
|
.sort((left, right) => left.displayName.localeCompare(right.displayName));
|
});
|
const columns: BasicColumn[] = [
|
{ dataIndex: 'displayName', minWidth: 180, title: '业务名称' },
|
{ dataIndex: 'path', minWidth: 320, title: '路径' },
|
{ dataIndex: 'scope', minWidth: 180, title: '适用于' },
|
{ dataIndex: 'revision', title: '版本', width: 90 },
|
{ dataIndex: 'status', slots: { default: 'status' }, title: '状态', width: 100 },
|
];
|
|
const [registerSearchForm, { getFieldsValue, resetFields }] = useForm({
|
baseColProps: { lg: 8, md: 12, sm: 12, xl: 6, xs: 24 },
|
compact: true,
|
labelWidth: 76,
|
schemas: [
|
{
|
component: 'Input',
|
componentProps: { allowClear: true, placeholder: '业务名称或路径', submitOnPressEnter: true },
|
field: 'keyword',
|
label: '关键字',
|
},
|
{
|
component: 'Select',
|
componentProps: {
|
allowClear: false,
|
options: statusOptions.map((item) => ({ fullName: item.label, id: item.value })),
|
placeholder: '全部',
|
},
|
defaultValue: 'ALL',
|
field: 'status',
|
label: '状态',
|
},
|
],
|
showActionButtonGroup: true,
|
showAdvancedButton: false,
|
});
|
const [registerTable, { setLoading }] = useVxeTable({
|
columns,
|
dataSource: filteredPaths,
|
immediate: false,
|
pagination: false,
|
rowKey: 'id',
|
showIndexColumn: false,
|
showTableSetting: false,
|
});
|
const [registerPathDrawer, { openDrawer: openPathDrawer }] = useDrawer();
|
|
onMounted(load);
|
|
async function load() {
|
setLoading(true);
|
errorMessage.value = '';
|
try {
|
const response = await getSourcePaths();
|
paths.value = response.data || [];
|
} catch (error) {
|
errorMessage.value = sourceErrorMessage(error, '来源路径加载失败,请重试');
|
} finally {
|
setLoading(false);
|
}
|
}
|
|
function handleSearch() {
|
const values = getFieldsValue();
|
searchText.value = typeof values.keyword === 'string' ? values.keyword.trim() : '';
|
statusFilter.value = statusOptions.some((item) => item.value === values.status) ? values.status : 'ALL';
|
}
|
|
async function handleReset() {
|
await resetFields();
|
searchText.value = '';
|
statusFilter.value = 'ALL';
|
}
|
|
function openNewPath() {
|
operationMessage.value = '';
|
openPathDrawer(true);
|
}
|
|
async function handleCreated() {
|
operationMessage.value = '来源路径已校验并发布';
|
await load();
|
}
|
|
function statusLabel(status: SourcePath['status']) {
|
return status === 'PUBLISHED' ? '已发布' : '草稿';
|
}
|
|
function sourceErrorMessage(error: unknown, fallback: string) {
|
const response = (error as { response?: { data?: { msg?: string } } })?.response?.data;
|
return response?.msg || (error instanceof Error && error.message) || fallback;
|
}
|
</script>
|
|
<template>
|
<div class="jnpf-content-wrapper">
|
<div class="jnpf-content-wrapper-center source-path-page">
|
<div class="jnpf-content-wrapper-search-box">
|
<BasicForm @register="registerSearchForm" @reset="handleReset" @submit="handleSearch" />
|
</div>
|
<div class="jnpf-content-wrapper-content source-path-table-content">
|
<a-alert v-if="errorMessage" :message="errorMessage" show-icon type="error">
|
<template #action><a-button size="small" @click="load">重试</a-button></template>
|
</a-alert>
|
<div v-if="operationMessage" class="operation-message" role="status">{{ operationMessage }}</div>
|
<BasicVxeTable @register="registerTable" :data-source="filteredPaths">
|
<template #tableTitle>
|
<a-button data-test="new-source-path" pre-icon="icon-ym icon-ym-btn-add" type="primary" @click="openNewPath">新增路径</a-button>
|
</template>
|
<template #status="{ record }">
|
<a-tag :color="record.status === 'PUBLISHED' ? 'success' : 'processing'">{{ statusLabel(record.status) }}</a-tag>
|
</template>
|
</BasicVxeTable>
|
</div>
|
<SourcePathDrawer @register="registerPathDrawer" @success="handleCreated" />
|
</div>
|
</div>
|
</template>
|
|
<style lang="scss" scoped>
|
.source-path-page,
|
.source-path-table-content {
|
min-width: 0;
|
min-height: 0;
|
}
|
.source-path-table-content {
|
display: flex;
|
flex-direction: column;
|
}
|
.source-path-table-content > :deep(.jnpf-vxe-table) {
|
min-height: 0;
|
flex: 1;
|
}
|
.operation-message {
|
padding: 9px 12px;
|
color: #17663b;
|
background: #edf8f1;
|
border: 1px solid #b7dfc7;
|
border-radius: 4px;
|
}
|
</style>
|