2026-05-07 17:53:48 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<BasicTable @register="registerTable" :rowSelection="rowSelection">
|
|
|
|
|
|
<template #tableTitle>
|
|
|
|
|
|
<a-button type="primary" v-auth="'xslmes:mes_xsl_raw_material_entry:add'" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
|
|
|
|
|
|
<a-button type="primary" v-auth="'xslmes:mes_xsl_raw_material_entry:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
|
|
|
|
|
|
<j-upload-button type="primary" v-auth="'xslmes:mes_xsl_raw_material_entry:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
|
2026-05-12 14:06:07 +08:00
|
|
|
|
<a-button type="primary" v-auth="'xslmes:mes_xsl_raw_material_entry:stockIn'" preIcon="ant-design:check-circle-outlined" @click="handleStockIn"> 结存入库</a-button>
|
2026-05-15 15:08:07 +08:00
|
|
|
|
<!-- 与原材料卡片一致:字典选桥接地址 + 打印机 + 刷新 -->
|
|
|
|
|
|
<JDictSelectTag
|
2026-05-13 17:25:13 +08:00
|
|
|
|
v-model:value="printDotWsUrl"
|
2026-05-15 15:08:07 +08:00
|
|
|
|
dictCode="xslmes_print_dot_ws"
|
|
|
|
|
|
:showChooseOption="false"
|
|
|
|
|
|
style="width: 280px; margin-left: 8px"
|
|
|
|
|
|
placeholder="选择打印桥接"
|
|
|
|
|
|
@change="onPrintDotWsUrlChange"
|
2026-05-13 17:25:13 +08:00
|
|
|
|
/>
|
2026-05-15 15:08:07 +08:00
|
|
|
|
<a-button v-if="!printDotConnected" style="margin-left: 8px" @click="downloadPrintPlugin">下载打印插件</a-button>
|
2026-05-13 17:25:13 +08:00
|
|
|
|
<a-select
|
|
|
|
|
|
v-model:value="selectedPrinterName"
|
|
|
|
|
|
:options="printerOptions"
|
|
|
|
|
|
style="width: 220px; margin-left: 8px"
|
|
|
|
|
|
allow-clear
|
|
|
|
|
|
show-search
|
|
|
|
|
|
option-filter-prop="label"
|
|
|
|
|
|
:placeholder="printerSelectPlaceholder"
|
|
|
|
|
|
/>
|
2026-05-15 15:08:07 +08:00
|
|
|
|
<a-button style="margin-left: 8px" @click="() => refreshPrinterOptions(true)">刷新打印机</a-button>
|
2026-05-13 17:25:13 +08:00
|
|
|
|
<a-button
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
ghost
|
|
|
|
|
|
v-auth="'xslmes:mes_xsl_raw_material_entry:edit'"
|
|
|
|
|
|
:loading="printLoading"
|
2026-05-15 15:08:07 +08:00
|
|
|
|
:disabled="selectedRowKeys.length === 0"
|
2026-05-13 17:25:13 +08:00
|
|
|
|
@click="handlePrintSelected"
|
|
|
|
|
|
>
|
|
|
|
|
|
<Icon icon="ant-design:printer-outlined" />
|
|
|
|
|
|
打印选中
|
|
|
|
|
|
</a-button>
|
2026-05-07 17:53:48 +08:00
|
|
|
|
<a-dropdown v-if="selectedRowKeys.length > 0">
|
|
|
|
|
|
<template #overlay>
|
|
|
|
|
|
<a-menu>
|
|
|
|
|
|
<a-menu-item key="1" @click="batchHandleDelete">
|
|
|
|
|
|
<Icon icon="ant-design:delete-outlined" />
|
|
|
|
|
|
删除
|
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
|
</a-menu>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<a-button v-auth="'xslmes:mes_xsl_raw_material_entry:deleteBatch'">批量操作
|
|
|
|
|
|
<Icon icon="mdi:chevron-down" />
|
|
|
|
|
|
</a-button>
|
|
|
|
|
|
</a-dropdown>
|
|
|
|
|
|
<super-query :config="superQueryConfig" @search="handleSuperQuery" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template #action="{ record }">
|
|
|
|
|
|
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</BasicTable>
|
|
|
|
|
|
<MesXslRawMaterialEntryModal @register="registerModal" @success="handleSuccess" />
|
2026-05-13 17:25:13 +08:00
|
|
|
|
<RawMaterialEntryPrintPreviewModal
|
|
|
|
|
|
v-model:open="printPreviewOpen"
|
|
|
|
|
|
:entry-id="printPreviewEntryId"
|
|
|
|
|
|
:barcode="printPreviewBarcode"
|
|
|
|
|
|
/>
|
2026-05-07 17:53:48 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script lang="ts" name="xslmes-mesXslRawMaterialEntry" setup>
|
2026-05-15 15:08:07 +08:00
|
|
|
|
import { onMounted, ref, reactive, watch, h } from 'vue';
|
2026-05-13 17:25:13 +08:00
|
|
|
|
import { Icon } from '/@/components/Icon';
|
|
|
|
|
|
import { BasicTable, TableAction } from '/@/components/Table';
|
2026-05-07 17:53:48 +08:00
|
|
|
|
import { useModal } from '/@/components/Modal';
|
|
|
|
|
|
import { useListPage } from '/@/hooks/system/useListPage';
|
2026-05-15 15:08:07 +08:00
|
|
|
|
import { initDictOptions } from '/@/utils/dict';
|
|
|
|
|
|
import { JDictSelectTag } from '/@/components/Form';
|
2026-05-07 17:53:48 +08:00
|
|
|
|
import MesXslRawMaterialEntryModal from './components/MesXslRawMaterialEntryModal.vue';
|
2026-05-13 17:25:13 +08:00
|
|
|
|
import RawMaterialEntryPrintPreviewModal from './components/RawMaterialEntryPrintPreviewModal.vue';
|
2026-05-07 17:53:48 +08:00
|
|
|
|
import { columns, searchFormSchema, superQuerySchema } from './MesXslRawMaterialEntry.data';
|
2026-05-13 17:25:13 +08:00
|
|
|
|
import {
|
|
|
|
|
|
list,
|
|
|
|
|
|
deleteOne,
|
|
|
|
|
|
batchDelete,
|
|
|
|
|
|
batchStockIn,
|
|
|
|
|
|
getImportUrl,
|
|
|
|
|
|
getExportUrl,
|
2026-05-15 15:08:07 +08:00
|
|
|
|
getLinkedRawMaterialCards,
|
2026-05-13 17:25:13 +08:00
|
|
|
|
prepareNativePrint,
|
2026-05-15 15:08:07 +08:00
|
|
|
|
type MesXslRawMaterialCardBrief,
|
2026-05-13 17:25:13 +08:00
|
|
|
|
} from './MesXslRawMaterialEntry.api';
|
|
|
|
|
|
import { useMessage } from '/@/hooks/web/useMessage';
|
|
|
|
|
|
import {
|
|
|
|
|
|
PRINT_TEMPLATE_SELECTED_PRINTER_KEY,
|
|
|
|
|
|
printNativeSchemaViaPrintDot,
|
|
|
|
|
|
} from '/@/views/print/template/utils/printNativeViaPrintDot';
|
|
|
|
|
|
import { normalizeImportedNativeSchema } from '/@/views/print/template/native/core/nativeSchemaNormalize';
|
|
|
|
|
|
import {
|
|
|
|
|
|
fetchPrintDotPrinters,
|
|
|
|
|
|
getPrintDotBridgeConfig,
|
|
|
|
|
|
setPrintDotBridgeConfig,
|
|
|
|
|
|
} from '/@/views/print/template/utils/printDotBridge';
|
2026-05-07 17:53:48 +08:00
|
|
|
|
|
2026-05-12 14:06:07 +08:00
|
|
|
|
const { createConfirm, createMessage } = useMessage();
|
2026-05-15 15:08:07 +08:00
|
|
|
|
/** 与 Flyway 中 sys_dict.dict_code 一致(与原材料卡片列表相同) */
|
|
|
|
|
|
const PRINT_DOT_WS_DICT = 'xslmes_print_dot_ws';
|
|
|
|
|
|
const printDotWsUrl = ref('');
|
|
|
|
|
|
/** 是否已成功连接桥并拿到响应(用于显示「下载打印插件」) */
|
|
|
|
|
|
const printDotConnected = ref(false);
|
2026-05-13 17:25:13 +08:00
|
|
|
|
|
|
|
|
|
|
function persistPrintDotConfig() {
|
2026-05-15 15:08:07 +08:00
|
|
|
|
// 与原材料卡片一致:桥接密钥固定为空,由本地 PrintDot 与字典地址对接
|
|
|
|
|
|
setPrintDotBridgeConfig(String(printDotWsUrl.value || '').trim(), '');
|
2026-05-13 17:25:13 +08:00
|
|
|
|
void refreshPrinterOptions(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-15 15:08:07 +08:00
|
|
|
|
function onPrintDotWsUrlChange() {
|
|
|
|
|
|
printDotConnected.value = false;
|
|
|
|
|
|
persistPrintDotConfig();
|
2026-05-13 17:25:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function downloadPrintPlugin() {
|
|
|
|
|
|
const base = import.meta.env.BASE_URL || '/';
|
|
|
|
|
|
const normalizedBase = base.endsWith('/') ? base : `${base}/`;
|
|
|
|
|
|
const url = `${normalizedBase}print-plugin/XSL-PrintDot.exe`;
|
|
|
|
|
|
const link = document.createElement('a');
|
|
|
|
|
|
link.href = url;
|
|
|
|
|
|
link.setAttribute('download', 'XSL-PrintDot.exe');
|
|
|
|
|
|
link.rel = 'noopener';
|
|
|
|
|
|
document.body.appendChild(link);
|
|
|
|
|
|
link.click();
|
|
|
|
|
|
document.body.removeChild(link);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-07 17:53:48 +08:00
|
|
|
|
const queryParam = reactive<any>({});
|
|
|
|
|
|
const [registerModal, { openModal }] = useModal();
|
|
|
|
|
|
|
2026-05-13 17:25:13 +08:00
|
|
|
|
const { tableContext, onExportXls, onImportXls } = useListPage({
|
2026-05-07 17:53:48 +08:00
|
|
|
|
tableProps: {
|
|
|
|
|
|
title: '原料入场记录',
|
|
|
|
|
|
api: list,
|
|
|
|
|
|
columns,
|
|
|
|
|
|
canResize: true,
|
|
|
|
|
|
formConfig: {
|
|
|
|
|
|
schemas: searchFormSchema,
|
|
|
|
|
|
autoSubmitOnEnter: true,
|
|
|
|
|
|
showAdvancedButton: true,
|
|
|
|
|
|
fieldMapToTime: [
|
|
|
|
|
|
['entryTime', ['entryTime_begin', 'entryTime_end'], 'YYYY-MM-DD HH:mm:ss'],
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
actionColumn: {
|
2026-05-13 17:25:13 +08:00
|
|
|
|
width: 320,
|
2026-05-07 17:53:48 +08:00
|
|
|
|
fixed: 'right',
|
2026-05-13 17:25:13 +08:00
|
|
|
|
title: '操作',
|
|
|
|
|
|
dataIndex: 'action',
|
|
|
|
|
|
slots: { customRender: 'action' },
|
2026-05-07 17:53:48 +08:00
|
|
|
|
},
|
|
|
|
|
|
beforeFetch: (params) => {
|
|
|
|
|
|
return Object.assign(params, queryParam);
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
exportConfig: {
|
|
|
|
|
|
name: '原料入场记录',
|
|
|
|
|
|
url: getExportUrl,
|
|
|
|
|
|
params: queryParam,
|
|
|
|
|
|
},
|
|
|
|
|
|
importConfig: {
|
|
|
|
|
|
url: getImportUrl,
|
|
|
|
|
|
success: handleSuccess,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-13 17:25:13 +08:00
|
|
|
|
const [registerTable, { reload }, { rowSelection, selectedRowKeys, selectedRows }] = tableContext;
|
2026-05-07 17:53:48 +08:00
|
|
|
|
const superQueryConfig = reactive(superQuerySchema);
|
|
|
|
|
|
|
2026-05-13 17:25:13 +08:00
|
|
|
|
/** 打印预览弹窗 */
|
|
|
|
|
|
const printPreviewOpen = ref(false);
|
|
|
|
|
|
const printPreviewEntryId = ref<string | null>(null);
|
|
|
|
|
|
const printPreviewBarcode = ref<string | undefined>(undefined);
|
|
|
|
|
|
|
|
|
|
|
|
function handlePrintPreview(record: Recordable) {
|
|
|
|
|
|
printPreviewEntryId.value = record.id as string;
|
|
|
|
|
|
printPreviewBarcode.value = record.barcode as string | undefined;
|
|
|
|
|
|
printPreviewOpen.value = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const printerOptions = ref<Array<{ label: string; value: string }>>([]);
|
|
|
|
|
|
const selectedPrinterName = ref<string>('__system_default__');
|
|
|
|
|
|
const printLoading = ref(false);
|
|
|
|
|
|
const printerSelectPlaceholder = '选择打印机(PrintDot 桥接)';
|
|
|
|
|
|
|
|
|
|
|
|
watch(selectedPrinterName, (v) => {
|
|
|
|
|
|
if (v) {
|
|
|
|
|
|
localStorage.setItem(PRINT_TEMPLATE_SELECTED_PRINTER_KEY, v);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
async function refreshPrinterOptions(showMessage = true) {
|
|
|
|
|
|
const optionMap = new Map<string, { label: string; value: string }>();
|
|
|
|
|
|
optionMap.set('__system_default__', { label: '系统默认打印机', value: '__system_default__' });
|
|
|
|
|
|
try {
|
|
|
|
|
|
const dotList = await fetchPrintDotPrinters();
|
2026-05-15 15:08:07 +08:00
|
|
|
|
printDotConnected.value = true;
|
2026-05-13 17:25:13 +08:00
|
|
|
|
dotList.forEach((p) => {
|
|
|
|
|
|
const name = String(p.name || '').trim();
|
|
|
|
|
|
if (!name) return;
|
|
|
|
|
|
const defMark = p.isDefault ? '(默认)' : '';
|
|
|
|
|
|
optionMap.set(name, { label: `${name}${defMark}`, value: name });
|
|
|
|
|
|
});
|
|
|
|
|
|
if (selectedPrinterName.value && !optionMap.has(selectedPrinterName.value)) {
|
|
|
|
|
|
optionMap.set(selectedPrinterName.value, {
|
|
|
|
|
|
label: `${selectedPrinterName.value}(手动)`,
|
|
|
|
|
|
value: selectedPrinterName.value,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
printerOptions.value = Array.from(optionMap.values());
|
|
|
|
|
|
if (showMessage) {
|
|
|
|
|
|
if (dotList.length) {
|
|
|
|
|
|
createMessage.success(`已从 PrintDot 桥接识别 ${dotList.length} 台打印机`);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
createMessage.warning('PrintDot 已连接但未返回打印机列表');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e: unknown) {
|
2026-05-15 15:08:07 +08:00
|
|
|
|
printDotConnected.value = false;
|
2026-05-13 17:25:13 +08:00
|
|
|
|
if (selectedPrinterName.value && !optionMap.has(selectedPrinterName.value)) {
|
|
|
|
|
|
optionMap.set(selectedPrinterName.value, {
|
|
|
|
|
|
label: `${selectedPrinterName.value}(手动)`,
|
|
|
|
|
|
value: selectedPrinterName.value,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
printerOptions.value = Array.from(optionMap.values());
|
|
|
|
|
|
if (showMessage) {
|
|
|
|
|
|
createMessage.warning(`PrintDot:${e instanceof Error ? e.message : String(e)}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function executePrint(record: Recordable, options?: { silentSuccess?: boolean }) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const prep = (await prepareNativePrint(record.id as string)) as Record<string, unknown>;
|
|
|
|
|
|
const templateJsonRaw = prep.templateJson as string;
|
|
|
|
|
|
const printData = prep.printData as Record<string, unknown>;
|
|
|
|
|
|
const paperWidthMm = Number((prep as any).paperWidthMm ?? 0);
|
|
|
|
|
|
const paperHeightMm = Number((prep as any).paperHeightMm ?? 0);
|
|
|
|
|
|
const paperOrientation = String((prep as any).paperOrientation || '').toLowerCase();
|
|
|
|
|
|
if (!templateJsonRaw) {
|
|
|
|
|
|
throw new Error('模板 JSON 为空');
|
|
|
|
|
|
}
|
|
|
|
|
|
let raw: unknown;
|
|
|
|
|
|
try {
|
|
|
|
|
|
raw = typeof templateJsonRaw === 'string' ? JSON.parse(templateJsonRaw) : templateJsonRaw;
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
throw new Error('模板 JSON 格式错误');
|
|
|
|
|
|
}
|
|
|
|
|
|
const schema = normalizeImportedNativeSchema(raw);
|
|
|
|
|
|
if (paperWidthMm > 0 && paperHeightMm > 0) {
|
|
|
|
|
|
const orient = paperOrientation === 'landscape' ? 'landscape' : paperOrientation === 'portrait' ? 'portrait' : '';
|
|
|
|
|
|
const normalized =
|
|
|
|
|
|
orient === 'landscape'
|
|
|
|
|
|
? {
|
|
|
|
|
|
width: Math.max(paperWidthMm, paperHeightMm),
|
|
|
|
|
|
height: Math.min(paperWidthMm, paperHeightMm),
|
|
|
|
|
|
}
|
|
|
|
|
|
: orient === 'portrait'
|
|
|
|
|
|
? {
|
|
|
|
|
|
width: Math.min(paperWidthMm, paperHeightMm),
|
|
|
|
|
|
height: Math.max(paperWidthMm, paperHeightMm),
|
|
|
|
|
|
}
|
|
|
|
|
|
: {
|
|
|
|
|
|
width: paperWidthMm,
|
|
|
|
|
|
height: paperHeightMm,
|
|
|
|
|
|
};
|
|
|
|
|
|
schema.page.width = normalized.width;
|
|
|
|
|
|
schema.page.height = normalized.height;
|
|
|
|
|
|
}
|
|
|
|
|
|
await printNativeSchemaViaPrintDot({
|
|
|
|
|
|
schema,
|
|
|
|
|
|
data: printData as Record<string, unknown>,
|
|
|
|
|
|
jobName: `原料入场记录-${(record.barcode as string) || record.id}.pdf`,
|
|
|
|
|
|
printerSelection:
|
|
|
|
|
|
selectedPrinterName.value ||
|
|
|
|
|
|
localStorage.getItem(PRINT_TEMPLATE_SELECTED_PRINTER_KEY) ||
|
|
|
|
|
|
'__system_default__',
|
|
|
|
|
|
});
|
|
|
|
|
|
if (!options?.silentSuccess) {
|
|
|
|
|
|
createMessage.success('已通过 PrintDot 提交打印');
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e: unknown) {
|
|
|
|
|
|
throw new Error(e instanceof Error ? e.message : String(e));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handlePrintSelected() {
|
2026-05-15 15:08:07 +08:00
|
|
|
|
if (!String(printDotWsUrl.value || '').trim()) {
|
|
|
|
|
|
createMessage.warning('请先选择打印桥接(PrintDot 地址)');
|
2026-05-13 17:25:13 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const rows = selectedRows.value || [];
|
|
|
|
|
|
if (!rows.length) {
|
|
|
|
|
|
createMessage.warning('请至少勾选一条记录后再点击「打印选中」');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
printLoading.value = true;
|
|
|
|
|
|
const hideLoadingMsg = createMessage.loading(`正在打印 ${rows.length} 条记录,请稍候…`, 0);
|
|
|
|
|
|
(async () => {
|
|
|
|
|
|
let ok = 0;
|
|
|
|
|
|
let firstError = '';
|
|
|
|
|
|
for (const row of rows) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await executePrint(row, { silentSuccess: true });
|
|
|
|
|
|
ok += 1;
|
|
|
|
|
|
} catch (e: unknown) {
|
|
|
|
|
|
if (!firstError) {
|
|
|
|
|
|
firstError = e instanceof Error ? e.message : String(e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (ok === rows.length) {
|
|
|
|
|
|
createMessage.success(`已通过 PrintDot 提交 ${ok} 条打印任务`);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
createMessage.warning(`打印完成:成功 ${ok},失败 ${rows.length - ok}${firstError ? `。首条错误:${firstError}` : ''}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
hideLoadingMsg();
|
|
|
|
|
|
printLoading.value = false;
|
|
|
|
|
|
})();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handlePrintRow(record: Recordable) {
|
2026-05-15 15:08:07 +08:00
|
|
|
|
if (!String(printDotWsUrl.value || '').trim()) {
|
|
|
|
|
|
createMessage.warning('请先选择打印桥接(PrintDot 地址)');
|
2026-05-13 17:25:13 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
printLoading.value = true;
|
|
|
|
|
|
const hideLoadingMsg = createMessage.loading('正在生成 PDF 并提交打印,版面复杂时可能需数十秒,请稍候…', 0);
|
|
|
|
|
|
executePrint(record)
|
|
|
|
|
|
.then(() => {
|
|
|
|
|
|
createMessage.success('已通过 PrintDot 提交打印');
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch((e: unknown) => {
|
|
|
|
|
|
createMessage.error(e instanceof Error ? e.message : String(e));
|
|
|
|
|
|
})
|
|
|
|
|
|
.finally(() => {
|
|
|
|
|
|
hideLoadingMsg();
|
|
|
|
|
|
printLoading.value = false;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-15 15:08:07 +08:00
|
|
|
|
onMounted(async () => {
|
|
|
|
|
|
const cfg = getPrintDotBridgeConfig();
|
|
|
|
|
|
// 密钥不使用,与原材料卡片列表一致
|
|
|
|
|
|
setPrintDotBridgeConfig(cfg.wsUrl || '', '');
|
|
|
|
|
|
printDotWsUrl.value = cfg.wsUrl || '';
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const raw = await initDictOptions(PRINT_DOT_WS_DICT);
|
|
|
|
|
|
const items = Array.isArray(raw) ? raw : [];
|
|
|
|
|
|
const values = items
|
|
|
|
|
|
.map((it: Recordable) => String(it.value ?? it.itemValue ?? '').trim())
|
|
|
|
|
|
.filter(Boolean);
|
|
|
|
|
|
const valueSet = new Set(values);
|
|
|
|
|
|
if (valueSet.size && printDotWsUrl.value && !valueSet.has(String(printDotWsUrl.value).trim())) {
|
|
|
|
|
|
printDotWsUrl.value = values[0];
|
|
|
|
|
|
setPrintDotBridgeConfig(printDotWsUrl.value, '');
|
|
|
|
|
|
} else if (valueSet.size && !String(printDotWsUrl.value || '').trim()) {
|
|
|
|
|
|
printDotWsUrl.value = values[0];
|
|
|
|
|
|
setPrintDotBridgeConfig(printDotWsUrl.value, '');
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
/* 字典未配置时沿用 localStorage */
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 17:25:13 +08:00
|
|
|
|
const saved = localStorage.getItem(PRINT_TEMPLATE_SELECTED_PRINTER_KEY);
|
|
|
|
|
|
if (saved) {
|
|
|
|
|
|
selectedPrinterName.value = saved;
|
|
|
|
|
|
}
|
2026-05-15 15:08:07 +08:00
|
|
|
|
await refreshPrinterOptions(false);
|
2026-05-13 17:25:13 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-05-07 17:53:48 +08:00
|
|
|
|
function handleSuperQuery(params) {
|
|
|
|
|
|
Object.keys(params).map((k) => {
|
|
|
|
|
|
queryParam[k] = params[k];
|
|
|
|
|
|
});
|
|
|
|
|
|
reload();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleAdd() {
|
|
|
|
|
|
openModal(true, { isUpdate: false, showFooter: true });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleEdit(record: Recordable) {
|
|
|
|
|
|
openModal(true, { record, isUpdate: true, showFooter: true });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleDetail(record: Recordable) {
|
|
|
|
|
|
openModal(true, { record, isUpdate: true, showFooter: false });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-15 15:08:07 +08:00
|
|
|
|
/** 删除确认框:关联卡片以列表形式展示 */
|
|
|
|
|
|
function renderLinkedCardsConfirmContent(linked: MesXslRawMaterialCardBrief[], summaryText: string) {
|
|
|
|
|
|
const labelStyle = { color: 'rgba(0,0,0,0.45)', display: 'inline-block', minWidth: '40px' as const };
|
|
|
|
|
|
return h('div', { class: 'linked-raw-material-cards-confirm' }, [
|
|
|
|
|
|
h('p', { style: { margin: '0 0 12px', color: 'rgba(0,0,0,0.88)' } }, summaryText),
|
|
|
|
|
|
h(
|
|
|
|
|
|
'ul',
|
|
|
|
|
|
{
|
|
|
|
|
|
style: {
|
|
|
|
|
|
listStyle: 'none',
|
|
|
|
|
|
padding: 0,
|
|
|
|
|
|
margin: 0,
|
|
|
|
|
|
maxHeight: '360px',
|
|
|
|
|
|
overflowY: 'auto' as const,
|
|
|
|
|
|
border: '1px solid #f0f0f0',
|
|
|
|
|
|
borderRadius: '6px',
|
|
|
|
|
|
background: '#fafafa',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
linked.map((c, idx) =>
|
|
|
|
|
|
h(
|
|
|
|
|
|
'li',
|
|
|
|
|
|
{
|
|
|
|
|
|
key: c.id ?? `${c.barcode ?? ''}-${c.batchNo ?? ''}-${idx}`,
|
|
|
|
|
|
style: {
|
|
|
|
|
|
padding: '10px 12px',
|
|
|
|
|
|
borderBottom: idx < linked.length - 1 ? '1px solid #f0f0f0' : 'none',
|
|
|
|
|
|
background: '#fff',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
[
|
|
|
|
|
|
h('div', { style: { fontSize: '13px', lineHeight: '22px', color: 'rgba(0,0,0,0.88)', wordBreak: 'break-all' as const } }, [
|
|
|
|
|
|
h('div', null, [h('span', { style: labelStyle }, '条码'), ':', c.barcode || '-']),
|
|
|
|
|
|
h('div', null, [h('span', { style: labelStyle }, '批次'), ':', c.batchNo || '-']),
|
|
|
|
|
|
h('div', null, [h('span', { style: labelStyle }, '物料'), ':', c.materialName || '-']),
|
|
|
|
|
|
]),
|
|
|
|
|
|
],
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function handleDelete(record: Recordable) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const linked = await getLinkedRawMaterialCards(record.id as string);
|
|
|
|
|
|
if (linked && linked.length > 0) {
|
|
|
|
|
|
createConfirm({
|
|
|
|
|
|
iconType: 'warning',
|
|
|
|
|
|
title: '已生成原材料卡片',
|
|
|
|
|
|
width: 600,
|
|
|
|
|
|
content: renderLinkedCardsConfirmContent(linked, `以下 ${linked.length} 张卡片将随入场记录一并删除:`),
|
|
|
|
|
|
okText: '仍要删除',
|
|
|
|
|
|
cancelText: '取消',
|
|
|
|
|
|
onOk: async () => {
|
|
|
|
|
|
await deleteOne({ id: record.id as string, cascadeDeleteCards: true }, handleSuccess);
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
createConfirm({
|
|
|
|
|
|
iconType: 'warning',
|
|
|
|
|
|
title: '确认删除',
|
|
|
|
|
|
content: '是否确认删除该条原料入场记录?',
|
|
|
|
|
|
okText: '确认',
|
|
|
|
|
|
cancelText: '取消',
|
|
|
|
|
|
onOk: async () => {
|
|
|
|
|
|
await deleteOne({ id: record.id as string, cascadeDeleteCards: false }, handleSuccess);
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e: unknown) {
|
|
|
|
|
|
createMessage.error(e instanceof Error ? e.message : String(e));
|
|
|
|
|
|
}
|
2026-05-07 17:53:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function batchHandleDelete() {
|
2026-05-15 15:08:07 +08:00
|
|
|
|
const ids = (selectedRowKeys.value || []).join(',');
|
|
|
|
|
|
if (!ids) {
|
|
|
|
|
|
createMessage.warning('请先勾选要删除的记录');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
const linked = await getLinkedRawMaterialCards(ids);
|
|
|
|
|
|
const cardList: MesXslRawMaterialCardBrief[] = linked && linked.length > 0 ? linked : [];
|
|
|
|
|
|
const hasCards = cardList.length > 0;
|
|
|
|
|
|
createConfirm({
|
|
|
|
|
|
iconType: 'warning',
|
|
|
|
|
|
title: hasCards ? '已生成原材料卡片' : '确认删除',
|
|
|
|
|
|
width: hasCards ? 600 : 416,
|
|
|
|
|
|
content: hasCards
|
|
|
|
|
|
? renderLinkedCardsConfirmContent(cardList, `以下 ${cardList.length} 张卡片将随选中入场记录一并删除:`)
|
|
|
|
|
|
: `是否删除选中的 ${selectedRowKeys.value.length} 条数据?`,
|
|
|
|
|
|
okText: '确认删除',
|
|
|
|
|
|
cancelText: '取消',
|
|
|
|
|
|
onOk: async () => {
|
|
|
|
|
|
await batchDelete({ ids, cascadeDeleteCards: !!hasCards }, handleSuccess);
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (e: unknown) {
|
|
|
|
|
|
createMessage.error(e instanceof Error ? e.message : String(e));
|
|
|
|
|
|
}
|
2026-05-07 17:53:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-12 14:06:07 +08:00
|
|
|
|
function handleStockIn() {
|
|
|
|
|
|
if (!selectedRowKeys.value.length) {
|
|
|
|
|
|
createMessage.warning('请先勾选要结存入库的记录');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
createConfirm({
|
|
|
|
|
|
iconType: 'warning',
|
|
|
|
|
|
title: '结存入库',
|
|
|
|
|
|
content: `是否将选中的 ${selectedRowKeys.value.length} 条记录结存入库`,
|
|
|
|
|
|
okText: '确认',
|
|
|
|
|
|
cancelText: '取消',
|
|
|
|
|
|
onOk: async () => {
|
|
|
|
|
|
await batchStockIn(selectedRowKeys.value.join(','));
|
|
|
|
|
|
createMessage.success('结存入库成功!');
|
|
|
|
|
|
handleSuccess();
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-07 17:53:48 +08:00
|
|
|
|
function handleSuccess() {
|
|
|
|
|
|
(selectedRowKeys.value = []) && reload();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getTableAction(record) {
|
|
|
|
|
|
return [
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '编辑',
|
|
|
|
|
|
onClick: handleEdit.bind(null, record),
|
|
|
|
|
|
auth: 'xslmes:mes_xsl_raw_material_entry:edit',
|
|
|
|
|
|
},
|
2026-05-13 17:25:13 +08:00
|
|
|
|
{
|
|
|
|
|
|
label: '打印预览',
|
|
|
|
|
|
onClick: handlePrintPreview.bind(null, record),
|
|
|
|
|
|
auth: 'xslmes:mes_xsl_raw_material_entry:edit',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '打印',
|
|
|
|
|
|
onClick: handlePrintRow.bind(null, record),
|
|
|
|
|
|
auth: 'xslmes:mes_xsl_raw_material_entry:edit',
|
|
|
|
|
|
},
|
2026-05-07 17:53:48 +08:00
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getDropDownAction(record) {
|
|
|
|
|
|
return [
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '详情',
|
|
|
|
|
|
onClick: handleDetail.bind(null, record),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '删除',
|
2026-05-15 15:08:07 +08:00
|
|
|
|
onClick: handleDelete.bind(null, record),
|
2026-05-07 17:53:48 +08:00
|
|
|
|
auth: 'xslmes:mes_xsl_raw_material_entry:delete',
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="less" scoped>
|
|
|
|
|
|
:deep(.ant-picker-range) {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|