中间表采集新增采集配置,实现可视化可控采集

This commit is contained in:
geht
2026-06-18 10:55:11 +08:00
parent 1c982052d3
commit 73a22b5ed9
29 changed files with 2602 additions and 27 deletions

View File

@@ -14,10 +14,10 @@ enum Api {
export const list = (params) => defHttp.get({ url: Api.list, params });
export const checkActionName = (params: { actionName: string; dataId?: string }) =>
export const checkActionName = (params: { equipmentId?: string; actionName: string; dataId?: string }) =>
defHttp.get({ url: Api.checkActionName, params }, { successMessageMode: 'none', errorMessageMode: 'none' });
export const checkActionCode = (params: { actionCode: string; dataId?: string }) =>
export const checkActionCode = (params: { equipmentId?: string; actionCode: string; dataId?: string }) =>
defHttp.get({ url: Api.checkActionCode, params }, { successMessageMode: 'none', errorMessageMode: 'none' });
export const deleteOne = (params, handleSuccess) =>

View File

@@ -2,9 +2,11 @@ import { BasicColumn, FormSchema } from '/@/components/Table';
import { checkActionCode, checkActionName } from './MesXslMixerAction.api';
export const columns: BasicColumn[] = [
{ title: '设备名称', align: 'center', dataIndex: 'equipmentId_dictText', width: 180 },
{ title: '设备名称', align: 'center', dataIndex: 'equipmentName', width: 180 },
{ title: '机台编号', align: 'center', dataIndex: 'equipId', width: 110 },
{ title: '机台类型', align: 'center', dataIndex: 'equipType', width: 110 },
{ title: '动作名称', align: 'center', dataIndex: 'actionName', width: 180 },
{ title: '动作代号', align: 'center', dataIndex: 'actionCode', width: 160 },
{ title: '动作代号', align: 'center', dataIndex: 'actionCode', width: 120 },
{ title: '创建时间', align: 'center', dataIndex: 'createTime', width: 170 },
];
@@ -16,6 +18,7 @@ export const searchFormSchema: FormSchema[] = [
componentProps: { dictCode: 'mes_xsl_equipment_ledger,equipment_name,id' },
colProps: { span: 6 },
},
{ label: '机台编号', field: 'equipId', component: 'Input', colProps: { span: 6 } },
{ label: '动作名称', field: 'actionName', component: 'Input', colProps: { span: 6 } },
{ label: '动作代号', field: 'actionCode', component: 'Input', colProps: { span: 6 } },
];
@@ -29,6 +32,21 @@ export const formSchema: FormSchema[] = [
required: true,
componentProps: { dictCode: 'mes_xsl_equipment_ledger,equipment_name,id', placeholder: '请选择设备台账中的设备' },
},
// 采集冗余字段:仅采集数据有值,只读展示
{
label: '机台编号',
field: 'equipId',
component: 'Input',
componentProps: { disabled: true },
ifShow: ({ values }) => !!values.equipId,
},
{
label: '机台类型',
field: 'equipType',
component: 'Input',
componentProps: { disabled: true },
ifShow: ({ values }) => !!values.equipType,
},
{
label: '动作名称',
field: 'actionName',
@@ -41,10 +59,10 @@ export const formSchema: FormSchema[] = [
const v = value == null ? '' : String(value).trim();
if (!v) return Promise.resolve();
try {
await checkActionName({ actionName: v, dataId: model?.id });
await checkActionName({ equipmentId: model?.equipmentId, actionName: v, dataId: model?.id });
return Promise.resolve();
} catch (e: any) {
return Promise.reject(e?.response?.data?.message || e?.message || '动作名称不能重复');
return Promise.reject(e?.response?.data?.message || e?.message || '同一设备下动作名称不能重复');
}
},
trigger: 'blur',
@@ -63,10 +81,10 @@ export const formSchema: FormSchema[] = [
const v = value == null ? '' : String(value).trim();
if (!v) return Promise.resolve();
try {
await checkActionCode({ actionCode: v, dataId: model?.id });
await checkActionCode({ equipmentId: model?.equipmentId, actionCode: v, dataId: model?.id });
return Promise.resolve();
} catch (e: any) {
return Promise.reject(e?.response?.data?.message || e?.message || '动作代号不能重复');
return Promise.reject(e?.response?.data?.message || e?.message || '同一设备下动作代号不能重复');
}
},
trigger: 'blur',

View File

@@ -0,0 +1,201 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" title="采集操作" :width="560" @ok="handleOk">
<a-spin :spinning="loading">
<a-form :labelCol="{ span: 7 }" :wrapperCol="{ span: 16 }">
<a-form-item label="采集配置">
<span>{{ configName || '-' }}</span>
<span v-if="sourceTable" style="color: #999"> 源表{{ sourceTable }}</span>
</a-form-item>
<a-form-item label="是否采集">
<a-switch v-model:checked="enabled" checkedChildren="采集中" unCheckedChildren="已停止" />
</a-form-item>
<a-form-item label="采集间隔">
<a-input-number v-model:value="intervalSeconds" :min="1" :precision="0" addonAfter="秒" style="width: 100%" />
</a-form-item>
<a-form-item label="采集模式">
<a-radio-group v-model:value="syncMode" button-style="solid">
<a-radio-button value="FULL">全量匹配</a-radio-button>
<a-radio-button value="TIME">时间匹配</a-radio-button>
<a-radio-button value="INCR">增量匹配</a-radio-button>
</a-radio-group>
<div style="color: #999; font-size: 12px; margin-top: 4px">{{ modeHint }}</div>
</a-form-item>
<template v-if="syncMode === 'TIME'">
<a-form-item label="时间列" required>
<a-select v-model:value="incrColumn" :options="sourceColumnOptions" showSearch allowClear placeholder="选择源表的时间列(如 WriteTime)" />
</a-form-item>
<a-form-item label="时间范围">
<a-radio-group v-model:value="timeWindow">
<a-radio value="TODAY">当天</a-radio>
<a-radio value="LAST7">最近七天</a-radio>
</a-radio-group>
</a-form-item>
</template>
<template v-else-if="syncMode === 'INCR'">
<a-form-item label="标记列" required>
<a-select v-model:value="incrColumn" :options="sourceColumnOptions" showSearch allowClear placeholder="选择源表用于标记是否已采集的列" />
<div style="color: #999; font-size: 12px">需在字段映射中勾选匹配键( GUID)作为回写主键并开启中间库写入开关</div>
</a-form-item>
<a-form-item label="采集条件" required>
<a-radio-group v-model:value="flagCondition" button-style="solid">
<a-radio-button value="IS_NULL">为空</a-radio-button>
<a-radio-button value="EQ_EMPTY">等于</a-radio-button>
<a-radio-button value="NE_EMPTY">不等于</a-radio-button>
</a-radio-group>
<!-- update-begin---author:GHT ---date:20260617 forMES上辅机等于/不等于支持自定义匹配值----------- -->
<a-input
v-if="flagCondition === 'EQ_EMPTY' || flagCondition === 'NE_EMPTY'"
v-model:value="flagMatchValue"
:placeholder="`填写要${flagCondition === 'EQ_EMPTY' ? '等于' : '不等于'}的值(留空表示空字符串“”)`"
allowClear
style="width: 100%; margin-top: 6px"
/>
<!-- update-end---author:GHT ---date:20260617 forMES上辅机等于/不等于支持自定义匹配值----------- -->
<div style="color: #999; font-size: 12px">仅采集标记列满足此条件的行</div>
</a-form-item>
<a-form-item label="回写值">
<a-input v-model:value="flagWriteValue" placeholder="采集完成后写回标记列的值,默认 1" allowClear style="width: 100%" />
<div style="color: #999; font-size: 12px">采集完成后把标记列回写成该值使这些行不再满足上面的采集条件默认1</div>
</a-form-item>
<a-form-item label="每轮最大行数">
<a-input-number v-model:value="batchLimit" :min="100" :step="500" :precision="0" addonAfter="行" style="width: 100%" />
<div style="color: #999; font-size: 12px">每个采集周期最多取这么多行分批吃完历史未采集数据</div>
</a-form-item>
</template>
<a-form-item v-if="lastSyncResult" label="最近采集">
<span style="color: #999">{{ lastSyncResult }}</span>
</a-form-item>
</a-form>
</a-spin>
</BasicModal>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { useMessage } from '/@/hooks/web/useMessage';
import { queryById, getByBizType, saveCollect, getSourceColumns } from '../mcsSyncConfig.api';
const emit = defineEmits(['register', 'success']);
const { createMessage } = useMessage();
const loading = ref(false);
const configId = ref('');
const configName = ref('');
const sourceTable = ref('');
const enabled = ref(false);
const intervalSeconds = ref(1);
const syncMode = ref('FULL');
const incrColumn = ref<string | undefined>(undefined);
const timeWindow = ref('TODAY');
const batchLimit = ref(2000);
const flagCondition = ref('IS_NULL');
const flagMatchValue = ref('');
const flagWriteValue = ref('1');
const lastWatermark = ref('');
const lastSyncResult = ref('');
const sourceColumnOptions = ref<any[]>([]);
const modeHint = computed(() => {
if (syncMode.value === 'TIME') return '只采集时间列在所选范围内的数据,再按匹配键更新/新增。适合中大型表只关注近期数据。';
if (syncMode.value === 'INCR') return '标记位回写只采集“标记列”为空的行采完回写“1”下轮不再重复。适合带 GUID 主键、无可靠递增列的流水表。';
return '全表读取并按匹配键更新/新增。适合数据量小、以更新为主的表。';
});
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
setModalProps({ confirmLoading: false });
loading.value = true;
try {
reset();
const cfg: any = data?.id ? await queryById(data.id) : await getByBizType(data?.bizType || 'MIX_ACT');
if (!cfg || !cfg.id) {
createMessage.warning('未找到采集配置,请先在「采集配置」中新增');
return;
}
configId.value = cfg.id;
configName.value = cfg.configName || cfg.bizName || cfg.sourceTable;
sourceTable.value = cfg.sourceTable || '';
enabled.value = cfg.running === true || cfg.status === '1';
intervalSeconds.value = cfg.intervalSeconds || 1;
syncMode.value = cfg.syncMode || 'FULL';
incrColumn.value = cfg.incrColumn || undefined;
timeWindow.value = cfg.timeWindow || 'TODAY';
batchLimit.value = cfg.batchLimit || 2000;
flagCondition.value = cfg.flagCondition || 'IS_NULL';
flagMatchValue.value = cfg.flagMatchValue ?? '';
flagWriteValue.value = cfg.flagWriteValue ?? '1';
lastWatermark.value = cfg.lastWatermark || '';
lastSyncResult.value = cfg.lastSyncResult || '';
// 载入源表列供时间列/增量列选择(中间库未连接时静默忽略)
if (sourceTable.value) {
try {
const cols: any = await getSourceColumns(sourceTable.value);
sourceColumnOptions.value = (cols || []).map((c: any) => ({
label: c.columnName + (c.columnComment ? ` - ${c.columnComment}` : '') + (c.dataType ? ` (${c.dataType})` : ''),
value: c.columnName,
}));
} catch (e) {
sourceColumnOptions.value = [];
}
}
} finally {
loading.value = false;
}
});
function reset() {
configId.value = '';
configName.value = '';
sourceTable.value = '';
enabled.value = false;
intervalSeconds.value = 1;
syncMode.value = 'FULL';
incrColumn.value = undefined;
timeWindow.value = 'TODAY';
batchLimit.value = 2000;
flagCondition.value = 'IS_NULL';
flagMatchValue.value = '';
flagWriteValue.value = '1';
lastWatermark.value = '';
lastSyncResult.value = '';
sourceColumnOptions.value = [];
}
async function handleOk() {
if (!configId.value) {
closeModal();
return;
}
if (!intervalSeconds.value || intervalSeconds.value < 1) {
createMessage.warning('采集间隔不能小于1秒');
return;
}
if ((syncMode.value === 'TIME' || syncMode.value === 'INCR') && !incrColumn.value) {
createMessage.warning(syncMode.value === 'TIME' ? '请选择时间列' : '请选择标记列');
return;
}
setModalProps({ confirmLoading: true });
try {
await saveCollect({
id: configId.value,
status: enabled.value ? '1' : '0',
intervalSeconds: intervalSeconds.value,
syncMode: syncMode.value,
incrColumn: incrColumn.value,
timeWindow: timeWindow.value,
batchLimit: batchLimit.value,
flagCondition: flagCondition.value,
flagMatchValue: flagMatchValue.value,
flagWriteValue: flagWriteValue.value,
});
closeModal();
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>

View File

@@ -0,0 +1,267 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" :width="960" @ok="handleSubmit" :confirmLoading="confirmLoading">
<a-spin :spinning="loading">
<a-form :model="form" :labelCol="{ span: 6 }" :wrapperCol="{ span: 16 }">
<a-row>
<a-col :span="12">
<a-form-item label="配置名称" required>
<a-input v-model:value="form.configName" placeholder="如:密炼机动作采集" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="采集间隔">
<a-input-number v-model:value="form.intervalSeconds" :min="1" :precision="0" addonAfter="秒" style="width: 100%" />
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :span="12">
<a-form-item label="中间库源表" required>
<a-select
v-model:value="form.sourceTable"
:options="sourceTableOptions"
showSearch
placeholder="选择中间库的表"
style="width: 100%"
@change="onSourceTableChange"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="MES目标表" required>
<a-select
v-model:value="form.targetTable"
:options="targetTableOptions"
showSearch
placeholder="选择MES的表(mes_xsl_)"
style="width: 100%"
@change="onTargetTableChange"
/>
</a-form-item>
</a-col>
</a-row>
</a-form>
<a-divider style="margin: 8px 0">字段映射中间库字段 / MES接收字段勾选匹配键作为去重Upsert唯一键</a-divider>
<a-table
:columns="mappingColumns"
:dataSource="mappingRows"
:pagination="false"
size="small"
rowKey="sourceField"
:scroll="{ y: 360 }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'sourceField'">
<div>{{ record.sourceField }}</div>
<div style="color: #999; font-size: 12px">{{ record.sourceFieldComment }}</div>
</template>
<template v-else-if="column.dataIndex === 'targetField'">
<a-select
v-model:value="record.targetField"
:options="targetColumnOptions"
allowClear
showSearch
placeholder="选择MES接收字段"
style="width: 100%"
:disabled="!form.targetTable"
@change="(v) => onTargetFieldChange(record, v)"
/>
</template>
<template v-else-if="column.dataIndex === 'matchKey'">
<a-checkbox v-model:checked="record.matchKey" />
</template>
</template>
</a-table>
<div v-if="mappingRows.length === 0" style="text-align: center; color: #999; padding: 16px">请选择中间库源表以载入字段</div>
</a-spin>
</BasicModal>
</template>
<script lang="ts" setup>
import { computed, reactive, ref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { useMessage } from '/@/hooks/web/useMessage';
import { queryById, saveOrUpdate, getSourceTables, getSourceColumns, getTargetTables, getTargetColumns } from '../mcsSyncConfig.api';
const emit = defineEmits(['register', 'success']);
const { createMessage } = useMessage();
const isUpdate = ref(false);
const loading = ref(false);
const confirmLoading = ref(false);
const form = reactive<any>({
id: undefined,
configName: '',
bizType: '',
sourceTable: undefined,
sourceTableComment: '',
targetTable: undefined,
targetTableComment: '',
intervalSeconds: 1,
});
const sourceTableOptions = ref<any[]>([]);
const targetTableOptions = ref<any[]>([]);
const targetColumnOptions = ref<any[]>([]);
const mappingRows = ref<any[]>([]);
const title = computed(() => (isUpdate.value ? '编辑采集配置' : '新增采集配置'));
const mappingColumns = [
{ title: '中间库字段', dataIndex: 'sourceField', width: 240 },
{ title: '类型', dataIndex: 'sourceFieldType', width: 90 },
{ title: 'MES接收字段', dataIndex: 'targetField' },
{ title: '匹配键', dataIndex: 'matchKey', width: 70, align: 'center' },
];
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
setModalProps({ confirmLoading: false });
loading.value = true;
try {
resetForm();
isUpdate.value = !!data?.isUpdate;
// 表清单
const [srcTables, tgtTables] = await Promise.all([safeGet(getSourceTables), getTargetTables()]);
sourceTableOptions.value = (srcTables || []).map((t: any) => ({
label: t.tableName + (t.tableComment ? ` - ${t.tableComment}` : ''),
value: t.tableName,
comment: t.tableComment,
}));
targetTableOptions.value = (tgtTables || []).map((t: any) => ({
label: t.tableName + (t.tableComment ? ` - ${t.tableComment}` : ''),
value: t.tableName,
comment: t.tableComment,
}));
if (isUpdate.value && data?.record?.id) {
const cfg: any = await queryById(data.record.id);
Object.assign(form, {
id: cfg.id,
configName: cfg.configName,
bizType: cfg.bizType,
sourceTable: cfg.sourceTable,
sourceTableComment: cfg.sourceTableComment,
targetTable: cfg.targetTable,
targetTableComment: cfg.targetTableComment,
intervalSeconds: cfg.intervalSeconds || 1,
});
// 载入目标字段选项
if (cfg.targetTable) {
targetColumnOptions.value = buildColumnOptions(await getTargetColumns(cfg.targetTable));
}
// 字段映射来自已存配置
mappingRows.value = (cfg.fieldList || []).map((f: any) => ({
sourceField: f.sourceField,
sourceFieldComment: f.sourceFieldComment,
sourceFieldType: f.sourceFieldType,
targetField: f.targetField,
targetFieldComment: f.targetFieldComment,
matchKey: f.matchKey === '1',
}));
}
} finally {
loading.value = false;
}
});
function resetForm() {
Object.assign(form, {
id: undefined,
configName: '',
bizType: '',
sourceTable: undefined,
sourceTableComment: '',
targetTable: undefined,
targetTableComment: '',
intervalSeconds: 1,
});
targetColumnOptions.value = [];
mappingRows.value = [];
}
async function safeGet(fn: () => Promise<any>) {
try {
return await fn();
} catch (e) {
return [];
}
}
function buildColumnOptions(cols: any[]) {
return (cols || []).map((c: any) => ({
label: c.columnName + (c.columnComment ? ` - ${c.columnComment}` : ''),
value: c.columnName,
comment: c.columnComment,
}));
}
async function onSourceTableChange(val: string) {
const opt = sourceTableOptions.value.find((o) => o.value === val);
form.sourceTableComment = opt?.comment || '';
if (!val) {
mappingRows.value = [];
return;
}
const cols = await getSourceColumns(val);
mappingRows.value = (cols || []).map((c: any) => {
// 同名自动匹配 MES 字段
const guess = targetColumnOptions.value.find((o) => o.value.toLowerCase() === String(c.columnName).toLowerCase());
return {
sourceField: c.columnName,
sourceFieldComment: c.columnComment,
sourceFieldType: c.dataType,
targetField: guess ? guess.value : undefined,
targetFieldComment: guess ? guess.comment : '',
matchKey: false,
};
});
}
async function onTargetTableChange(val: string) {
const opt = targetTableOptions.value.find((o) => o.value === val);
form.targetTableComment = opt?.comment || '';
targetColumnOptions.value = val ? buildColumnOptions(await getTargetColumns(val)) : [];
}
function onTargetFieldChange(record: any, val: string) {
const opt = targetColumnOptions.value.find((o) => o.value === val);
record.targetFieldComment = opt?.comment || '';
}
async function handleSubmit() {
if (!form.configName) {
createMessage.warning('请填写配置名称');
return;
}
if (!form.sourceTable || !form.targetTable) {
createMessage.warning('请选择中间库源表与MES目标表');
return;
}
const fieldList = mappingRows.value
.filter((r) => r.targetField)
.map((r, idx) => ({
sourceField: r.sourceField,
sourceFieldComment: r.sourceFieldComment,
sourceFieldType: r.sourceFieldType,
targetField: r.targetField,
targetFieldComment: r.targetFieldComment,
matchKey: r.matchKey ? '1' : '0',
sortNo: idx,
}));
if (fieldList.length === 0) {
createMessage.warning('请至少为一个中间库字段选择 MES 接收字段');
return;
}
confirmLoading.value = true;
try {
await saveOrUpdate({ ...form, fieldList }, isUpdate.value);
closeModal();
emit('success');
} finally {
confirmLoading.value = false;
}
}
</script>

View File

@@ -0,0 +1,64 @@
<template>
<div>
<BasicTable @register="registerTable">
<template #tableTitle>
<a-button type="primary" v-auth="'xslmes:mcsSyncConfig:add'" preIcon="ant-design:plus-outlined" @click="handleAdd">新增采集配置</a-button>
</template>
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
<SyncConfigModal @register="registerEditModal" @success="reload" />
<CollectModal @register="registerCollectModal" @success="reload" />
</div>
</template>
<script lang="ts" name="xslmes-mcs-mcsSyncConfig" setup>
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import SyncConfigModal from './components/SyncConfigModal.vue';
import CollectModal from './components/CollectModal.vue';
import { columns, searchFormSchema } from './mcsSyncConfig.data';
import { list, deleteOne } from './mcsSyncConfig.api';
const [registerEditModal, { openModal: openEditModal }] = useModal();
const [registerCollectModal, { openModal: openCollectModal }] = useModal();
const { tableContext } = useListPage({
tableProps: {
title: '采集配置',
api: list,
columns,
canResize: true,
formConfig: { labelWidth: 100, schemas: searchFormSchema, autoSubmitOnEnter: true },
actionColumn: { width: 200, fixed: 'right' },
},
});
const [registerTable, { reload }] = tableContext;
function handleAdd() {
openEditModal(true, { isUpdate: false });
}
function handleEdit(record) {
openEditModal(true, { isUpdate: true, record });
}
function handleCollect(record) {
openCollectModal(true, { id: record.id });
}
async function handleDelete(record) {
await deleteOne(record.id, reload);
}
function getTableAction(record) {
return [
{ label: '编辑', onClick: handleEdit.bind(null, record), auth: 'xslmes:mcsSyncConfig:edit' },
{ label: '采集操作', onClick: handleCollect.bind(null, record), auth: 'xslmes:mcsSyncConfig:setting' },
{
label: '删除',
color: 'error',
popConfirm: { title: '确认删除该采集配置?', confirm: handleDelete.bind(null, record) },
auth: 'xslmes:mcsSyncConfig:delete',
},
];
}
</script>

View File

@@ -0,0 +1,44 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
list = '/xslmes/mcs/syncConfig/list',
queryById = '/xslmes/mcs/syncConfig/queryById',
getByBizType = '/xslmes/mcs/syncConfig/getByBizType',
add = '/xslmes/mcs/syncConfig/add',
edit = '/xslmes/mcs/syncConfig/edit',
deleteOne = '/xslmes/mcs/syncConfig/delete',
saveCollect = '/xslmes/mcs/syncConfig/saveCollect',
sourceTables = '/xslmes/mcs/syncConfig/meta/sourceTables',
sourceColumns = '/xslmes/mcs/syncConfig/meta/sourceColumns',
targetTables = '/xslmes/mcs/syncConfig/meta/targetTables',
targetColumns = '/xslmes/mcs/syncConfig/meta/targetColumns',
}
export const list = (params) => defHttp.get({ url: Api.list, params });
export const queryById = (id: string) => defHttp.get({ url: Api.queryById, params: { id } });
export const getByBizType = (bizType = 'MIX_ACT') => defHttp.get({ url: Api.getByBizType, params: { bizType } });
export const saveOrUpdate = (params, isUpdate: boolean) => defHttp.post({ url: isUpdate ? Api.edit : Api.add, params });
export const deleteOne = (id: string, handleSuccess) =>
defHttp.delete({ url: Api.deleteOne, params: { id } }, { joinParamsToUrl: true }).then(() => handleSuccess());
// 采集操作status '1'/'0' 表示是否采集syncMode FULL/TIME/INCR
export const saveCollect = (params: {
id: string;
status: string;
intervalSeconds: number;
syncMode?: string;
incrColumn?: string;
timeWindow?: string;
batchLimit?: number;
flagCondition?: string;
flagWriteValue?: string;
}) => defHttp.post({ url: Api.saveCollect, params });
export const getSourceTables = () => defHttp.get({ url: Api.sourceTables }, { errorMessageMode: 'message' });
export const getSourceColumns = (table: string) => defHttp.get({ url: Api.sourceColumns, params: { table } }, { errorMessageMode: 'message' });
export const getTargetTables = () => defHttp.get({ url: Api.targetTables });
export const getTargetColumns = (table: string) => defHttp.get({ url: Api.targetColumns, params: { table } });

