feat:更改页面

This commit is contained in:
dzc committed 2026-09-08 15:25:06 +08:00
1 parent b422df5b76
commit 44b2df144c
84 files changed
+11701 -2114

No files matched your search

@@ -0,0 +1,289 @@
<template>
<el-dialog v-model="visible" :title="dialogTitle" width="560px" draggable :close-on-click-modal="false">
<el-form v-if="mode === 'bind'" ref="bindFormRef" :model="bindForm" label-width="120px">
<el-form-item label="业务终端">
<el-input :model-value="sourceName" disabled />
</el-form-item>
<el-alert class="mb-5" type="info" :closable="false" show-icon>
<template #title
>此处建立“业务终端 → 尔印 USB 打印机”的一对一关系。打印机没有独立屏幕、账号和心跳;绑定的食堂或超市终端负责发现 USB
设备、接收任务、打印并回传结果。</template
>
</el-alert>
<el-form-item label="尔印打印机" prop="printerHardId" :rules="[{ required: true, message: '请选择打印机' }]">
<el-select
v-model="bindForm.printerHardId"
class="w-full"
filterable
:loading="loading"
placeholder="请选择同商户同部门的空闲打印机"
>
<el-option
v-for="printer in availablePrinters"
:key="printer.id"
:label="`${printer.name || '未命名打印机'}(ID:${printer.id})`"
:value="String(printer.id)"
/>
</el-select>
</el-form-item>
<p v-if="!loading && availablePrinters.length === 0" class="text-sm text-[var(--el-text-color-secondary)]">
当前部门没有可绑定的空闲尔印打印机资产,请先新增并分配商户部门。
</p>
</el-form>
<el-form v-else ref="configFormRef" :model="configForm" :rules="configRules" label-width="150px">
<el-alert class="mb-5" type="info" :closable="false" show-icon>
<template #title
>推荐在已绑定的食堂或超市终端打开“打印机信息”,点击“上报当前设备”自动回填型号、VID 和
PID;管理端手填仅用于现场无法上报时。</template
>
</el-alert>
<el-form-item prop="printerModel">
<template #label>
<span class="inline-flex items-center gap-1">
打印机型号
<el-tooltip
content="用于运维人员识别实际 USB 打印机。优先由绑定的食堂或超市终端自动上报;手工填写示例:GEZHI / micro-printer。"
placement="top"
popper-class="max-w-[360px] leading-5"
>
<el-icon class="cursor-help text-[var(--el-text-color-secondary)]" tabindex="0" aria-label="打印机型号填写说明"
><InfoFilled
/></el-icon>
</el-tooltip>
</span>
</template>
<el-input v-model="configForm.printerModel" placeholder="推荐由绑定的业务终端自动上报" />
</el-form-item>
<el-form-item prop="usbVendorId">
<template #label>
<span class="inline-flex items-center gap-1">
USB VID
<el-tooltip
content="USB 厂商编号,管理端填写十进制值,范围 0~65535。当前联调设备识别为 10473(十六进制 0x28E9);不确定时请勿猜测,使用终端自动上报。"
placement="top"
popper-class="max-w-[360px] leading-5"
>
<el-icon class="cursor-help text-[var(--el-text-color-secondary)]" tabindex="0" aria-label="USB VID 填写说明"
><InfoFilled
/></el-icon>
</el-tooltip>
</span>
</template>
<el-input-number
v-model="configForm.usbVendorId"
:min="0"
:max="65535"
controls-position="right"
class="w-full"
placeholder="例如 10473"
/>
</el-form-item>
<el-form-item prop="usbProductId">
<template #label>
<span class="inline-flex items-center gap-1">
USB PID
<el-tooltip
content="USB 产品编号,管理端填写十进制值,必须与 VID 同时填写或同时留空。当前联调设备识别为 649(十六进制 0x0289);推荐使用终端自动上报。"
placement="top"
popper-class="max-w-[360px] leading-5"
>
<el-icon class="cursor-help text-[var(--el-text-color-secondary)]" tabindex="0" aria-label="USB PID 填写说明"
><InfoFilled
/></el-icon>
</el-tooltip>
</span>
</template>
<el-input-number
v-model="configForm.usbProductId"
:min="0"
:max="65535"
controls-position="right"
class="w-full"
placeholder="例如 649"
/>
</el-form-item>
<el-form-item prop="paperWidth">
<template #label>
<span class="inline-flex items-center gap-1">
纸宽
<el-tooltip
content="首期只支持 80mm 热敏纸,对应后端图片宽度 576px。此项固定为 80,无需修改。"
placement="top"
popper-class="max-w-[360px] leading-5"
>
<el-icon class="cursor-help text-[var(--el-text-color-secondary)]" tabindex="0" aria-label="纸宽配置说明"
><InfoFilled
/></el-icon>
</el-tooltip>
</span>
</template>
<el-input-number v-model="configForm.paperWidth" :min="80" :max="80" controls-position="right" class="w-full" />
<span class="ml-2">mm(首期固定)</span>
</el-form-item>
<el-form-item>
<template #label>
<span class="inline-flex items-center gap-1">
自动切刀
<el-tooltip
content="打印完成后发送切纸命令。打印机带自动切刀时保持开启;若型号没有切刀,或测试打印在切纸阶段报错,再关闭此项。"
placement="top"
popper-class="max-w-[360px] leading-5"
>
<el-icon class="cursor-help text-[var(--el-text-color-secondary)]" tabindex="0" aria-label="自动切刀配置说明"
><InfoFilled
/></el-icon>
</el-tooltip>
</span>
</template>
<el-switch v-model="configForm.autoCut" active-value="1" inactive-value="0" />
</el-form-item>
<el-form-item>
<template #label>
<span class="inline-flex items-center gap-1">
图片指令模式
<el-tooltip
content="控制 PNG 图片转换成哪种 ESC/POS 指令。优先使用 GS v 0;只有真机测试出现白纸、缺行或图片不完整时,才切换 ESC * 兼容模式并重新测试。"
placement="top"
popper-class="max-w-[360px] leading-5"
>
<el-icon class="cursor-help text-[var(--el-text-color-secondary)]" tabindex="0" aria-label="图片指令模式说明"
><InfoFilled
/></el-icon>
</el-tooltip>
</span>
</template>
<el-radio-group v-model="configForm.imageMode"
><el-radio label="GS_V0">GS v 0(默认)</el-radio><el-radio label="ESC_STAR">ESC *(兼容回退)</el-radio></el-radio-group
>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" :loading="loading" :disabled="mode === 'bind' && availablePrinters.length === 0" @click="submit"
>保存</el-button
>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="HardPrinterDialog">
import { InfoFilled } from "@element-plus/icons-vue"
import * as printerApi from "/@/api/hard/hard-printer"
import { useMessage } from "/@/hooks/message"
const emit = defineEmits(["refresh"])
const visible = ref(false)
const loading = ref(false)
const mode = ref<"bind" | "config">("bind")
const sourceName = ref("")
/** 来源业务终端雪花 ID,始终按字符串传递以保留完整精度。 */
const sourceHardId = ref<printerApi.HardwareId | null>(null)
/** 目标打印机资产雪花 ID,始终按字符串传递以保留完整精度。 */
const printerHardId = ref<printerApi.HardwareId | null>(null)
const availablePrinters = ref<any[]>([])
const bindFormRef = ref()
const configFormRef = ref()
/** 当前待提交的一对一打印机绑定。 */
const bindForm = reactive<{ printerHardId: printerApi.HardwareId | null }>({ printerHardId: null })
const configForm = reactive<printerApi.HardPrinterConfig>({
printerHardId: "",
printerModel: "",
usbVendorId: undefined,
usbProductId: undefined,
paperWidth: 80,
autoCut: "1",
imageMode: "GS_V0",
})
const validateUsbPair = (_rule: unknown, _value: unknown, callback: (error?: Error) => void) => {
const hasVid = configForm.usbVendorId !== undefined && configForm.usbVendorId !== null
const hasPid = configForm.usbProductId !== undefined && configForm.usbProductId !== null
callback(hasVid === hasPid ? undefined : new Error("USB VID 和 PID 必须同时填写或同时留空"))
}
const configRules = {
usbVendorId: [{ validator: validateUsbPair, trigger: "change" }],
usbProductId: [{ validator: validateUsbPair, trigger: "change" }],
paperWidth: [{ required: true, message: "纸宽必须为80mm", trigger: "change" }],
}
const dialogTitle = computed(() => (mode.value === "bind" ? "绑定尔印打印机" : "打印机配置"))
/** 恢复打印配置默认值,避免连续打开不同设备时残留上一台打印机的参数。 */
const resetConfig = (hardId: printerApi.HardwareId) => {
Object.assign(configForm, {
id: undefined,
printerHardId: hardId,
printerModel: "",
usbVendorId: undefined,
usbProductId: undefined,
paperWidth: 80,
autoCut: "1",
imageMode: "GS_V0",
})
}
/** 打开来源终端绑定弹窗,并使用未经过数值转换的雪花 ID 查询候选打印机。 */
const openBind = async (row: any) => {
mode.value = "bind"
const hardId = String(row.id)
sourceHardId.value = hardId
sourceName.value = row.name || `设备 ${row.id}`
bindForm.printerHardId = null
visible.value = true
loading.value = true
try {
const response: any = await printerApi.listAvailable(hardId)
availablePrinters.value = response.data || []
} catch (error: any) {
availablePrinters.value = []
useMessage().error(error?.msg || "读取可绑定打印机失败")
} finally {
loading.value = false
}
}
/** 打开 EY_PRINT 参数弹窗,并保持目标设备雪花 ID 的字符串形态。 */
const openConfig = async (row: any) => {
mode.value = "config"
const hardId = String(row.id)
printerHardId.value = hardId
resetConfig(hardId)
visible.value = true
loading.value = true
try {
const response: any = await printerApi.getConfig(hardId)
Object.assign(configForm, response.data || {}, { printerHardId: hardId })
} catch (error: any) {
useMessage().error(error?.msg || "读取打印机配置失败")
} finally {
loading.value = false
}
}
/** 校验并提交绑定或配置,设备主键保持字符串以便后端无损转换为 Long。 */
const submit = async () => {
const formRef = mode.value === "bind" ? bindFormRef.value : configFormRef.value
const valid = await formRef?.validate?.().catch(() => false)
if (valid === false) return
loading.value = true
try {
if (mode.value === "bind") {
if (!sourceHardId.value || !bindForm.printerHardId) {
useMessage().warning("请选择尔印打印机")
return
}
await printerApi.bind({ sourceHardId: sourceHardId.value, printerHardId: bindForm.printerHardId })
useMessage().success("绑定成功,请在该业务终端的“打印机信息”页面刷新并上报 USB 设备")
} else {
await printerApi.saveConfig({ ...configForm, printerHardId: printerHardId.value || configForm.printerHardId })
useMessage().success("配置保存成功")
}
visible.value = false
emit("refresh")
} catch (error: any) {
useMessage().error(error?.msg || "保存失败")
} finally {
loading.value = false
}
}
defineExpose({ openBind, openConfig })
</script>