202 lines
9.0 KiB
Vue
202 lines
9.0 KiB
Vue
<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 for:【MES上辅机】等于/不等于支持自定义匹配值----------- -->
|
||
<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 for:【MES上辅机】等于/不等于支持自定义匹配值----------- -->
|
||
<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>
|