新增混炼示方生成预览与批量创建功能,优化相关字段及用户交互,修复界面显示问题,增强系统稳定性和用户体验。

This commit is contained in:
geht
2026-05-22 19:43:41 +08:00
parent f3e3a99ebc
commit c85657d199
30 changed files with 1786 additions and 61 deletions

View File

@@ -10,6 +10,13 @@ import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form';
import { defHttp } from '/@/utils/http/axios';
import { loadTreeData } from '/@/views/system/category/category.api';
import {
MATERIAL_RAW_AUX_CODE,
materialRawAuxCategoryId,
isMaterialRawAuxSubCategory,
toIsRubberFlag,
fromIsRubberFlag,
} from '/@/views/system/category/category.constants';
import { useMessage } from '/@/hooks/web/useMessage';
import type { FormSchema } from '/@/components/Form';
import type { Recordable } from '/@/types/global';
@@ -34,6 +41,15 @@ const schemas: FormSchema[] = [
required: true,
componentProps: { maxlength: 50, placeholder: '请输入分类名称' },
},
{
label: ' ',
field: 'isRubber',
component: 'Checkbox',
defaultValue: false,
renderComponentContent: '胶料',
colProps: { span: 24 },
ifShow: ({ values }) => isMaterialRawAuxSubCategory(values.pid),
},
];
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema, scrollToField }] = useForm({
@@ -64,10 +80,30 @@ async function buildPidTree(): Promise<Recordable[]> {
];
}
async function ensureMaterialRawAuxCategoryId() {
if (materialRawAuxCategoryId.value) {
return;
}
const res = await defHttp.get(
{ url: '/sys/category/loadOne', params: { field: 'code', val: MATERIAL_RAW_AUX_CODE } },
{ isTransformResponse: false },
);
if (res?.success && res?.result?.id) {
materialRawAuxCategoryId.value = res.result.id;
}
}
function normalizeSubmitValues(values: Recordable) {
const payload = { ...values };
payload.isRubber = isMaterialRawAuxSubCategory(payload.pid) ? toIsRubberFlag(payload.isRubber) : '0';
return payload;
}
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
await resetFields();
setModalProps({ confirmLoading: false });
isUpdate.value = !!data?.isUpdate;
await ensureMaterialRawAuxCategoryId();
const tree = await buildPidTree();
if (!tree.length) {
createMessage.warning('未加载到物料分类树,请确认分类字典根编码 XSLMES_MATERIAL 已存在');
@@ -89,7 +125,7 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data
if (unref(isUpdate) && data?.record?.id) {
const cat = await defHttp.get<Recordable>({ url: '/sys/category/queryById', params: { id: data.record.id } });
await setFieldsValue({ id: cat.id, pid: cat.pid, name: cat.name });
await setFieldsValue({ id: cat.id, pid: cat.pid, name: cat.name, isRubber: fromIsRubberFlag(cat.isRubber) });
} else {
const pid = data?.parentId != null && data.parentId !== '' ? String(data.parentId) : '';
await setFieldsValue({ pid: pid || undefined, name: '' });
@@ -100,13 +136,13 @@ const title = computed(() => (unref(isUpdate) ? '编辑物料分类' : '新增
async function handleSubmit() {
try {
const values = await validate();
const values = normalizeSubmitValues(await validate());
setModalProps({ confirmLoading: true });
if (unref(isUpdate)) {
const full = await defHttp.get<Recordable>({ url: '/sys/category/queryById', params: { id: values.id } });
await editMaterialSysCategory({ ...full, pid: values.pid, name: values.name });
await editMaterialSysCategory({ ...full, pid: values.pid, name: values.name, isRubber: values.isRubber });
} else {
await saveMaterialSysCategory({ pid: values.pid, name: values.name });
await saveMaterialSysCategory({ pid: values.pid, name: values.name, isRubber: values.isRubber });
}
closeModal();
emit('success');

View File

@@ -0,0 +1,22 @@
import { ref } from 'vue';
/** MES 物料分类 - 原辅材料编码 */
export const MATERIAL_RAW_AUX_CODE = 'XSLMES_MATERIAL_RAW_AUX';
/** 原辅材料分类节点 ID运行时加载 */
export const materialRawAuxCategoryId = ref('');
/** 是否为原辅材料的直接子类 */
export function isMaterialRawAuxSubCategory(pid?: string) {
return !!materialRawAuxCategoryId.value && pid === materialRawAuxCategoryId.value;
}
/** 表单 Checkbox 布尔值 -> 数据库存储值 */
export function toIsRubberFlag(value: unknown) {
return value === true || value === '1' ? '1' : '0';
}
/** 数据库存储值 -> 表单 Checkbox 布尔值 */
export function fromIsRubberFlag(value: unknown) {
return value === '1' || value === 1 || value === true;
}

View File

@@ -1,5 +1,6 @@
import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table';
import { isMaterialRawAuxSubCategory } from './category.constants';
export const columns: BasicColumn[] = [
{
@@ -73,4 +74,13 @@ export const formSchema: FormSchema[] = [
placeholder: '留空将按规则自动生成(如 A01.A02',
},
},
{
label: ' ',
field: 'isRubber',
component: 'Checkbox',
defaultValue: false,
renderComponentContent: '胶料',
colProps: { span: 24 },
show: ({ values }) => isMaterialRawAuxSubCategory(values.pid),
},
];

View File

@@ -9,12 +9,40 @@
import { BasicForm, useForm } from '/src/components/Form';
import { formSchema } from '../category.data';
import { loadTreeData, saveOrUpdateDict } from '../category.api';
import { defHttp } from '/@/utils/http/axios';
import {
MATERIAL_RAW_AUX_CODE,
materialRawAuxCategoryId,
isMaterialRawAuxSubCategory,
toIsRubberFlag,
fromIsRubberFlag,
} from '../category.constants';
import type { Recordable } from '/@/types/global';
// 获取emit
const emit = defineEmits(['register', 'success']);
const isUpdate = ref(true);
const expandedRowKeys = ref([]);
const treeData = ref([]);
const isSubAdd = ref(false);
async function ensureMaterialRawAuxCategoryId() {
if (materialRawAuxCategoryId.value) {
return;
}
const res = await defHttp.get(
{ url: '/sys/category/loadOne', params: { field: 'code', val: MATERIAL_RAW_AUX_CODE } },
{ isTransformResponse: false },
);
if (res?.success && res?.result?.id) {
materialRawAuxCategoryId.value = res.result.id;
}
}
function normalizeSubmitValues(values: Recordable) {
const payload = { ...values };
payload.isRubber = isMaterialRawAuxSubCategory(payload.pid) ? toIsRubberFlag(payload.isRubber) : '0';
return payload;
}
//表单配置
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema }] = useForm({
schemas: formSchema,
@@ -35,12 +63,14 @@
expandedRowKeys.value = [];
setModalProps({ confirmLoading: false, minHeight: 80 });
isUpdate.value = !!data?.isUpdate;
await ensureMaterialRawAuxCategoryId();
// 代码逻辑说明: 分类字典data.record为空报错------------
isSubAdd.value = !data?.isUpdate && data.record && data.record.id;
if (data?.record) {
//表单赋值
await setFieldsValue({
...data.record,
isRubber: fromIsRubberFlag(data.record.isRubber),
});
}
//父级节点树信息
@@ -76,6 +106,7 @@
async function handleSubmit() {
try {
let values = await validate();
values = normalizeSubmitValues(values);
setModalProps({ confirmLoading: true });
//提交表单
await saveOrUpdateDict(values, isUpdate.value);

View File

@@ -0,0 +1,76 @@
<template>
<BasicModal v-bind="$attrs" title="选择机台(可多选)" :width="1000" @register="registerModal" @ok="handleOk">
<BasicTable @register="registerTable" />
</BasicModal>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicTable, useTable } from '/@/components/Table';
import { list } from '/@/views/xslmes/mesXslEquipmentLedger/MesXslEquipmentLedger.api';
const emit = defineEmits(['register', 'select']);
const selectedRows = ref<Recordable[]>([]);
function handleSelectionChange(_keys: string[], rows: Recordable[]) {
selectedRows.value = rows || [];
}
const [registerTable, { reload, getSelectRows, setSelectedRowKeys, clearSelectedRowKeys }] = useTable({
api: list,
columns: [
{ title: '设备名称', dataIndex: 'equipmentName', width: 160 },
{ title: '设备编号', dataIndex: 'equipmentCode', width: 140 },
{ title: '设备类别', dataIndex: 'equipmentCategoryName', width: 120 },
{ title: '设备类型', dataIndex: 'equipmentTypeName', width: 120 },
],
rowKey: 'id',
useSearchForm: true,
formConfig: {
labelWidth: 90,
schemas: [
{ label: '设备名称', field: 'equipmentName', component: 'Input', colProps: { span: 8 } },
{ label: '设备编号', field: 'equipmentCode', component: 'Input', colProps: { span: 8 } },
],
},
pagination: { pageSize: 10 },
canResize: false,
showIndexColumn: false,
immediate: true,
rowSelection: {
type: 'checkbox',
columnWidth: 48,
onChange: handleSelectionChange,
},
clickToRowSelect: true,
});
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
selectedRows.value = [];
clearSelectedRowKeys?.();
setModalProps({ confirmLoading: false });
const preset = (data?.selectedMachines || []) as Array<{ machineId?: string }>;
const keys = preset.map((m) => m.machineId).filter(Boolean) as string[];
if (keys.length) {
setSelectedRowKeys?.(keys);
}
reload();
});
async function handleOk() {
const rows = ((getSelectRows?.() || []) as Recordable[]).length
? (getSelectRows?.() || []) as Recordable[]
: selectedRows.value;
const machines = rows
.filter((r) => r?.id)
.map((r) => ({
machineId: r.id,
machineName: r.equipmentName || '',
machineCode: r.equipmentCode || '',
}));
emit('select', machines);
closeModal();
}
</script>

View File

@@ -14,6 +14,8 @@ enum Api {
generateRubberCode = '/xslmes/mesXslFormulaSpec/generateRubberCode',
getRubberContentSetting = '/xslmes/mesXslFormulaSpec/getRubberContentSetting',
saveRubberContentSetting = '/xslmes/mesXslFormulaSpec/saveRubberContentSetting',
buildMixingGeneratePreview = '/xslmes/mesXslFormulaSpec/buildMixingGeneratePreview',
generateMixingSpec = '/xslmes/mesXslFormulaSpec/generateMixingSpec',
}
export const getExportUrl = Api.exportXls;
@@ -41,3 +43,9 @@ export const batchDelete = (params, handleSuccess) => {
};
export const saveOrUpdate = (params, isUpdate) => defHttp.post({ url: isUpdate ? Api.edit : Api.save, params });
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A38】配合示方生成混炼示方-----------
export const buildMixingGeneratePreview = (params) =>
defHttp.get({ url: Api.buildMixingGeneratePreview, params }, { successMessageMode: 'none' });
export const generateMixingSpec = (params) => defHttp.post({ url: Api.generateMixingSpec, params });
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A38】配合示方生成混炼示方-----------

View File

@@ -36,27 +36,40 @@
</a-button>
</a-dropdown>
<super-query :config="superQueryConfig" @search="handleSuperQuery" />
<a-button
type="primary"
v-auth="'xslmes:mes_xsl_mixing_spec:add'"
preIcon="ant-design:thunderbolt-outlined"
@click="handleGenerateMixing"
>
生成混炼示方
</a-button>
</template>
<template #action="{ record }">
<TableAction :actions="getTableActions(record)" :dropDownActions="getDropDownAction(record)" />
</template>
</BasicTable>
<MesXslFormulaSpecModal @register="registerModal" @success="handleSuccess" />
<MesXslFormulaGenerateMixingModal @register="registerGenerateMixingModal" @success="handleGenerateMixingSuccess" />
</div>
</template>
<script lang="ts" name="xslmes-mesXslFormulaSpec" setup>
import { reactive } from 'vue';
import { useMessage } from '/@/hooks/web/useMessage';
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import Icon from '/@/components/Icon';
import MesXslFormulaSpecModal from './components/MesXslFormulaSpecModal.vue';
import MesXslFormulaGenerateMixingModal from './components/MesXslFormulaGenerateMixingModal.vue';
import { columns, searchFormSchema, superQuerySchema } from './MesXslFormulaSpec.data';
import { list, deleteOne, batchDelete, getExportUrl, getImportUrl } from './MesXslFormulaSpec.api';
const { createMessage } = useMessage();
const queryParam = reactive<any>({});
const [registerModal, { openModal }] = useModal();
const [registerGenerateMixingModal, { openModal: openGenerateMixingModal }] = useModal();
const { tableContext, onExportXls, onImportXls } = useListPage({
tableProps: {
@@ -125,6 +138,19 @@
selectedRowKeys.value = [];
}
function handleGenerateMixing() {
const keys = selectedRowKeys.value || [];
if (keys.length !== 1) {
createMessage.warning('请勾选一条配合示方数据');
return;
}
openGenerateMixingModal(true, { formulaSpecId: keys[0] });
}
function handleGenerateMixingSuccess() {
selectedRowKeys.value = [];
}
function canEdit(record: Recordable) {
return !record?.status || record.status === 'compile';
}

View File

@@ -0,0 +1,193 @@
<template>
<BasicModal
v-bind="$attrs"
title="生成混炼示方"
:width="880"
destroyOnClose
@register="registerModal"
@ok="handleConfirm"
>
<a-spin :spinning="loading">
<div v-if="rubberName" class="generate-mixing-tip">
胶料名称<strong>{{ rubberName }}</strong>混合段数<strong>{{ mixingStages }}</strong>确认后将按预览行 × 机台生成混炼示方
</div>
<a-table
:columns="tableColumns"
:data-source="tableRows"
:pagination="false"
row-key="rowKey"
size="small"
bordered
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'machines'">
<div class="machine-cell">
<a-input
:value="formatMachineNames(record.machines)"
readonly
placeholder="请点击选择机台(可多选)"
class="machine-picker-input"
@click="openMachinePicker(record)"
/>
<a-button type="link" size="small" @click="openMachinePicker(record)">选择</a-button>
</div>
</template>
</template>
</a-table>
<div v-if="totalGenerateCount > 0" class="generate-mixing-summary">
预计生成 <strong>{{ totalGenerateCount }}</strong> 条混炼示方
</div>
</a-spin>
<MesXslEquipmentLedgerMultiSelectModal @register="registerMachineModal" @select="onMachineSelect" />
</BasicModal>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
import { BasicModal, useModal, useModalInner } from '/@/components/Modal';
import { useMessage } from '/@/hooks/web/useMessage';
import MesXslEquipmentLedgerMultiSelectModal from '/@/views/xslmes/mesXslEquipInspectConfig/components/MesXslEquipmentLedgerMultiSelectModal.vue';
import { buildMixingGeneratePreview, generateMixingSpec } from '../MesXslFormulaSpec.api';
const emit = defineEmits(['register', 'success']);
const { createMessage } = useMessage();
const loading = ref(false);
const formulaSpecId = ref('');
const rubberName = ref('');
const mixingStages = ref(0);
const tableRows = ref<Recordable[]>([]);
const editingRowKey = ref<number | null>(null);
function formatStageCountText(record: Recordable) {
const current = record?.stageIndex;
const total = mixingStages.value;
if (total > 0) {
return `${current ?? ''}/${total}`;
}
return current != null ? String(current) : '';
}
const tableColumns = [
{ title: '示方编号', dataIndex: 'specCode', width: 220 },
{ title: '段数', dataIndex: 'stageIndex', width: 72, customRender: ({ record }) => formatStageCountText(record) },
{ title: '机台', dataIndex: 'machines' },
];
const totalGenerateCount = computed(() =>
tableRows.value.reduce((sum, row) => sum + (row.machines?.length || 0), 0),
);
const [registerMachineModal, { openModal: openMachineModalInner }] = useModal();
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
formulaSpecId.value = data?.formulaSpecId || '';
tableRows.value = [];
rubberName.value = '';
mixingStages.value = 0;
setModalProps({ confirmLoading: false });
if (!formulaSpecId.value) {
createMessage.warning('未指定配合示方');
return;
}
loading.value = true;
try {
const preview = await buildMixingGeneratePreview({ formulaSpecId: formulaSpecId.value });
rubberName.value = preview?.rubberName || '';
mixingStages.value = preview?.mixingStages || 0;
tableRows.value = (preview?.items || []).map((item: Recordable) => ({
rowKey: item.rowKey,
specCode: item.specCode,
stageIndex: item.stageIndex,
aSegmentIndex: item.aSegmentIndex,
stepType: item.stepType,
machines: [],
}));
if (!tableRows.value.length) {
createMessage.warning('无可生成的混炼段,请检查混合段数');
}
} finally {
loading.value = false;
}
});
function formatMachineNames(machines: Recordable[] = []) {
return machines.map((m) => m.machineName).filter(Boolean).join('、');
}
function openMachinePicker(record: Recordable) {
editingRowKey.value = record.rowKey;
openMachineModalInner(true, { selectedMachines: record.machines || [] });
}
function onMachineSelect(machines: Recordable[]) {
const key = editingRowKey.value;
if (key == null) {
return;
}
const row = tableRows.value.find((r) => r.rowKey === key);
if (row) {
row.machines = machines || [];
}
editingRowKey.value = null;
}
async function handleConfirm() {
if (!formulaSpecId.value) {
createMessage.warning('未指定配合示方');
return;
}
if (!tableRows.value.length) {
createMessage.warning('无可生成的混炼段');
return;
}
const emptyMachine = tableRows.value.find((r) => !r.machines?.length);
if (emptyMachine) {
createMessage.warning(`请为示方「${emptyMachine.specCode}」选择机台`);
return;
}
setModalProps({ confirmLoading: true });
try {
const res = await generateMixingSpec({
formulaSpecId: formulaSpecId.value,
rows: tableRows.value.map((r) => ({
specCode: r.specCode,
stageIndex: r.stageIndex,
aSegmentIndex: r.aSegmentIndex,
stepType: r.stepType,
machines: (r.machines || []).map((m: Recordable) => ({
machineId: m.machineId,
machineName: m.machineName,
})),
})),
});
const count = Number(res?.count ?? 0);
createMessage.success(count > 0 ? `成功生成 ${count} 条混炼示方` : '生成成功');
closeModal();
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>
<style lang="less" scoped>
.generate-mixing-tip {
margin-bottom: 12px;
color: rgba(0, 0, 0, 0.65);
}
.machine-cell {
display: flex;
align-items: center;
gap: 4px;
.machine-picker-input {
flex: 1;
cursor: pointer;
}
}
.generate-mixing-summary {
margin-top: 12px;
text-align: right;
color: rgba(0, 0, 0, 0.85);
}
</style>

View File

@@ -8,6 +8,17 @@
@register="registerModal"
@ok="handleSubmit"
>
<template #insertFooter>
<a-button
v-if="showGenerateMixingBtn"
type="primary"
v-auth="'xslmes:mes_xsl_mixing_spec:add'"
preIcon="ant-design:thunderbolt-outlined"
@click="handleGenerateMixing"
>
生成混炼示方
</a-button>
</template>
<template #title>
<span class="formula-spec-modal-title">{{ title }}</span>
<a-tooltip title="含胶率物料小类设置">
@@ -390,6 +401,7 @@
@getSelectResult="onIssueDeptSelect"
/>
<MesXslFormulaRubberContentSettingModal @register="registerRubberContentSettingModal" @success="onRubberContentSettingSaved" />
<MesXslFormulaGenerateMixingModal @register="registerGenerateMixingModal" />
</BasicModal>
</template>
@@ -439,6 +451,7 @@
} from '../MesXslFormulaSpec.data';
import { saveOrUpdate, queryById, generateRubberCode as generateRubberCodeApi, getRubberContentSetting } from '../MesXslFormulaSpec.api';
import MesXslFormulaRubberContentSettingModal from './MesXslFormulaRubberContentSettingModal.vue';
import MesXslFormulaGenerateMixingModal from './MesXslFormulaGenerateMixingModal.vue';
import MesXslFormulaLineColumnSetting from './MesXslFormulaLineColumnSetting.vue';
import { queryById as queryMixerById } from '/@/views/mes/material/MesMixerMaterial.api';
import { buildUUID } from '/@/utils/uuid';
@@ -518,6 +531,7 @@
const [registerIssueNumberModal, { openModal: openIssueNumberModalInner }] = useModal();
const [registerIssueDeptModal, { openModal: openIssueDeptModalInner }] = useModal();
const [registerRubberContentSettingModal, { openModal: openRubberContentSettingModal }] = useModal();
const [registerGenerateMixingModal, { openModal: openGenerateMixingModal }] = useModal();
const issueDeptPickerValue = ref<string>('');
const rubberMaterialPickerId = ref<string>('');
const mixerPsCompilePickerId = ref<string>('');
@@ -1172,6 +1186,19 @@
return !unref(isUpdate) ? '新增配合示方' : '编辑配合示方';
});
const showGenerateMixingBtn = computed(
() => !showFooterFlag.value && unref(isUpdate) && !!loadedMainRecord.value?.id,
);
function handleGenerateMixing() {
const id = loadedMainRecord.value?.id;
if (!id) {
createMessage.warning('请先保存配合示方');
return;
}
openGenerateMixingModal(true, { formulaSpecId: id });
}
async function handleLineValueChange(event) {
const { row, column } = event || {};
if (!row || !column) {

View File

@@ -0,0 +1,91 @@
<template>
<BasicModal v-bind="$attrs" title="选择密炼机动作" :width="960" @register="registerModal" @ok="handleOk">
<BasicTable @register="registerTable" />
</BasicModal>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicTable, useTable } from '/@/components/Table';
import { list, queryById } from '../MesXslMixerAction.api';
import { columns as actionColumns, searchFormSchema } from '../MesXslMixerAction.data';
import { useMessage } from '/@/hooks/web/useMessage';
const emit = defineEmits(['register', 'select']);
const { createMessage } = useMessage();
const equipmentId = ref('');
const selectedRow = ref<Recordable | null>(null);
const [registerTable, { reload, getSelectRowKeys, getSelectRows, setSelectedRowKeys, clearSelectedRowKeys }] = useTable({
api: list,
columns: actionColumns,
rowKey: 'id',
useSearchForm: true,
formConfig: {
labelWidth: 90,
schemas: searchFormSchema,
},
pagination: { pageSize: 10 },
canResize: false,
showIndexColumn: false,
immediate: true,
beforeFetch: (params) => ({
...params,
equipmentId: equipmentId.value || undefined,
}),
rowSelection: {
type: 'radio',
columnWidth: 48,
onChange: (_keys, rows) => {
selectedRow.value = rows?.[0] ?? null;
},
},
clickToRowSelect: true,
});
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
equipmentId.value = (data?.equipmentId as string) || '';
selectedRow.value = null;
clearSelectedRowKeys?.();
setModalProps({ confirmLoading: false });
const actionId = data?.actionId as string | undefined;
if (actionId) {
setSelectedRowKeys?.([actionId]);
try {
const raw = await queryById({ id: actionId });
const row = (raw as Recordable)?.id != null ? raw : (raw as Recordable)?.result;
if (row) {
selectedRow.value = row;
}
} catch {
// 忽略
}
}
reload();
});
async function handleOk() {
const keys = (getSelectRowKeys?.() || []) as string[];
let row = selectedRow.value || ((getSelectRows?.() || []) as Recordable[])[0];
if (!row && keys.length) {
try {
const raw = await queryById({ id: keys[0] });
row = (raw as Recordable)?.id != null ? raw : (raw as Recordable)?.result;
} catch {
// 忽略
}
}
if (!row?.id) {
createMessage.warning('请选择一条密炼机动作');
return;
}
emit('select', {
actionId: row.id,
actionName: row.actionName || '',
actionCode: row.actionCode || '',
});
closeModal();
}
</script>

View File

@@ -0,0 +1,91 @@
<template>
<BasicModal v-bind="$attrs" title="选择密炼机条件" :width="960" @register="registerModal" @ok="handleOk">
<BasicTable @register="registerTable" />
</BasicModal>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicTable, useTable } from '/@/components/Table';
import { list, queryById } from '../MesXslMixerCondition.api';
import { columns as conditionColumns, searchFormSchema } from '../MesXslMixerCondition.data';
import { useMessage } from '/@/hooks/web/useMessage';
const emit = defineEmits(['register', 'select']);
const { createMessage } = useMessage();
const equipmentId = ref('');
const selectedRow = ref<Recordable | null>(null);
const [registerTable, { reload, getSelectRowKeys, getSelectRows, setSelectedRowKeys, clearSelectedRowKeys }] = useTable({
api: list,
columns: conditionColumns.slice(0, 4),
rowKey: 'id',
useSearchForm: true,
formConfig: {
labelWidth: 90,
schemas: searchFormSchema,
},
pagination: { pageSize: 10 },
canResize: false,
showIndexColumn: false,
immediate: true,
beforeFetch: (params) => ({
...params,
equipmentId: equipmentId.value || undefined,
}),
rowSelection: {
type: 'radio',
columnWidth: 48,
onChange: (_keys, rows) => {
selectedRow.value = rows?.[0] ?? null;
},
},
clickToRowSelect: true,
});
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
equipmentId.value = (data?.equipmentId as string) || '';
selectedRow.value = null;
clearSelectedRowKeys?.();
setModalProps({ confirmLoading: false });
const conditionId = data?.conditionId as string | undefined;
if (conditionId) {
setSelectedRowKeys?.([conditionId]);
try {
const raw = await queryById({ id: conditionId });
const row = (raw as Recordable)?.id != null ? raw : (raw as Recordable)?.result;
if (row) {
selectedRow.value = row;
}
} catch {
// 忽略
}
}
reload();
});
async function handleOk() {
const keys = (getSelectRowKeys?.() || []) as string[];
let row = selectedRow.value || ((getSelectRows?.() || []) as Recordable[])[0];
if (!row && keys.length) {
try {
const raw = await queryById({ id: keys[0] });
row = (raw as Recordable)?.id != null ? raw : (raw as Recordable)?.result;
} catch {
// 忽略
}
}
if (!row?.id) {
createMessage.warning('请选择一条密炼机条件');
return;
}
emit('select', {
conditionId: row.id,
conditionName: row.conditionName || '',
conditionCode: row.conditionCode || '',
});
closeModal();
}
</script>

View File

@@ -35,7 +35,7 @@ export const columns: BasicColumn[] = [
{ title: '机台', align: 'center', dataIndex: 'machineName', width: 120 },
{ title: '制作日期', align: 'center', dataIndex: 'makeDate', width: 120 },
{ title: '发行编号', align: 'center', dataIndex: 'issueNumber', width: 150 },
{ title: '段数', align: 'center', dataIndex: 'stageCount', width: 80 },
{ title: '段数', align: 'center', dataIndex: 'stageCount', width: 88 },
{ title: '纯混炼时间(秒)', align: 'center', dataIndex: 'pureMixSec', width: 130 },
{ title: '变更日期', align: 'center', dataIndex: 'changeDate', width: 120 },
{ title: '修改时间', align: 'center', dataIndex: 'updateTime', width: 165 },
@@ -49,18 +49,21 @@ export const searchFormSchema: FormSchema[] = [
export const mainSchema: FormSchema[] = [
{ label: '', field: 'id', component: 'Input', show: false },
{ label: '', field: 'machineId', component: 'Input', show: false },
{ label: '规格', field: 'specName', component: 'Input', required: true, colProps: { span: 8 } },
{ label: '用途', field: 'purpose', component: 'AutoComplete', required: true, colProps: { span: 8 } },
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A33】混炼示方基本信息字段优化-----------
{ label: '用途', field: 'purpose', component: 'Input', required: true, colProps: { span: 8 } },
{ label: '机台', field: 'machineName', component: 'Input', colProps: { span: 8 } },
{ label: '制作日期', field: 'makeDate', component: 'DatePicker', colProps: { span: 8 }, componentProps: { valueFormat: 'YYYY-MM-DD' } },
{ label: '发行编号', field: 'issueNumber', component: 'AutoComplete', required: true, colProps: { span: 8 } },
{ label: '发行编号', field: 'issueNumber', component: 'Input', required: true, colProps: { span: 8 } },
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A33】混炼示方基本信息字段优化-----------
{ label: '换算系数', field: 'convertFactor', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 6, style: { width: '100%' } } },
{ label: '填充体积', field: 'fillVolume', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 6, style: { width: '100%' } } },
{ label: '回收炭黑(秒)', field: 'recycleCarbonSec', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 0, style: { width: '100%' } } },
{ label: '母胶比重', field: 'motherRubberSg', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 6, style: { width: '100%' } } },
{ label: '终炼胶比重', field: 'finalRubberSg', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 6, style: { width: '100%' } } },
{ label: '适用工厂', field: 'applyFactory', component: 'Input', colProps: { span: 8 } },
{ label: '段数', field: 'stageCount', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 0, style: { width: '100%' } } },
{ label: '段数', field: 'stageCount', component: 'Input', colProps: { span: 8 }, componentProps: { placeholder: '如 2/3' } },
{ label: '纯混炼时间(秒)', field: 'pureMixSec', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 0, style: { width: '100%' } } },
{ label: '回收炭黑(KG)', field: 'recycleCarbonKg', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 6, style: { width: '100%' } } },
{ label: '自动小料打印设定', field: 'autoSmallPrintSetting', component: 'Input', colProps: { span: 8 } },
@@ -198,13 +201,32 @@ export function calcMixingMaterialTableWidth(columns: JVxeColumn[], widthMap: Re
export const MIXING_STEP_MIN_COLUMN_WIDTH = 48;
export const stepColumns: JVxeColumn[] = [
{ title: '动作', key: 'actionName', type: JVxeTypes.input, width: 120, minWidth: MIXING_STEP_MIN_COLUMN_WIDTH },
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A35】混合步骤动作/组合列常显下拉倒三角-----------
{
title: '动作',
key: 'actionName',
type: JVxeTypes.slot,
slotName: 'actionNameSlot',
width: 120,
minWidth: MIXING_STEP_MIN_COLUMN_WIDTH,
},
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A35】混合步骤动作/组合列常显下拉倒三角-----------
{ title: '时间(")', key: 'actionSec', type: JVxeTypes.inputNumber, width: 80, minWidth: MIXING_STEP_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '保护时间', key: 'protectSec', type: JVxeTypes.inputNumber, width: 80, minWidth: MIXING_STEP_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '温度(℃)', key: 'tempC', type: JVxeTypes.inputNumber, width: 80, minWidth: MIXING_STEP_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '功率(Kw)', key: 'powerKw', type: JVxeTypes.inputNumber, width: 80, minWidth: MIXING_STEP_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '能量(Kwh)', key: 'energyKwh', type: JVxeTypes.inputNumber, width: 80, minWidth: MIXING_STEP_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '组合', key: 'comboMode', type: JVxeTypes.input, width: 72, minWidth: MIXING_STEP_MIN_COLUMN_WIDTH, align: 'center' },
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A35】混合步骤动作/组合列常显下拉倒三角-----------
{
title: '组合',
key: 'comboMode',
type: JVxeTypes.slot,
slotName: 'comboModeSlot',
width: 72,
minWidth: MIXING_STEP_MIN_COLUMN_WIDTH,
align: 'center',
},
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A35】混合步骤动作/组合列常显下拉倒三角-----------
{ title: '转速(rpm)', key: 'speedRpm', type: JVxeTypes.inputNumber, width: 80, minWidth: MIXING_STEP_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '压力(Mpa)', key: 'pressureMpa', type: JVxeTypes.inputNumber, width: 80, minWidth: MIXING_STEP_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '栓(%)', key: 'boltPercent', type: JVxeTypes.inputNumber, width: 72, minWidth: MIXING_STEP_MIN_COLUMN_WIDTH, align: 'center' },
@@ -281,7 +303,9 @@ export const MIXING_TCU_COLUMN_WIDTH_CACHE_KEY = 'mes_xsl_mixing_spec_tcu_column
export const MIXING_TCU_MIN_COLUMN_WIDTH = 48;
export const tcuColumns: JVxeColumn[] = [
{ title: '区分', key: 'sectionType', type: JVxeTypes.select, dictCode: 'xslmes_mixing_tcu_section', width: 96, minWidth: MIXING_TCU_MIN_COLUMN_WIDTH, align: 'center' },
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A33】TCU区分固定上/下密炼机-----------
{ title: '区分', key: 'sectionType', type: JVxeTypes.select, dictCode: 'xslmes_mixing_tcu_section', disabled: true, width: 96, minWidth: MIXING_TCU_MIN_COLUMN_WIDTH, align: 'center' },
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A33】TCU区分固定上/下密炼机-----------
{ title: '前转子温度', key: 'frontRotorTemp', type: JVxeTypes.inputNumber, width: 76, minWidth: MIXING_TCU_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '后转子温度', key: 'rearRotorTemp', type: JVxeTypes.inputNumber, width: 76, minWidth: MIXING_TCU_MIN_COLUMN_WIDTH, align: 'center' },
{ title: '前混炼室温度', key: 'frontChamberTemp', type: JVxeTypes.inputNumber, width: 76, minWidth: MIXING_TCU_MIN_COLUMN_WIDTH, align: 'center' },
@@ -580,8 +604,36 @@ export function createEmptyDownStepRows(count = DEFAULT_MIXING_DOWN_STEP_ROW_COU
return createEmptyMixingRows(count);
}
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A33】TCU默认两行及上密炼机药品称默认值-----------
/** 上密炼机 TCU 行默认药品称量位置(药品称) */
export const MIXING_TCU_UP_MIXER_DRUG_WEIGH_POS = 'drug_scale';
/** 构建 TCU 温度条件默认两行(上密炼机/下密炼机) */
export function buildDefaultMixingTcuRows(rows: Recordable[] = []): Recordable[] {
const up =
rows.find((r) => r.sectionType === 'up_mixer') ||
({ sectionType: 'up_mixer', drugWeighPos: MIXING_TCU_UP_MIXER_DRUG_WEIGH_POS } as Recordable);
if (up.sectionType === 'up_mixer' && !up.drugWeighPos) {
up.drugWeighPos = MIXING_TCU_UP_MIXER_DRUG_WEIGH_POS;
}
const down = rows.find((r) => r.sectionType === 'down_mixer') || ({ sectionType: 'down_mixer', drugWeighPos: undefined } as Recordable);
return [up, down];
}
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A33】TCU默认两行及上密炼机药品称默认值-----------
/** 确保明细行具备唯一 idJVxeTable 依赖 rowKey=id */
export function normalizeMixingDetailRows(rows: Recordable[] = []): Recordable[] {
return (rows || []).map((row) => ({ ...row, id: row?.id || buildUUID() }));
}
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A39】编辑页明细补齐默认空行与新增一致-----------
/** 编辑/详情加载时补齐明细默认空行(不少于 defaultCount已有数据行保留 */
export function ensureMixingDetailRows(rows: Recordable[] = [], defaultCount: number): Recordable[] {
const normalized = normalizeMixingDetailRows(rows);
if (normalized.length >= defaultCount) {
return normalized;
}
return [...normalized, ...createEmptyMixingRows(defaultCount - normalized.length)];
}
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A39】编辑页明细补齐默认空行与新增一致-----------
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A22】明细表默认空行数-----------

View File

@@ -28,22 +28,28 @@
</td>
<th class="formTitle" colspan="1">发行编号</th>
<td class="formValue" colspan="3">
<a-select
v-model:value="sheetForm.issueNumber"
show-search
allow-clear
:options="issueNumberOptions"
<a-input
:value="sheetForm.issueNumber"
readonly
placeholder="请点击选择密炼PS"
:disabled="!showFooter"
:filter-option="selectFilterOption"
:bordered="false"
class="form-input"
style="width: 100%"
:class="['form-input', 'mixing-picker-input', { 'is-filled': !!sheetForm.issueNumber }]"
@click="openIssueNumberPicker"
/>
</td>
</tr>
<tr>
<td class="formValue" colspan="2" rowspan="2">
<a-input v-model:value="sheetForm.machineName" :disabled="!showFooter" :bordered="false" class="form-input" />
<a-input
:value="sheetForm.machineName"
readonly
placeholder="请点击选择设备台账"
:disabled="!showFooter"
:bordered="false"
:class="['form-input', 'mixing-picker-input', { 'is-filled': !!sheetForm.machineName }]"
@click="openMachinePicker"
/>
</td>
</tr>
<tr>
@@ -63,16 +69,13 @@
<tr>
<th class="formTitle required" rowspan="2">用途</th>
<td class="formValue" colspan="3" rowspan="2">
<a-select
<a-input
v-model:value="sheetForm.purpose"
show-search
placeholder="请输入用途"
allow-clear
:options="purposeOptions"
:disabled="!showFooter"
:filter-option="selectFilterOption"
:bordered="false"
class="form-input"
style="width: 100%"
/>
</td>
<th class="formTitle" colspan="1">母胶比重</th>
@@ -81,7 +84,13 @@
</td>
<th class="formTitle">段数</th>
<td class="formValue" colspan="2">
<a-input-number v-model:value="sheetForm.stageCount" :disabled="!showFooter" :precision="0" :bordered="false" class="form-input" style="width: 100%" />
<a-input
v-model:value="sheetForm.stageCount"
placeholder="如 2/3"
:disabled="!showFooter"
:bordered="false"
class="form-input"
/>
</td>
<th class="formTitle" colspan="1">纯混炼时间()</th>
<td class="formValue">
@@ -253,7 +262,28 @@
:disabled="!showFooter"
@resizable-change="handleStepColumnResize"
@column-resizable-change="handleStepColumnResize"
/>
>
<template #actionNameSlot="{ row }">
<MesXslMixingStepSelectCell
:row="row"
field="actionName"
:options="mixerActionSelectOptions"
:disabled="!showFooter"
:machine-id="sheetForm.machineId"
:placeholder="stepSelectPlaceholder"
/>
</template>
<template #comboModeSlot="{ row }">
<MesXslMixingStepSelectCell
:row="row"
field="comboMode"
:options="mixerConditionSelectOptions"
:disabled="!showFooter"
:machine-id="sheetForm.machineId"
:placeholder="stepSelectPlaceholder"
/>
</template>
</JVxeTable>
</div>
<!--update-end---author:cursor ---date:20260522 forXSLMES-20260522-A21混合步骤与下密炼机列宽同步可调----------- -->
</div>
@@ -282,7 +312,28 @@
:disabled="!showFooter"
@resizable-change="handleStepColumnResize"
@column-resizable-change="handleStepColumnResize"
/>
>
<template #actionNameSlot="{ row }">
<MesXslMixingStepSelectCell
:row="row"
field="actionName"
:options="mixerActionSelectOptions"
:disabled="!showFooter"
:machine-id="sheetForm.machineId"
:placeholder="stepSelectPlaceholder"
/>
</template>
<template #comboModeSlot="{ row }">
<MesXslMixingStepSelectCell
:row="row"
field="comboMode"
:options="mixerConditionSelectOptions"
:disabled="!showFooter"
:machine-id="sheetForm.machineId"
:placeholder="stepSelectPlaceholder"
/>
</template>
</JVxeTable>
</div>
</div>
</div>
@@ -317,13 +368,17 @@
</div>
<BasicForm v-show="false" @register="registerForm" />
<MesXslEquipmentLedgerSelectModal @register="registerMachineModal" @select="onMachineSelect" />
<MesXslMixerPsCompileSelectModal @register="registerIssueNumberModal" @select="onIssueNumberSelect" />
</div>
</BasicModal>
</template>
<script lang="ts" setup>
import { computed, reactive, ref, unref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import dayjs from 'dayjs';
import { BasicModal, useModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { useMessage } from '/@/hooks/web/useMessage';
import { useUserStore } from '/@/store/modules/user';
@@ -351,14 +406,23 @@ import {
createEmptyMaterialRows,
createEmptyStepRows,
createEmptyDownStepRows,
normalizeMixingDetailRows,
ensureMixingDetailRows,
DEFAULT_MIXING_MATERIAL_ROW_COUNT,
DEFAULT_MIXING_STEP_ROW_COUNT,
DEFAULT_MIXING_DOWN_STEP_ROW_COUNT,
buildDefaultMixingTcuRows,
MIXING_MATERIAL_MIN_COLUMN_WIDTH,
MIXING_TCU_MIN_COLUMN_WIDTH,
MIXING_STEP_MIN_COLUMN_WIDTH,
} from '../MesXslMixingSpec.data';
import { saveOrUpdate, queryById, queryIssueNumberOptions, queryPurposeOptions } from '../MesXslMixingSpec.api';
import { saveOrUpdate, queryById } from '../MesXslMixingSpec.api';
import MesXslMixingMaterialColumnSetting from './MesXslMixingMaterialColumnSetting.vue';
import MesXslMixingTableRowHeightSetting from './MesXslMixingTableRowHeightSetting.vue';
import MesXslMixingStepSelectCell from './MesXslMixingStepSelectCell.vue';
import { list as mixerActionList } from '/@/views/xslmes/mesXslMixerAction/MesXslMixerAction.api';
import { list as mixerConditionList } from '/@/views/xslmes/mesXslMixerCondition/MesXslMixerCondition.api';
import MesXslEquipmentLedgerSelectModal from '/@/views/xslmes/mesXslEquipInspectConfig/components/MesXslEquipmentLedgerSelectModal.vue';
import MesXslMixerPsCompileSelectModal from '/@/views/xslmes/mesXslMixerPsCompile/components/MesXslMixerPsCompileSelectModal.vue';
const emit = defineEmits(['register', 'success']);
const { createMessage } = useMessage();
@@ -366,8 +430,11 @@ const userStore = useUserStore();
const isUpdate = ref(false);
const showFooter = ref(true);
const issueNumberOptions = ref<{ label: string; value: string }[]>([]);
const purposeOptions = ref<{ label: string; value: string }[]>([]);
const mixerPsCompilePickerId = ref('');
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A34】混合步骤动作/组合下拉选项-----------
const mixerActionOptions = ref<{ title: string; value: string }[]>([]);
const mixerConditionOptions = ref<{ title: string; value: string }[]>([]);
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A34】混合步骤动作/组合下拉选项-----------
const materialRef = ref();
const stepRef = ref();
@@ -414,10 +481,12 @@ const tcuTableLayoutKey = computed(
() => `${tcuHeightPref.value.rowHeight}-${tcuHeightPref.value.visibleRowCount}-${tcuTableWidth.value}`,
);
const stepTableLayoutKey = computed(
() => `${stepHeightPref.value.rowHeight}-${stepHeightPref.value.visibleRowCount}-${visibleStepColumns.value.length}`,
() =>
`${stepHeightPref.value.rowHeight}-${stepHeightPref.value.visibleRowCount}-${mixerActionOptions.value.length}-${mixerConditionOptions.value.length}`,
);
const downStepTableLayoutKey = computed(
() => `${downStepHeightPref.value.rowHeight}-${downStepHeightPref.value.visibleRowCount}-${visibleStepColumns.value.length}`,
() =>
`${downStepHeightPref.value.rowHeight}-${downStepHeightPref.value.visibleRowCount}-${mixerActionOptions.value.length}-${mixerConditionOptions.value.length}`,
);
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A32】四明细表行高/展示行数设置持久化-----------
@@ -478,8 +547,58 @@ function handleTcuColumnResize(params: Recordable) {
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A21】混合步骤与下密炼机列宽同步可调-----------
const stepColumnWidths = ref<Record<string, number>>(loadMixingStepColumnWidths());
function extractPageRecords(raw: Recordable) {
if (Array.isArray(raw?.records)) {
return raw.records;
}
if (Array.isArray(raw?.result?.records)) {
return raw.result.records;
}
if (Array.isArray(raw)) {
return raw;
}
return [];
}
function buildMixerSelectOptions(records: Recordable[], labelKey: string) {
const seen = new Set<string>();
const options: { title: string; value: string }[] = [];
records.forEach((item) => {
const label = String(item?.[labelKey] || '').trim();
if (!label || seen.has(label)) {
return;
}
seen.add(label);
options.push({ title: label, value: label });
});
return options;
}
async function loadMixerStepOptions(equipmentId?: string) {
if (!equipmentId) {
mixerActionOptions.value = [];
mixerConditionOptions.value = [];
return;
}
const params = { equipmentId, pageNo: 1, pageSize: 500 };
const [actionRaw, conditionRaw] = await Promise.all([mixerActionList(params), mixerConditionList(params)]);
mixerActionOptions.value = buildMixerSelectOptions(extractPageRecords(actionRaw), 'actionName');
mixerConditionOptions.value = buildMixerSelectOptions(extractPageRecords(conditionRaw), 'conditionName');
}
const visibleStepColumns = computed(() => applyMixingStepColumnWidths(stepColumns, stepColumnWidths.value));
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A35】混合步骤动作/组合列常显下拉倒三角-----------
const mixerActionSelectOptions = computed(() =>
mixerActionOptions.value.map((item) => ({ label: item.title, value: item.value })),
);
const mixerConditionSelectOptions = computed(() =>
mixerConditionOptions.value.map((item) => ({ label: item.title, value: item.value })),
);
const stepSelectPlaceholder = computed(() => '');
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A35】混合步骤动作/组合列常显下拉倒三角-----------
function handleStepColumnResize(params: Recordable) {
const column = params?.resizeColumn ?? params?.column;
const key = column?.params?.key ?? column?.field;
@@ -505,6 +624,7 @@ const sheetForm = reactive<Recordable>({
id: '',
specName: '',
purpose: '',
machineId: '',
machineName: '',
makeDate: '',
issueNumber: '',
@@ -514,7 +634,7 @@ const sheetForm = reactive<Recordable>({
motherRubberSg: null,
finalRubberSg: null,
applyFactory: '',
stageCount: null,
stageCount: '',
pureMixSec: null,
recycleCarbonKg: null,
autoSmallPrintSetting: '',
@@ -587,27 +707,43 @@ const [registerForm, { resetFields, setFieldsValue, validate, setProps }] = useF
baseColProps: { span: 8 },
});
const selectFilterOption = (input: string, option: any) => String(option?.label || '').toLowerCase().includes(input.toLowerCase());
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A33】混炼示方主表选择弹窗-----------
const [registerMachineModal, { openModal: openMachineModalInner }] = useModal();
const [registerIssueNumberModal, { openModal: openIssueNumberModalInner }] = useModal();
async function applyAutoCompleteSource() {
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A17】混炼示方用途/发行编号联想-----------
const issueRows = await queryIssueNumberOptions({});
const purposeRows = await queryPurposeOptions({});
const toOptions = (rows: any) => {
const list = Array.isArray(rows) ? rows : rows?.result || [];
return list.map((item) => ({ label: item.label, value: item.value }));
};
issueNumberOptions.value = toOptions(issueRows);
purposeOptions.value = toOptions(purposeRows);
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A17】混炼示方用途/发行编号联想-----------
function openMachinePicker() {
if (!showFooter.value) {
return;
}
openMachineModalInner(true, { equipmentLedgerId: sheetForm.machineId || '' });
}
async function onMachineSelect(payload: Recordable | null) {
sheetForm.machineId = payload?.equipmentLedgerId || '';
sheetForm.machineName = payload?.equipmentName || '';
await loadMixerStepOptions(sheetForm.machineId);
}
function openIssueNumberPicker() {
if (!showFooter.value) {
return;
}
openIssueNumberModalInner(true, { psCompileId: mixerPsCompilePickerId.value || '' });
}
function onIssueNumberSelect(payload: Recordable | null) {
if (!payload) {
return;
}
mixerPsCompilePickerId.value = payload.psCompileId || '';
sheetForm.issueNumber = payload.psCode || '';
}
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A33】混炼示方主表选择弹窗-----------
function ensureTcuDefaultRows(rows: Recordable[] = []) {
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A17】TCU子表默认固定两行-----------
const up = rows.find((r) => r.sectionType === 'up_mixer') || { sectionType: 'up_mixer' };
const down = rows.find((r) => r.sectionType === 'down_mixer') || { sectionType: 'down_mixer', drugWeighPos: undefined };
return [up, down];
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A17】TCU子表默认固定两行-----------
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A33】TCU默认两行及上密炼机药品称默认值-----------
return buildDefaultMixingTcuRows(rows);
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A33】TCU默认两行及上密炼机药品称默认值-----------
}
function handleTcuValueChange(event) {
@@ -629,8 +765,10 @@ function resetSheetForm() {
sheetForm[key] =
key === 'specName' ||
key === 'purpose' ||
key === 'machineId' ||
key === 'machineName' ||
key === 'issueNumber' ||
key === 'stageCount' ||
key.endsWith('By') ||
key === 'applyFactory' ||
key === 'autoSmallPrintSetting'
@@ -644,6 +782,7 @@ function resetSheetForm() {
sheetForm.auditTime = '';
sheetForm.approveTime = '';
sheetForm.changeDate = '';
mixerPsCompilePickerId.value = '';
refreshSignDisplay({});
}
@@ -670,25 +809,29 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data
stepData.value = [];
downStepData.value = [];
tcuData.value = ensureTcuDefaultRows([]);
await loadMixerStepOptions('');
isUpdate.value = !!data?.isUpdate;
showFooter.value = !!data?.showFooter;
await setProps({ disabled: !showFooter.value });
setModalProps({ showOkBtn: showFooter.value, showCancelBtn: showFooter.value, confirmLoading: false });
await applyAutoCompleteSource();
if (isUpdate.value && data?.record?.id) {
const raw = await queryById({ id: data.record.id });
const row = raw?.result || raw;
Object.assign(sheetForm, row || {});
refreshSignDisplay(row || {});
await loadMixerStepOptions(sheetForm.machineId);
await syncSheetToForm();
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A22】明细表默认空行数-----------
materialData.value = normalizeMixingDetailRows(row?.materialList || []);
stepData.value = normalizeMixingDetailRows(row?.stepList || []);
downStepData.value = normalizeMixingDetailRows(row?.downStepList || []);
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A22】明细表默认空行数-----------
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A39】编辑页明细补齐默认空行与新增一致-----------
materialData.value = ensureMixingDetailRows(row?.materialList || [], DEFAULT_MIXING_MATERIAL_ROW_COUNT);
stepData.value = ensureMixingDetailRows(row?.stepList || [], DEFAULT_MIXING_STEP_ROW_COUNT);
downStepData.value = ensureMixingDetailRows(row?.downStepList || [], DEFAULT_MIXING_DOWN_STEP_ROW_COUNT);
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A39】编辑页明细补齐默认空行与新增一致-----------
tcuData.value = ensureTcuDefaultRows(row?.tcuList || []);
} else {
const userInfo = userStore.getUserInfo || {};
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A33】新增混炼示方制作日期默认当天-----------
sheetForm.makeDate = dayjs().format('YYYY-MM-DD');
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A33】新增混炼示方制作日期默认当天-----------
refreshSignDisplay({
createBy_dictText: userInfo.realname,
createBy: userInfo.username,
@@ -856,6 +999,15 @@ async function handleSubmit() {
:deep(.form-input.ant-picker-suffix) {
color: #999;
}
:deep(.mixing-picker-input) {
cursor: pointer;
}
:deep(.mixing-picker-input.is-filled) {
color: #262626;
}
}
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A17】顶部施工表对齐旧系统13列表格-----------
@@ -1099,6 +1251,20 @@ async function handleSubmit() {
font-weight: 600;
}
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A35】混合步骤动作/组合列常显下拉倒三角-----------
.panel-right :deep(.vxe-body--column.col--actionName),
.panel-right :deep(.vxe-body--column.col--comboMode) {
padding: 0 !important;
overflow: visible !important;
}
.panel-right :deep(.vxe-body--column.col--actionName .vxe-cell),
.panel-right :deep(.vxe-body--column.col--comboMode .vxe-cell) {
padding: 0 !important;
overflow: visible !important;
}
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A35】混合步骤动作/组合列常显下拉倒三角-----------
.sheet-sign-footer {
border-top: 1px solid #d9d9d9;
background: #fff;
@@ -1171,4 +1337,10 @@ async function handleSubmit() {
background: #fcfdfd;
}
}
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A36】动作/组合下拉挂到body避免被表格裁切-----------
.mixing-step-select-dropdown {
z-index: 2100 !important;
}
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A36】动作/组合下拉挂到body避免被表格裁切-----------
</style>

View File

@@ -0,0 +1,109 @@
<template>
<a-select
:value="row[field]"
:options="options"
:disabled="disabled"
:open="dropdownOpen"
allow-clear
show-search
:bordered="false"
size="small"
class="mixing-step-select"
popup-class-name="mixing-step-select-dropdown"
:placeholder="placeholder"
:get-popup-container="getPopupContainer"
@update:value="handleChange"
@dropdown-visible-change="handleDropdownVisibleChange"
@click.stop
/>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { useMessage } from '/@/hooks/web/useMessage';
const props = defineProps<{
row: Recordable;
field: string;
options: { label: string; value: string }[];
disabled?: boolean;
placeholder?: string;
machineId?: string;
}>();
const { createMessage } = useMessage();
const dropdownOpen = ref(false);
function handleChange(value: string | undefined) {
props.row[props.field] = value;
}
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A37】未选机台点击动作/组合提示请先选择机台-----------
function handleDropdownVisibleChange(visible: boolean) {
if (props.disabled) {
dropdownOpen.value = false;
return;
}
if (visible && !props.machineId) {
createMessage.warning('请先选择机台');
dropdownOpen.value = false;
return;
}
dropdownOpen.value = visible;
}
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A37】未选机台点击动作/组合提示请先选择机台-----------
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A36】动作/组合下拉挂到body避免被表格裁切-----------
function getPopupContainer() {
return document.body;
}
//update-end---author:cursor ---date:20260522 for【XSLMES-20260522-A36】动作/组合下拉挂到body避免被表格裁切-----------
</script>
<style lang="less" scoped>
.mixing-step-select {
width: 100%;
height: 100%;
display: flex;
align-items: center;
:deep(.ant-select) {
width: 100%;
height: 100%;
}
:deep(.ant-select-selector) {
border: none !important;
box-shadow: none !important;
background: transparent !important;
padding: 0 18px 0 2px !important;
min-height: 24px !important;
height: 100% !important;
display: flex !important;
align-items: center !important;
}
:deep(.ant-select-selection-item),
:deep(.ant-select-selection-placeholder) {
line-height: 1.2 !important;
text-align: center;
padding-inline-end: 0 !important;
}
:deep(.ant-select-selection-search-input) {
height: 100% !important;
}
:deep(.ant-select-arrow) {
right: 2px;
margin-top: -3px;
color: #666;
font-size: 10px;
pointer-events: none;
}
:deep(.ant-select-clear) {
right: 14px;
}
}
</style>