View File

@@ -0,0 +1,41 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
export const columns: BasicColumn[] = [
{ title: '配置名称', align: 'center', dataIndex: 'configName', width: 160 },
{
title: '中间库源表',
align: 'center',
dataIndex: 'sourceTable',
width: 200,
customRender: ({ record }) => (record.sourceTableComment ? `${record.sourceTable}${record.sourceTableComment}` : record.sourceTable),
},
{
title: 'MES目标表',
align: 'center',
dataIndex: 'targetTable',
width: 200,
customRender: ({ record }) => (record.targetTableComment ? `${record.targetTable}${record.targetTableComment}` : record.targetTable),
},
{
title: '采集模式',
align: 'center',
dataIndex: 'syncMode',
width: 100,
customRender: ({ record }) => ({ FULL: '全量匹配', TIME: '时间匹配', INCR: '增量匹配' }[record.syncMode] || '全量匹配'),
},
{ title: '采集间隔(秒)', align: 'center', dataIndex: 'intervalSeconds', width: 100 },
{
title: '状态',
align: 'center',
dataIndex: 'running',
width: 90,
customRender: ({ record }) => (record.running ? '采集中' : '已停止'),
},
{ title: '最近采集时间', align: 'center', dataIndex: 'lastSyncTime', width: 160 },
{ title: '最近采集结果', align: 'center', dataIndex: 'lastSyncResult', width: 200 },
];
export const searchFormSchema: FormSchema[] = [
{ label: '配置名称', field: 'configName', component: 'Input', colProps: { span: 6 } },
{ label: '中间库源表', field: 'sourceTable', component: 'Input', colProps: { span: 6 } },
];

