This commit is contained in:
2026-06-29 13:33:16 +08:00
89 changed files with 8024 additions and 943 deletions

View File

@@ -5,15 +5,6 @@
<a-button type="primary" v-auth="'mes:mes_material:add'" @click="handleAdd" preIcon="ant-design:plus-outlined">新增</a-button>
<a-button type="primary" v-auth="'mes:mes_material:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls">导出</a-button>
<j-upload-button type="primary" v-auth="'mes:mes_material:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<a-button
type="primary"
v-auth="'mes:mes_material:rubberQuickTestInspect'"
preIcon="ant-design:experiment-outlined"
:disabled="selectedRowKeys.length === 0"
@click="handleRubberQuickTest"
>
检验
</a-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
@@ -38,10 +29,7 @@ import { useListPage } from '/@/hooks/system/useListPage';
import MesMaterialModal from './modules/MesMaterialModal.vue';
import { columns, searchFormSchema } from './MesMaterial.data';
import { batchDelete, deleteOne, getExportUrl, getImportUrl, list } from './MesMaterial.api';
import { batchFromMaterial } from '/@/views/xslmes/mesXslRubberQuickTestRecord/MesXslRubberQuickTestRecord.api';
import { useMessage } from '/@/hooks/web/useMessage';
const { createMessage } = useMessage();
const [registerModal, { openModal }] = useModal();
const { tableContext, onExportXls, onImportXls } = useListPage({
tableProps: {
@@ -75,18 +63,6 @@ async function batchHandleDelete() {
function handleSuccess() {
reload();
}
async function handleRubberQuickTest() {
if (!selectedRowKeys.value?.length) {
createMessage.warning('请至少选择一条胶料');
return;
}
try {
await batchFromMaterial({ materialIds: [...selectedRowKeys.value] });
createMessage.success('快检记录已生成,请到「胶料快检记录」中编辑');
} catch (e: any) {
createMessage.error(e?.message || '生成失败');
}
}
function getTableAction(record) {
return [{ label: '编辑', onClick: handleEdit.bind(null, record), auth: 'mes:mes_material:edit' }];
}

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

@@ -13,7 +13,9 @@ enum Api {
exportXls = '/xslmes/mesXslRubberQuickTestRecord/exportXls',
queryById = '/xslmes/mesXslRubberQuickTestRecord/queryById',
queryLineList = '/xslmes/mesXslRubberQuickTestRecord/queryLineListByRecordId',
batchFromMaterial = '/xslmes/mesXslRubberQuickTestRecord/batchFromMaterial',
queryStdLineList = '/xslmes/mesXslRubberQuickTestRecord/queryStdLineListByRecordId',
queryRawLineList = '/xslmes/mesXslRubberQuickTestRecord/queryRawLineListByRecordId',
queryChartPointList = '/xslmes/mesXslRubberQuickTestRecord/queryChartPointListByRecordId',
}
export const getExportUrl = Api.exportXls;
@@ -25,7 +27,11 @@ export const queryById = (params: { id: string }) => defHttp.get({ url: Api.quer
export const queryLineListByRecordId = (params: { id: string }) => defHttp.get({ url: Api.queryLineList, params });
export const batchFromMaterial = (params) => defHttp.post({ url: Api.batchFromMaterial, params });
export const queryStdLineListByRecordId = (params: { id: string }) => defHttp.get({ url: Api.queryStdLineList, params });
export const queryRawLineListByRecordId = (params: { id: string }) => defHttp.get({ url: Api.queryRawLineList, params });
export const queryChartPointListByRecordId = (params: { id: string }) => defHttp.get({ url: Api.queryChartPointList, params });
export const deleteOne = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {

View File

@@ -4,14 +4,13 @@ import { JVxeColumn, JVxeTypes } from '/@/components/jeecg/JVxeTable/types';
const numProps = { style: { width: '100%' }, precision: 6 };
export const columns: BasicColumn[] = [
{ title: '号', align: 'center', dataIndex: 'recordNo', width: 150 },
{ title: '快检记录号', align: 'center', dataIndex: 'recordNo', width: 150 },
{ title: '胶料名称', align: 'center', dataIndex: 'rubberMaterialName', width: 140 },
{ title: '生产机台', align: 'center', dataIndex: 'prodEquipmentName', width: 120 },
{ title: '生产日期', align: 'center', dataIndex: 'productionDate', width: 110 },
{ title: '机台', align: 'center', dataIndex: 'prodEquipmentName', width: 120 },
{ title: '密炼日期', align: 'center', dataIndex: 'productionDate', width: 110 },
{ title: '车次编号', align: 'center', dataIndex: 'trainNo', width: 100 },
{ title: '班次', align: 'center', dataIndex: 'workShift_dictText', width: 80 },
{ title: '班组', align: 'center', dataIndex: 'workTeam_dictText', width: 80 },
{ title: '检验次数', align: 'center', dataIndex: 'inspectTimes', width: 90 },
{ title: '试验次数', align: 'center', dataIndex: 'inspectTimes', width: 90 },
{ title: '检验时间', align: 'center', dataIndex: 'inspectTime', width: 165 },
{
title: '检验人',
@@ -20,22 +19,24 @@ export const columns: BasicColumn[] = [
width: 100,
customRender: ({ record }) => record?.inspectorRealname || record?.inspectorUserId_dictText || '',
},
{ title: '检验类型', align: 'center', dataIndex: 'quickTestTypeName', width: 120 },
{ title: '实验标准', align: 'center', dataIndex: 'stdName', width: 120 },
{ title: '实验方法', align: 'center', dataIndex: 'testMethodName', width: 120 },
{ title: '实验类型', align: 'center', dataIndex: 'quickTestTypeName', width: 120 },
{ title: '检验结果', align: 'center', dataIndex: 'inspectResult_dictText', width: 90 },
{ title: '生产计划', align: 'center', dataIndex: 'productionPlanNo', width: 120 },
{ title: '密炼计划', align: 'center', dataIndex: 'productionPlanNo', width: 120 },
{ title: '检验机台', align: 'center', dataIndex: 'inspectEquipmentName', width: 120 },
{ title: '胶料卡片号', align: 'center', dataIndex: 'rubberCardNo', width: 120 },
{ title: '胶料批次', align: 'center', dataIndex: 'rubberBatchNo', width: 120 },
];
export const searchFormSchema: FormSchema[] = [
{ label: '号', field: 'recordNo', component: 'Input', colProps: { span: 6 } },
{ label: '快检记录号', field: 'recordNo', component: 'Input', colProps: { span: 6 } },
{ label: '胶料名称', field: 'rubberMaterialName', component: 'Input', colProps: { span: 6 } },
{
label: '生产机台',
label: '机台',
field: 'prodEquipmentLedgerId',
component: 'JDictSelectTag',
componentProps: { dictCode: 'mes_xsl_equipment_ledger,equipment_name,id', placeholder: '请选择生产机台' },
componentProps: { dictCode: 'mes_xsl_equipment_ledger,equipment_name,id', placeholder: '请选择机台' },
colProps: { span: 6 },
},
{
@@ -53,20 +54,13 @@ export const searchFormSchema: FormSchema[] = [
colProps: { span: 6 },
},
{
label: '班组',
field: 'workTeam',
component: 'JDictSelectTag',
componentProps: { dictCode: 'xslmes_rubber_quick_test_work_team', placeholder: '请选择班组' },
colProps: { span: 6 },
},
{
label: '检验类型',
label: '实验类型',
field: 'quickTestTypeId',
component: 'JSearchSelect',
componentProps: {
dict: 'mes_xsl_rubber_quick_test_type,type_name,id',
async: true,
placeholder: '请选择验类型',
placeholder: '请选择验类型',
},
colProps: { span: 6 },
},
@@ -82,7 +76,7 @@ export const searchFormSchema: FormSchema[] = [
},
colProps: { span: 6 },
},
{ label: '生产计划', field: 'productionPlanNo', component: 'Input', colProps: { span: 6 } },
{ label: '密炼计划', field: 'productionPlanNo', component: 'Input', colProps: { span: 6 } },
{ label: '胶料批次', field: 'rubberBatchNo', component: 'Input', colProps: { span: 6 } },
{
label: '检验结果',
@@ -103,7 +97,7 @@ export const formSchema: FormSchema[] = [
{ label: '', field: 'inspectorUsername', component: 'Input', show: false },
{ label: '', field: 'quickTestTypeId', component: 'Input', show: false },
{
label: '号',
label: '快检记录号',
field: 'recordNo',
component: 'Input',
componentProps: { readonly: true, placeholder: '保存时自动生成' },
@@ -115,13 +109,31 @@ export const formSchema: FormSchema[] = [
componentProps: { readonly: true },
},
{
label: '生产机台',
label: '实验标准',
field: 'stdName',
component: 'Input',
componentProps: { readonly: true },
},
{
label: '实验方法',
field: 'testMethodName',
component: 'Input',
componentProps: { readonly: true },
},
{
label: '实验类型',
field: 'quickTestTypeName',
component: 'Input',
componentProps: { readonly: true },
},
{
label: '炼机台',
field: 'prodEquipmentName',
component: 'Input',
slot: 'prodEquipmentPicker',
},
{
label: '生产日期',
label: '密炼日期',
field: 'productionDate',
component: 'DatePicker',
componentProps: { valueFormat: 'YYYY-MM-DD', style: { width: '100%' } },
@@ -134,13 +146,7 @@ export const formSchema: FormSchema[] = [
componentProps: { dictCode: 'xslmes_rubber_quick_test_work_shift', placeholder: '请选择班次' },
},
{
label: '班组',
field: 'workTeam',
component: 'JDictSelectTag',
componentProps: { dictCode: 'xslmes_rubber_quick_test_work_team', placeholder: '请选择班组' },
},
{
label: '检验次数',
label: '试验次数',
field: 'inspectTimes',
component: 'InputNumber',
componentProps: { style: { width: '100%' }, min: 0, precision: 0 },
@@ -176,23 +182,13 @@ export const formSchema: FormSchema[] = [
}),
},
{ label: '', field: 'inspectorRealname', component: 'Input', show: false },
{
label: '检验类型',
field: 'quickTestTypeId',
component: 'JSearchSelect',
componentProps: {
dict: 'mes_xsl_rubber_quick_test_type,type_name,id',
async: true,
placeholder: '请选择检验类型',
},
},
{
label: '检验结果',
field: 'inspectResult',
component: 'JDictSelectTag',
componentProps: { dictCode: 'xslmes_rubber_quick_test_record_result', placeholder: '合格/不合格' },
},
{ label: '生产计划', field: 'productionPlanNo', component: 'Input' },
{ label: '密炼计划', field: 'productionPlanNo', component: 'Input' },
{
label: '检验机台',
field: 'inspectEquipmentName',
@@ -203,6 +199,40 @@ export const formSchema: FormSchema[] = [
{ label: '胶料批次', field: 'rubberBatchNo', component: 'Input' },
];
export const stdLineJVxeColumns: JVxeColumn[] = [
{ title: '', key: 'dataPointId', type: JVxeTypes.hidden },
{ title: '数据点', key: 'pointName', type: JVxeTypes.normal, width: 160, disabled: true },
{ title: '下限值', key: 'lowerLimit', type: JVxeTypes.normal, width: 100, disabled: true },
{ title: '下警告值', key: 'lowerWarn', type: JVxeTypes.normal, width: 100, disabled: true },
{ title: '目标值', key: 'targetValue', type: JVxeTypes.normal, width: 100, disabled: true },
{ title: '上警告值', key: 'upperWarn', type: JVxeTypes.normal, width: 100, disabled: true },
{ title: '上限值', key: 'upperLimit', type: JVxeTypes.normal, width: 100, disabled: true },
];
export const rawLineJVxeColumns: JVxeColumn[] = [
{ title: '', key: 'dataPointId', type: JVxeTypes.hidden },
{ title: '编号', key: 'rowNo', type: JVxeTypes.normal, width: 90, disabled: true },
{ title: '数据点', key: 'inspectItem', type: JVxeTypes.normal, width: 140, disabled: true },
{ title: '下限值', key: 'lowerLimit', type: JVxeTypes.normal, width: 90, disabled: true },
{ title: '上限值', key: 'upperLimit', type: JVxeTypes.normal, width: 90, disabled: true },
{
title: '检测值',
key: 'inspectValue',
type: JVxeTypes.inputNumber,
width: 100,
componentProps: numProps,
},
{
title: '行检验结果',
key: 'rowInspectResult',
type: JVxeTypes.select,
width: 110,
disabled: true,
dictCode: 'xslmes_rubber_quick_test_record_result',
},
];
/** @deprecated 历史汇总明细,仅兼容旧数据 */
export const lineJVxeColumns: JVxeColumn[] = [
{ title: '', key: 'dataPointId', type: JVxeTypes.hidden },
{ title: '检验项目', key: 'inspectItem', type: JVxeTypes.normal, width: 180, disabled: true },

View File

@@ -3,14 +3,14 @@
v-bind="$attrs"
destroyOnClose
:title="title"
width="1100px"
width="1200px"
@register="registerModal"
@ok="handleSubmit"
>
<BasicForm @register="registerForm">
<template #prodEquipmentPicker="{ model, field }">
<a-input-group compact style="display: flex; width: 100%">
<a-input v-model:value="model[field]" read-only placeholder="请选择生产机台" style="flex: 1" />
<a-input v-model:value="model[field]" read-only placeholder="请选择机台" style="flex: 1" />
<a-button type="primary" :disabled="isDetail" @click="openProdEquipmentSelect">选择</a-button>
<a-button v-if="model.prodEquipmentLedgerId && !isDetail" @click="clearProdEquipment(model)">清除</a-button>
</a-input-group>
@@ -23,34 +23,86 @@
</a-input-group>
</template>
</BasicForm>
<a-divider orientation="left">检验明细由实验标准带出不可增删</a-divider>
<JVxeTable
v-if="tableReady"
ref="lineTableRef"
row-number
keep-source
:toolbar="false"
:insert-row="false"
:remove-btn="false"
:max-height="380"
:loading="lineLoading"
:columns="lineJVxeColumns"
:dataSource="lineDataSource"
:disabled="isDetail"
/>
<template v-if="useNewDetail">
<a-divider orientation="left">数据标准明细</a-divider>
<JVxeTable
v-if="tableReady"
row-number
keep-source
:toolbar="false"
:insert-row="false"
:remove-btn="false"
:max-height="260"
:loading="detailLoading"
:columns="stdLineJVxeColumns"
:dataSource="stdLineDataSource"
disabled
/>
<a-divider orientation="left">试验结果明细</a-divider>
<JVxeTable
v-if="tableReady"
row-number
keep-source
:toolbar="false"
:insert-row="false"
:remove-btn="false"
:max-height="320"
:loading="detailLoading"
:columns="rawLineJVxeColumns"
:dataSource="rawLineDataSource"
:disabled="isDetail"
/>
<a-divider orientation="left">曲线图</a-divider>
<a-row :gutter="16">
<a-col :span="12">
<div class="chart-title">温度曲线上模/下模</div>
<div ref="tempChartRef" class="chart-box"></div>
</a-col>
<a-col :span="12">
<div class="chart-title">S'(dNm) 曲线</div>
<div ref="torqueChartRef" class="chart-box"></div>
</a-col>
</a-row>
</template>
<template v-else>
<a-divider orientation="left">检验明细(历史数据)</a-divider>
<JVxeTable
v-if="tableReady"
row-number
keep-source
:toolbar="false"
:insert-row="false"
:remove-btn="false"
:max-height="380"
:loading="detailLoading"
:columns="lineJVxeColumns"
:dataSource="lineDataSource"
:disabled="isDetail"
/>
</template>
<MesXslEquipmentLedgerSelectModal @register="registerLedgerModal" @select="onLedgerSelect" />
</BasicModal>
</template>
<script lang="ts" setup>
import { computed, ref, unref } from 'vue';
import { computed, nextTick, ref, unref, watch, type Ref } from 'vue';
import { BasicModal, useModalInner, useModal } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import type { JVxeTableInstance } from '/@/components/jeecg/JVxeTable/types';
import { useMessage } from '/@/hooks/web/useMessage';
import { useUserStore } from '/@/store/modules/user';
import { formSchema, lineJVxeColumns } from '../MesXslRubberQuickTestRecord.data';
import { saveOrUpdate, queryById, queryLineListByRecordId } from '../MesXslRubberQuickTestRecord.api';
import { useECharts } from '/@/hooks/web/useECharts';
import {
formSchema,
lineJVxeColumns,
stdLineJVxeColumns,
rawLineJVxeColumns,
} from '../MesXslRubberQuickTestRecord.data';
import { saveOrUpdate, queryById, queryChartPointListByRecordId } from '../MesXslRubberQuickTestRecord.api';
import MesXslEquipmentLedgerSelectModal from '/@/views/xslmes/mesXslEquipInspectConfig/components/MesXslEquipmentLedgerSelectModal.vue';
const emit = defineEmits(['register', 'success']);
@@ -59,9 +111,19 @@
const isDetail = ref(false);
const tableReady = ref(false);
const lineLoading = ref(false);
const detailLoading = ref(false);
const useNewDetail = ref(false);
const stdLineDataSource = ref<Recordable[]>([]);
const rawLineDataSource = ref<Recordable[]>([]);
const chartPointDataSource = ref<Recordable[]>([]);
const lineDataSource = ref<Recordable[]>([]);
const lineTableRef = ref<JVxeTableInstance>();
const tempChartRef = ref<HTMLDivElement | null>(null);
const torqueChartRef = ref<HTMLDivElement | null>(null);
const { setOptions: setTempChartOptions, resize: resizeTempChart } = useECharts(tempChartRef as Ref<HTMLDivElement>);
const { setOptions: setTorqueChartOptions, resize: resizeTorqueChart } = useECharts(torqueChartRef as Ref<HTMLDivElement>);
const ledgerPickTarget = ref<'prod' | 'inspect'>('prod');
const [registerForm, { resetFields, setFieldsValue, validate, setProps, getFieldsValue }] = useForm({
@@ -75,19 +137,21 @@
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
tableReady.value = false;
stdLineDataSource.value = [];
rawLineDataSource.value = [];
chartPointDataSource.value = [];
lineDataSource.value = [];
useNewDetail.value = false;
await resetFields();
setModalProps({ confirmLoading: false, showCancelBtn: data?.showFooter, showOkBtn: data?.showFooter });
isDetail.value = !data?.showFooter;
setProps({ disabled: !data?.showFooter });
if (data?.record?.id) {
lineLoading.value = true;
detailLoading.value = true;
try {
const mainRaw = await queryById({ id: data.record.id });
const m = (mainRaw as any)?.id != null ? mainRaw : (mainRaw as any)?.result ?? mainRaw;
const linesRaw = await queryLineListByRecordId({ id: data.record.id });
const list = Array.isArray(linesRaw) ? linesRaw : (linesRaw as any)?.result ?? [];
const patch: Recordable = { ...m };
if (data?.showFooter && !patch.inspectorRealname && !patch.inspectorUserId) {
const user = userStore.getUserInfo || {};
@@ -96,16 +160,95 @@
patch.inspectorRealname = user.realname;
}
await setFieldsValue(patch);
lineDataSource.value = list || [];
const stdLines = m?.stdLineList ?? [];
const rawLines = m?.rawLineList ?? [];
let chartPoints = m?.chartPointList ?? [];
const legacyLines = m?.lineList ?? [];
if (stdLines.length > 0 || rawLines.length > 0 || chartPoints.length > 0) {
useNewDetail.value = true;
stdLineDataSource.value = stdLines;
rawLineDataSource.value = rawLines;
if (!chartPoints.length && data?.record?.id) {
try {
const chartRes = await queryChartPointListByRecordId({ id: data.record.id });
chartPoints = Array.isArray(chartRes) ? chartRes : (chartRes as any)?.result ?? [];
} catch (_) {
chartPoints = [];
}
}
chartPointDataSource.value = chartPoints;
} else {
lineDataSource.value = legacyLines;
}
} finally {
lineLoading.value = false;
detailLoading.value = false;
}
}
tableReady.value = true;
if (useNewDetail.value) {
await scheduleRenderCharts(chartPointDataSource.value);
}
});
async function scheduleRenderCharts(points: Recordable[]) {
await nextTick();
await nextTick();
window.setTimeout(() => {
renderCharts(points);
resizeTempChart();
resizeTorqueChart();
}, 120);
}
watch(useNewDetail, async (visible) => {
if (visible && chartPointDataSource.value.length) {
await scheduleRenderCharts(chartPointDataSource.value);
}
});
const title = computed(() => (unref(isDetail) ? '快检记录详情' : '编辑胶料快检记录'));
function toChartNumber(value: unknown) {
if (value === null || value === undefined || value === '') return null;
const num = Number(value);
return Number.isFinite(num) ? num : null;
}
function renderCharts(points: Recordable[]) {
if (!points?.length) {
setTempChartOptions({ title: { text: '暂无曲线数据', left: 'center', top: 'center', textStyle: { fontSize: 14 } } });
setTorqueChartOptions({ title: { text: '暂无曲线数据', left: 'center', top: 'center', textStyle: { fontSize: 14 } } });
return;
}
const sorted = [...points].sort((a, b) => (a.sortNo ?? 0) - (b.sortNo ?? 0));
const times = sorted.map((p) => toChartNumber(p.timeMin) ?? 0);
setTempChartOptions({
title: undefined,
tooltip: { trigger: 'axis' },
legend: { data: ['上模温度', '下模温度'] },
grid: { left: 48, right: 24, top: 40, bottom: 32 },
xAxis: { type: 'category', name: '时间(min)', data: times },
yAxis: { type: 'value', name: '温度()', min: 189, max: 201, interval: 3 },
series: [
{ name: '上模温度', type: 'line', smooth: true, data: sorted.map((p) => toChartNumber(p.upperTemp)) },
{ name: '下模温度', type: 'line', smooth: true, data: sorted.map((p) => toChartNumber(p.lowerTemp)) },
],
});
setTorqueChartOptions({
title: undefined,
tooltip: { trigger: 'axis' },
legend: { data: ["S'(dNm)"] },
grid: { left: 48, right: 24, top: 40, bottom: 32 },
xAxis: { type: 'category', name: '时间(min)', data: times },
yAxis: { type: 'value', name: "S'(dNm)", min: 0, max: 14.8 },
series: [{ name: "S'(dNm)", type: 'line', smooth: true, data: sorted.map((p) => toChartNumber(p.torqueS)) }],
});
}
function openProdEquipmentSelect() {
ledgerPickTarget.value = 'prod';
const vals = getFieldsValue();
@@ -149,23 +292,36 @@
}
try {
const values = await validate();
const lineRef = lineTableRef.value as any;
const tableData = (lineRef?.getTableData?.() || lineDataSource.value || []) as Recordable[];
const lineList = tableData
.filter((r) => r && r.inspectItem)
.map((r) => ({
dataPointId: r.dataPointId,
inspectItem: r.inspectItem,
lowerLimit: r.lowerLimit,
inspectValue: r.inspectValue,
upperLimit: r.upperLimit,
}));
if (!lineList.length) {
createMessage.warning('检验明细不能为空');
return;
const payload: Recordable = { ...values };
if (unref(useNewDetail)) {
payload.stdLineList = stdLineDataSource.value || [];
payload.rawLineList = rawLineDataSource.value || [];
payload.chartPointList = chartPointDataSource.value || [];
if (!payload.stdLineList.length || !payload.rawLineList.length) {
createMessage.warning('数据标准明细与试验结果明细不能为空');
return;
}
} else {
const tableData = lineDataSource.value || [];
const lineList = tableData
.filter((r) => r && r.inspectItem)
.map((r) => ({
dataPointId: r.dataPointId,
inspectItem: r.inspectItem,
lowerLimit: r.lowerLimit,
inspectValue: r.inspectValue,
upperLimit: r.upperLimit,
}));
if (!lineList.length) {
createMessage.warning('检验明细不能为空');
return;
}
payload.lineList = lineList;
}
setModalProps({ confirmLoading: true });
await saveOrUpdate({ ...values, lineList }, true);
await saveOrUpdate(payload, true);
closeModal();
emit('success');
} finally {
@@ -173,3 +329,15 @@
}
}
</script>
<style scoped>
.chart-title {
font-size: 13px;
color: rgba(0, 0, 0, 0.65);
margin-bottom: 8px;
}
.chart-box {
width: 100%;
height: 220px;
}
</style>

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();
}