Merge branch 'main' of http://27.223.88.102:33000/chenx/qhmes
This commit is contained in:
@@ -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' }];
|
||||
}
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 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>
|
||||
@@ -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>
|
||||
64
jeecgboot-vue3/src/views/xslmesMcs/mcsSyncConfig/index.vue
Normal file
64
jeecgboot-vue3/src/views/xslmesMcs/mcsSyncConfig/index.vue
Normal 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>
|
||||
@@ -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 } });
|
||||
@@ -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 } },
|
||||
];
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user