View File

@@ -1,8 +1,9 @@
<template>
<template>
<div>
<BasicTable @register="registerTable">
<template #tableTitle>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<a-button type="primary" v-auth="'xslmes:mcsSyncConfig:setting'" preIcon="ant-design:sync-outlined" @click="openCollect"> 采集操作 </a-button>
<a-button type="link" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<super-query :config="superQueryConfig" @search="handleSuperQuery" />
</template>
<template #action="{ record }">
@@ -10,6 +11,7 @@
</template>
</BasicTable>
<McsToMesMixActModal @register="registerModal" />
<CollectModal @register="registerCollectModal" @success="reload" />
</div>
</template>
@@ -19,11 +21,13 @@
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import McsToMesMixActModal from './components/McsToMesMixActModal.vue';
import CollectModal from '../mcsSyncConfig/components/CollectModal.vue';
import { columns, searchFormSchema } from './McsToMesMixAct.data';
import { list, getExportUrl } from './McsToMesMixAct.api';
const queryParam = reactive<any>({});
const [registerModal, { openModal }] = useModal();
const [registerCollectModal, { openModal: openCollectModal }] = useModal();
const { tableContext, onExportXls } = useListPage({
tableProps: {
@@ -49,8 +53,15 @@
const [registerTable, { reload }] = tableContext;
const superQueryConfig = reactive({});
// 采集操作:弹窗维护是否采集 + 采集间隔(绑定密炼动作采集配置 MIX_ACT
function openCollect() {
openCollectModal(true, { bizType: 'MIX_ACT' });
}
function handleSuperQuery(params) {
Object.keys(params).map((k) => { queryParam[k] = params[k]; });
Object.keys(params).map((k) => {
queryParam[k] = params[k];
});
reload();
}