Compare commits

...

6 Commits

@ -35,6 +35,7 @@ export interface MoldVO {
status: number // 状态 status: number // 状态
images: string // 模具图片 images: string // 模具图片
qrcodeUrl?: string qrcodeUrl?: string
templateJson?: string | any
fileUrl?: string fileUrl?: string
remark: string // 备注 remark: string // 备注
isEnable: boolean // 是否启用 isEnable: boolean // 是否启用

@ -7,6 +7,7 @@ export interface ProductVO {
barCode: string // 产品条码 barCode: string // 产品条码
isCode?: boolean isCode?: boolean
qrcodeUrl?: string qrcodeUrl?: string
templateJson?: string | any
categoryId: number // 产品类型编号 categoryId: number // 产品类型编号
subCategoryId: number // 产品类型子类编号 subCategoryId: number // 产品类型子类编号
subCategoryName: string // 产品类型子类名称 subCategoryName: string // 产品类型子类名称

@ -9,6 +9,7 @@ export interface CriticalComponentVO {
count?: number count?: number
remark?: string remark?: string
qrcodeUrl?: string qrcodeUrl?: string
templateJson?: string | any
createTime?: string createTime?: string
images?: string images?: string
} }

@ -26,6 +26,7 @@ export interface DeviceLedgerVO {
remark: string // 备注 remark: string // 备注
fileUrl?: string // 附件下载 fileUrl?: string // 附件下载
qrcodeUrl?: string qrcodeUrl?: string
templateJson?: string | any
isSchedueld?: number isSchedueld?: number
isScheduled?: number isScheduled?: number
ratedCapacity?: number ratedCapacity?: number

@ -0,0 +1,33 @@
import request from '@/config/axios'
export interface PrintTemplateVO {
id: number
templateCode: string
templateName: string
templateType: number
templateJson: string
remark: string
isEnable: boolean
createTime: string
}
export const PrintTemplateApi = {
getPrintTemplatePage: async (params: any) => {
return await request.get({ url: `/mes/print-template/page`, params })
},
getPrintTemplate: async (id: number) => {
return await request.get({ url: `/mes/print-template/get?id=` + id })
},
createPrintTemplate: async (data: PrintTemplateVO) => {
return await request.post({ url: `/mes/print-template/create`, data })
},
updatePrintTemplate: async (data: PrintTemplateVO) => {
return await request.put({ url: `/mes/print-template/update`, data })
},
deletePrintTemplate: async (id: number) => {
return await request.delete({ url: `/mes/print-template/delete?id=` + id })
},
exportPrintTemplate: async (params) => {
return await request.download({ url: `/mes/print-template/export-excel`, params })
},
}

@ -63,6 +63,9 @@ const props = withDefaults(
printPaperHeight?: number printPaperHeight?: number
printMaxWidth?: number printMaxWidth?: number
printMaxHeight?: number printMaxHeight?: number
templateJsonUrl?: string
templateJson?: any
printData?: Record<string, any>
}>(), }>(),
{ {
imageUrl: '', imageUrl: '',
@ -82,7 +85,10 @@ const props = withDefaults(
printPaperWidth: 80, printPaperWidth: 80,
printPaperHeight: 80, printPaperHeight: 80,
printMaxWidth: 180, printMaxWidth: 180,
printMaxHeight: 120 printMaxHeight: 120,
templateJsonUrl: '',
templateJson: undefined,
printData: () => ({})
} }
) )
@ -202,26 +208,88 @@ const handlePreview = () => {
}) })
} }
const replaceTemplateValues = (templateJson: any, printData: Record<string, any>) => {
if (!templateJson?.panels) return templateJson
return {
...templateJson,
panels: templateJson.panels.map((panel: any) => ({
...panel,
printElements: panel.printElements?.map((element: any) => {
if (!element?.options?.qid) return element
const qid = element.options.qid
const value = printData[qid]
if (value === undefined || value === null) return element
const newOptions = { ...element.options }
if (qid === 'qrcodeUrl') {
newOptions.src = value
newOptions.testData = value
} else {
newOptions.field = qid
if (newOptions.testData === undefined || newOptions.testData === '') {
newOptions.testData = String(value)
}
}
return {
...element,
options: newOptions
}
}) || []
}))
}
}
const handlePrint = async () => { const handlePrint = async () => {
if (!props.imageUrl || !props.showPrint) return if (!props.imageUrl || !props.showPrint) return
const printSize = await resolvePrintSize(props.imageUrl)
const imageWidth = printSize.width let templateJson: any
const imageHeight = printSize.height let printData: Record<string, any>
const printId = props.printId === undefined || props.printId === null ? '' : String(props.printId)
const idAreaHeight = printId ? 14 : 0 printData = {
const paperHeight = imageHeight + idAreaHeight qrcodeUrl: props.imageUrl,
printId: props.printId === undefined || props.printId === null ? '' : String(props.printId),
...props.printData
}
if (props.templateJson) {
templateJson = replaceTemplateValues(props.templateJson, printData)
} else if (props.templateJsonUrl) {
try {
const response = await request.get({ url: props.templateJsonUrl })
templateJson = typeof response.data === 'string' ? JSON.parse(response.data) : response.data
templateJson = replaceTemplateValues(templateJson, printData)
} catch (error) {
console.error('获取打印模板失败', error)
message.error('获取打印模板失败')
return
}
} else {
const printSize = await resolvePrintSize(props.imageUrl)
const imageWidth = printSize.width
const imageHeight = printSize.height
const printId = props.printId === undefined || props.printId === null ? '' : String(props.printId)
const idAreaHeight = printId ? 14 : 0
const paperHeight = imageHeight + idAreaHeight
templateJson = buildQrcodeTemplateJson(props.imageUrl, printId, imageWidth, imageHeight, paperHeight)
}
const paperSize = templateJson?.panels?.[0] ? {
width: templateJson.panels[0].width,
height: templateJson.panels[0].height
} : { width: 80, height: 80 }
hiprintPreviewDialogRef.value?.open({ hiprintPreviewDialogRef.value?.open({
title: props.printTitle, title: props.printTitle,
printData: { printData,
qrcodeUrl: props.imageUrl, templateJson,
printId
},
templateJson: buildQrcodeTemplateJson(props.imageUrl, printId, imageWidth, imageHeight, paperHeight),
withDefaultQrcodeLayout: false, withDefaultQrcodeLayout: false,
paperSize: { paperSize
width: imageWidth,
height: paperHeight
}
}) })
} }

@ -2798,6 +2798,36 @@ export default {
updateFail: 'Update failed' updateFail: 'Update failed'
} }
}, },
TemplateManagement: {
PrintTemplate: {
moduleName: 'Print Template',
templateCode: 'Template Code',
templateName: 'Template Name',
templateType: 'Template Type',
templateJson: 'Template JSON',
remark: 'Remark',
isEnable: 'Enabled',
enabled: 'Enabled',
disabled: 'Disabled',
createTime: 'Create Time',
operate: 'Operate',
exportFilename: 'PrintTemplate.xls',
typeLabel: 'Label',
typeBarcode: 'Barcode',
typeReport: 'Report',
placeholderTemplateCode: 'Please input template code',
templateCodeTooltip: 'Turn on the switch to auto-generate code, no manual input required',
placeholderTemplateName: 'Please input template name',
placeholderTemplateType: 'Please select template type',
placeholderTemplateJson: 'Please input template JSON content',
placeholderRemark: 'Please input remark',
validatorCodeRequired: 'Template code can not be empty',
validatorNameRequired: 'Template name can not be empty',
validatorTypeRequired: 'Template type can not be empty',
design: 'Design',
designTitle: 'Template Design'
}
},
QualityManagement: { QualityManagement: {
ZjType: { ZjType: {
moduleName: 'Inspection Type', moduleName: 'Inspection Type',

@ -2300,6 +2300,36 @@ export default {
updateFail: '更新失败' updateFail: '更新失败'
} }
}, },
TemplateManagement: {
PrintTemplate: {
moduleName: '打印模板',
templateCode: '模板编码',
templateName: '模板名称',
templateType: '模板类型',
templateJson: '模板JSON',
remark: '备注',
isEnable: '是否启用',
enabled: '启用',
disabled: '禁用',
createTime: '创建时间',
operate: '操作',
exportFilename: '打印模板.xls',
typeLabel: '标签',
typeBarcode: '条码',
typeReport: '报表',
placeholderTemplateCode: '请输入模板编码',
templateCodeTooltip: '开启开关则自动生成编码,无需手动输入',
placeholderTemplateName: '请输入模板名称',
placeholderTemplateType: '请选择模板类型',
placeholderTemplateJson: '请输入模板JSON内容',
placeholderRemark: '请输入备注',
validatorCodeRequired: '模板编码不能为空',
validatorNameRequired: '模板名称不能为空',
validatorTypeRequired: '模板类型不能为空',
design: '配置',
designTitle: '模板配置'
}
},
QualityManagement: { QualityManagement: {
ZjType: { ZjType: {
moduleName: '检验类型', moduleName: '检验类型',

@ -118,6 +118,8 @@
:refresh-url="getQrcodeRefreshUrl()" :refresh-url="getQrcodeRefreshUrl()"
:refresh-disabled="!formData.id || !formData.barCode" :refresh-disabled="!formData.id || !formData.barCode"
refresh-confirm-text="确认刷新该备件二维码吗?" refresh-confirm-text="确认刷新该备件二维码吗?"
:template-json="formData.templateJson"
:print-data="buildPrintData()"
@refresh-success="handleQrcodeRefreshSuccess" @refresh-success="handleQrcodeRefreshSuccess"
/> />
</el-form-item> </el-form-item>
@ -158,6 +160,7 @@ const formData = ref({
barCode: undefined, barCode: undefined,
isCode: undefined, isCode: undefined,
qrcodeUrl: undefined, qrcodeUrl: undefined,
templateJson: undefined,
categoryId: undefined, categoryId: undefined,
unitId: undefined, unitId: undefined,
status: undefined, status: undefined,
@ -201,7 +204,15 @@ const open = async (type: string, id?: number) => {
if (id) { if (id) {
formLoading.value = true formLoading.value = true
try { try {
formData.value = await ProductApi.getProduct(id) const productData = await ProductApi.getProduct(id)
const templateJson = productData?.templateJson
const parsedTemplateJson = typeof templateJson === 'string'
? JSON.parse(templateJson)
: templateJson
formData.value = {
...productData,
templateJson: parsedTemplateJson
}
} finally { } finally {
formLoading.value = false formLoading.value = false
} }
@ -223,6 +234,20 @@ const getQrcodeRefreshUrl = () => {
return `/erp/product/regenerate-code?id=${formData.value.id}&code=${encodeURIComponent(String(formData.value.barCode))}` return `/erp/product/regenerate-code?id=${formData.value.id}&code=${encodeURIComponent(String(formData.value.barCode))}`
} }
const buildPrintData = () => {
return {
id: formData.value.id,
barCode: formData.value.barCode,
name: formData.value.name,
standard: formData.value.standard,
unitId: formData.value.unitId,
safetyNumber: formData.value.safetyNumber,
status: formData.value.status,
remark: formData.value.remark,
qrcodeUrl: formData.value.qrcodeUrl
}
}
const handleQrcodeRefreshSuccess = async (data: any) => { const handleQrcodeRefreshSuccess = async (data: any) => {
if (!formData.value.id) return if (!formData.value.id) return
if (data?.qrcodeUrl) { if (data?.qrcodeUrl) {
@ -267,6 +292,7 @@ const resetForm = () => {
barCode: undefined, barCode: undefined,
isCode: true, isCode: true,
qrcodeUrl: undefined, qrcodeUrl: undefined,
templateJson: undefined,
categoryId: undefined, categoryId: undefined,
unitId: undefined, unitId: undefined,
status: CommonStatusEnum.ENABLE, status: CommonStatusEnum.ENABLE,

@ -97,6 +97,8 @@
:refresh-url="getQrcodeRefreshUrl()" :refresh-url="getQrcodeRefreshUrl()"
:refresh-disabled="!formData.id || !formData.code" :refresh-disabled="!formData.id || !formData.code"
refresh-confirm-text="确认刷新该模具二维码吗?" refresh-confirm-text="确认刷新该模具二维码吗?"
:template-json="formData.templateJson"
:print-data="buildPrintData()"
@refresh-success="handleQrcodeRefreshSuccess" @refresh-success="handleQrcodeRefreshSuccess"
/> />
</el-form-item> </el-form-item>
@ -159,6 +161,7 @@ const formData = ref({
status: undefined, status: undefined,
images: undefined, images: undefined,
qrcodeUrl: undefined, qrcodeUrl: undefined,
templateJson: undefined,
remark: undefined, remark: undefined,
isEnable: undefined, isEnable: undefined,
fileUrl: '', fileUrl: '',
@ -208,7 +211,15 @@ const open = async (type: string, id?: number, brandId: number) => {
if (id) { if (id) {
formLoading.value = true formLoading.value = true
try { try {
formData.value = await MoldBrandApi.getMold(id) const moldData = await MoldBrandApi.getMold(id)
const templateJson = moldData?.templateJson
const parsedTemplateJson = typeof templateJson === 'string'
? JSON.parse(templateJson)
: templateJson
formData.value = {
...moldData,
templateJson: parsedTemplateJson
}
} finally { } finally {
formLoading.value = false formLoading.value = false
} }
@ -231,6 +242,21 @@ const getQrcodeRefreshUrl = () => {
return `/erp/mold-brand/regenerate-code?id=${formData.value.id}&code=${encodeURIComponent(String(formData.value.code))}` return `/erp/mold-brand/regenerate-code?id=${formData.value.id}&code=${encodeURIComponent(String(formData.value.code))}`
} }
const buildPrintData = () => {
return {
id: formData.value.id,
code: formData.value.code,
name: formData.value.name,
unitId: formData.value.unitId,
machineId: formData.value.machineId,
useTime: formData.value.useTime,
inTime: formData.value.inTime,
status: formData.value.status,
remark: formData.value.remark,
qrcodeUrl: formData.value.qrcodeUrl
}
}
const handleQrcodeRefreshSuccess = async (data: any) => { const handleQrcodeRefreshSuccess = async (data: any) => {
if (!formData.value.id) return if (!formData.value.id) return
if (data?.qrcodeUrl) { if (data?.qrcodeUrl) {
@ -280,6 +306,7 @@ const resetForm = () => {
status: 3, status: 3,
images: undefined, images: undefined,
qrcodeUrl: undefined, qrcodeUrl: undefined,
templateJson: undefined,
remark: undefined, remark: undefined,
isEnable: true, isEnable: true,
fileUrl: '', fileUrl: '',

@ -163,6 +163,8 @@
:refresh-url="getQrcodeRefreshUrl()" :refresh-url="getQrcodeRefreshUrl()"
:refresh-disabled="!formData.id || !formData.barCode" :refresh-disabled="!formData.id || !formData.barCode"
refresh-confirm-text="确认刷新该产品二维码吗?" refresh-confirm-text="确认刷新该产品二维码吗?"
:template-json="formData.templateJson"
:print-data="buildPrintData()"
@refresh-success="handleQrcodeRefreshSuccess" @refresh-success="handleQrcodeRefreshSuccess"
/> />
</el-form-item> </el-form-item>
@ -296,6 +298,7 @@ const formData = ref({
barCode: undefined, barCode: undefined,
isCode: undefined, isCode: undefined,
qrcodeUrl: undefined, qrcodeUrl: undefined,
templateJson: undefined,
categoryId: undefined, categoryId: undefined,
unitId: undefined, unitId: undefined,
status: undefined, status: undefined,
@ -511,9 +514,14 @@ const open = async (type: string, id?: number) => {
['name', 'code'], ['name', 'code'],
'模具' '模具'
) )
const templateJson = productData?.templateJson
const parsedTemplateJson = typeof templateJson === 'string'
? JSON.parse(templateJson)
: templateJson
formData.value = { formData.value = {
...formData.value, ...formData.value,
...productData, ...productData,
templateJson: parsedTemplateJson,
devices, devices,
molds molds
} }
@ -538,6 +546,17 @@ const getQrcodeRefreshUrl = () => {
return `/erp/product/regenerate-code?id=${formData.value.id}&code=${encodeURIComponent(String(formData.value.barCode))}` return `/erp/product/regenerate-code?id=${formData.value.id}&code=${encodeURIComponent(String(formData.value.barCode))}`
} }
const buildPrintData = () => {
return {
id: formData.value.id,
name: formData.value.name,
barCode: formData.value.barCode,
standard: formData.value.standard,
remark: formData.value.remark,
qrcodeUrl: formData.value.qrcodeUrl
}
}
const handleQrcodeRefreshSuccess = async (data: any) => { const handleQrcodeRefreshSuccess = async (data: any) => {
if (!formData.value.id) return if (!formData.value.id) return
if (data?.qrcodeUrl) { if (data?.qrcodeUrl) {

@ -72,6 +72,8 @@
:refresh-url="getQrcodeRefreshUrl()" :refresh-url="getQrcodeRefreshUrl()"
:refresh-disabled="!formData.id || !formData.code" :refresh-disabled="!formData.id || !formData.code"
refresh-confirm-text="确认刷新该关键件二维码吗?" refresh-confirm-text="确认刷新该关键件二维码吗?"
:template-json="formData.templateJson"
:print-data="buildPrintData()"
@refresh-success="handleQrcodeRefreshSuccess" @refresh-success="handleQrcodeRefreshSuccess"
/> />
</el-form-item> </el-form-item>
@ -123,6 +125,7 @@ const formData = ref<Partial<CriticalComponentVO>>({
count: undefined, count: undefined,
remark: undefined, remark: undefined,
qrcodeUrl: undefined, qrcodeUrl: undefined,
templateJson: undefined,
images: undefined images: undefined
}) })
@ -152,7 +155,9 @@ const resetForm = () => {
description: undefined, description: undefined,
count: undefined, count: undefined,
remark: undefined, remark: undefined,
qrcodeUrl: undefined qrcodeUrl: undefined,
templateJson: undefined,
images: undefined
} }
formRef.value?.resetFields?.() formRef.value?.resetFields?.()
} }
@ -169,6 +174,18 @@ const getQrcodeRefreshUrl = () => {
return `/mes/critical-component/regenerate-code?id=${formData.value.id}&code=${encodeURIComponent(String(formData.value.code))}` return `/mes/critical-component/regenerate-code?id=${formData.value.id}&code=${encodeURIComponent(String(formData.value.code))}`
} }
const buildPrintData = () => {
return {
id: formData.value.id,
code: formData.value.code,
name: formData.value.name,
description: formData.value.description,
count: formData.value.count,
remark: formData.value.remark,
qrcodeUrl: formData.value.qrcodeUrl
}
}
const handleQrcodeRefreshSuccess = async (data: any) => { const handleQrcodeRefreshSuccess = async (data: any) => {
if (!formData.value.id) return if (!formData.value.id) return
if (data?.qrcodeUrl) { if (data?.qrcodeUrl) {
@ -189,6 +206,10 @@ const open = async (type: 'create' | 'update', id?: number) => {
formLoading.value = true formLoading.value = true
try { try {
const detail = await CriticalComponentApi.getCriticalComponent(id) const detail = await CriticalComponentApi.getCriticalComponent(id)
const templateJson = detail?.templateJson
const parsedTemplateJson = typeof templateJson === 'string'
? JSON.parse(templateJson)
: templateJson
formData.value = { formData.value = {
id: detail?.id, id: detail?.id,
code: detail?.code, code: detail?.code,
@ -199,6 +220,7 @@ const open = async (type: 'create' | 'update', id?: number) => {
count: detail?.count, count: detail?.count,
remark: detail?.remark, remark: detail?.remark,
qrcodeUrl: detail?.qrcodeUrl, qrcodeUrl: detail?.qrcodeUrl,
templateJson: parsedTemplateJson,
images: detail?.images images: detail?.images
} }
} finally { } finally {

@ -203,7 +203,10 @@
:print-max-width="220" :empty-text="t('EquipmentManagement.EquipmentLedger.qrcodeEmpty')" :print-max-width="220" :empty-text="t('EquipmentManagement.EquipmentLedger.qrcodeEmpty')"
:error-text="t('EquipmentManagement.EquipmentLedger.qrcodeLoadError')" :error-text="t('EquipmentManagement.EquipmentLedger.qrcodeLoadError')"
:refresh-url="getQrcodeRefreshUrl()" :refresh-disabled="!formData.id || !formData.deviceCode" :refresh-url="getQrcodeRefreshUrl()" :refresh-disabled="!formData.id || !formData.deviceCode"
refresh-confirm-text="确认刷新该设备二维码吗?" @refresh-success="handleQrcodeRefreshSuccess" /> refresh-confirm-text="确认刷新该设备二维码吗?"
:template-json="formData.templateJson"
:print-data="buildPrintData()"
@refresh-success="handleQrcodeRefreshSuccess" />
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -498,6 +501,7 @@ const initFormData = () => ({
beijianIds: [] as number[], beijianIds: [] as number[],
fileUrl: undefined, fileUrl: undefined,
qrcodeUrl: undefined, qrcodeUrl: undefined,
templateJson: undefined,
sort: undefined, sort: undefined,
dvId: undefined dvId: undefined
}) })
@ -628,6 +632,20 @@ const getQrcodeRefreshUrl = () => {
return `/mes/device-ledger/regenerate-code?id=${formData.value.id}&code=${encodeURIComponent(String(formData.value.deviceCode))}` return `/mes/device-ledger/regenerate-code?id=${formData.value.id}&code=${encodeURIComponent(String(formData.value.deviceCode))}`
} }
const buildPrintData = () => {
return {
id: formData.value.id,
deviceCode: formData.value.deviceCode,
deviceName: formData.value.deviceName,
deviceSpec: formData.value.deviceSpec,
deviceBrand: formData.value.deviceBrand,
deviceModel: formData.value.deviceModel,
deviceLocation: formData.value.deviceLocation,
remark: formData.value.remark,
qrcodeUrl: formData.value.qrcodeUrl
}
}
const handleQrcodeRefreshSuccess = async (data: any) => { const handleQrcodeRefreshSuccess = async (data: any) => {
if (!formData.value.id) return if (!formData.value.id) return
if (data?.qrcodeUrl) { if (data?.qrcodeUrl) {
@ -677,9 +695,14 @@ const open = async (type: string, id?: number, defaultDeviceTypeId?: number) =>
formLoading.value = true formLoading.value = true
try { try {
const detail = await DeviceLedgerApi.getDeviceLedger(id) const detail = await DeviceLedgerApi.getDeviceLedger(id)
const templateJson = (detail as any)?.templateJson
const parsedTemplateJson = typeof templateJson === 'string'
? JSON.parse(templateJson)
: templateJson
formData.value = { formData.value = {
...initFormData(), ...initFormData(),
...(detail as any), ...(detail as any),
templateJson: parsedTemplateJson,
isCode: (detail as any)?.isCode ?? false, isCode: (detail as any)?.isCode ?? false,
isScheduled: normalizeNumberish((detail as any)?.isScheduled ?? (detail as any)?.isScheduled) ?? 0, isScheduled: normalizeNumberish((detail as any)?.isScheduled ?? (detail as any)?.isScheduled) ?? 0,
ratedCapacity: normalizeNumberish((detail as any)?.ratedCapacity), ratedCapacity: normalizeNumberish((detail as any)?.ratedCapacity),

@ -0,0 +1,440 @@
<template>
<Dialog v-model="dialogVisible" :title="dialogTitle" width="92%" :fullscreen="false" top="4vh">
<div class="hiprint-preview">
<div class="hiprint-toolbar">
<div class="hiprint-paper">
<el-button
v-for="(value, type) in paperTypes"
:key="type"
size="small"
:type="curPaperType === type ? 'primary' : 'default'"
@click="setPaper(type, value)"
>
{{ type }}
</el-button>
<el-popover placement="bottom-start" :width="260" trigger="click" v-model:visible="paperPopVisible">
<template #reference>
<el-button size="small" :type="curPaperType === 'other' ? 'primary' : 'default'">自定义纸张</el-button>
</template>
<div class="paper-pop">
<div class="paper-pop-title">设置纸张宽高(mm)</div>
<div class="paper-pop-form">
<el-input-number v-model="paperWidth" :precision="1" :min="1" controls-position="right" />
<span>x</span>
<el-input-number v-model="paperHeight" :precision="1" :min="1" controls-position="right" />
</div>
<el-button class="mt-8px" size="small" type="primary" @click="setPaperOther"></el-button>
</div>
</el-popover>
</div>
<div class="hiprint-zoom">
<el-button size="small" @click="changeScale(false)">
<Icon icon="ep:zoom-out" />
</el-button>
<div class="zoom-value">{{ (scaleValue * 100).toFixed(0) }}%</div>
<el-button size="small" @click="changeScale(true)">
<Icon icon="ep:zoom-in" />
</el-button>
</div>
<el-button type="primary" size="small" :loading="saveLoading" @click="handleSave">
<Icon icon="ep:check" class="mr-4px" />
{{ t('common.save') }}
</el-button>
</div>
<div class="hiprint-body">
<div class="hiprint-left">
<div class="hiprint-title">基础元素</div>
<div :id="dragContainerId" class="hiprint-drag-wrap">
<div v-for="item in baseElements" :key="item.tid" class="ep-draggable-item hiprint-item" :tid="item.tid">
<span>{{ item.label }}</span>
</div>
</div>
<div v-if="barcodeElements.length" class="hiprint-title" style="margin-top: 8px"></div>
<div v-if="barcodeElements.length" :id="barcodeDragContainerId" class="hiprint-drag-wrap">
<div v-for="item in barcodeElements" :key="item.tid" class="ep-draggable-item hiprint-item" :tid="item.tid">
<span style="display: block; text-align: center;" v-html="item.label"></span>
</div>
</div>
</div>
<div class="hiprint-center">
<div :id="designerContainerId"></div>
</div>
<div class="hiprint-right">
<div :id="settingContainerId"></div>
</div>
</div>
</div>
</Dialog>
</template>
<script setup lang="ts">
import { defaultElementTypeProvider, hiprint } from 'vue-plugin-hiprint'
import { PrintTemplateApi } from '@/api/mes/printtemplate'
import { getSimpleDictDataList } from '@/api/system/dict/dict.data'
const { t } = useI18n()
const message = useMessage()
const baseElements = [
{ tid: 'defaultModule.text', label: '文本' },
{ tid: 'defaultModule.image', label: '图片' },
{ tid: 'qrcodeModule.qrcode', label: '二维码' },
{ tid: 'defaultModule.longText', label: '长文' },
{ tid: 'defaultModule.table', label: '表格' },
{ tid: 'defaultModule.hline', label: '横线' },
{ tid: 'defaultModule.vline', label: '竖线' },
{ tid: 'defaultModule.rect', label: '矩形' },
{ tid: 'defaultModule.oval', label: '圆形' }
]
const barcodeElements = ref<{ tid: string; label: string }[]>([])
const barcodeDictData = ref<any[]>([])
const loadBarcodeDictData = async () => {
try {
const res = await getSimpleDictDataList()
const filtered = (res || []).filter((item: any) => item.dictType === 'print_template_type')
barcodeDictData.value = filtered
const elements: { tid: string; label: string }[] = []
filtered.forEach((item: any) => {
elements.push({ tid: `barcodeModule.${item.value}_qrcode`, label: `${item.label}<br/>二维码` })
elements.push({ tid: `barcodeModule.${item.value}_text`, label: `${item.label}<br/>文本` })
})
barcodeElements.value = elements
} catch (e) {
console.error('加载条码字典数据失败', e)
}
}
const barcodeProvider = function () {
const addElementTypes = function (context: any) {
context.removePrintElementTypes('barcodeModule')
if (!barcodeDictData.value.length) return
const groups: any[] = []
barcodeDictData.value.forEach((item: any) => {
const remark = item.remark || ''
const parts = remark.split(',').map((s: string) => s.trim())
const qrcodeField = parts[0] || ''
const textField = parts[1] || ''
groups.push(
new hiprint.PrintElementTypeGroup(item.label, [
{
tid: `barcodeModule.${item.value}_qrcode`,
title: `${item.label}`,
type: 'image',
options: {
field: qrcodeField,
src: 'https://p119-minio-upload.ngsk.tech:7001/besure/productmaterial/qr/2026-04-17/295.png',
testData: 'https://p119-minio-upload.ngsk.tech:7001/besure/productmaterial/qr/2026-04-17/295.png',
width: 80,
height: 80,
title: `${item.label}<br/>二维码`
}
},
{
tid: `barcodeModule.${item.value}_text`,
title: `${item.label}`,
type: 'text',
options: {
field: textField,
testData: '',
title: `${item.label}<br/>文本`,
fontSize: 10,
textAlign: 'center',
width: 80,
height: 16
}
}
])
)
})
context.addPrintElementTypes('barcodeModule', groups)
}
return { addElementTypes }
}
const qrcodeProvider = function () {
const addElementTypes = function (context: any) {
context.removePrintElementTypes('qrcodeModule')
context.addPrintElementTypes('qrcodeModule', [
new hiprint.PrintElementTypeGroup('二维码', [
{
tid: 'qrcodeModule.qrcode',
title: '二维码',
type: 'image',
options: {
field: '',
src: 'https://p119-minio-upload.ngsk.tech:7001/besure/productmaterial/qr/2026-04-17/295.png',
testData: 'https://p119-minio-upload.ngsk.tech:7001/besure/productmaterial/qr/2026-04-17/295.png',
width: 80,
height: 80,
title: '二维码'
}
}
])
])
}
return { addElementTypes }
}
const dialogVisible = ref(false)
const dialogTitle = ref('模板配置')
const saveLoading = ref(false)
const currentRow = ref<any>(null)
const currentTemplateJson = ref<Record<string, any> | undefined>(undefined)
const instanceId = `hiprint-designer-${Math.random().toString(36).slice(2)}`
const dragContainerId = `${instanceId}-drag`
const barcodeDragContainerId = `${instanceId}-barcode-drag`
const designerContainerId = `${instanceId}-designer`
const settingContainerId = `${instanceId}-setting`
const paperTypes = {
A3: { width: 420, height: 296.6 },
A4: { width: 210, height: 296.6 },
A5: { width: 210, height: 147.6 },
B3: { width: 500, height: 352.6 },
B4: { width: 250, height: 352.6 },
B5: { width: 250, height: 175.6 }
}
const curPaper = ref({
type: 'A4',
width: 210,
height: 296.6
})
const paperPopVisible = ref(false)
const paperWidth = ref(220)
const paperHeight = ref(80)
const curPaperType = computed(() => {
let type = 'other'
for (const [key, value] of Object.entries(paperTypes)) {
if (value.width === curPaper.value.width && value.height === curPaper.value.height) {
type = key
break
}
}
return type
})
const scaleValue = ref(1)
const scaleMax = 5
const scaleMin = 0.5
let hiprintInited = false
let hiprintTemplate: any
const ensureInit = () => {
if (hiprintInited) {
return
}
hiprint.init({
providers: [new defaultElementTypeProvider(), qrcodeProvider(), barcodeProvider()]
})
hiprintInited = true
}
const buildLeftElement = () => {
const jquery = (window as any).$
if (!jquery) {
message.warning('未检测到 jQuery无法加载拖拽元素')
return
}
hiprint.PrintElementTypeManager.buildByHtml(jquery(`#${dragContainerId} .ep-draggable-item`))
if (barcodeElements.value.length) {
hiprint.PrintElementTypeManager.buildByHtml(jquery(`#${barcodeDragContainerId} .ep-draggable-item`))
}
}
const buildDesigner = () => {
const jquery = (window as any).$
if (!jquery) {
message.warning('未检测到 jQuery无法初始化打印设计器')
return
}
jquery(`#${designerContainerId}`).empty()
const template = currentTemplateJson.value || undefined
hiprintTemplate = new hiprint.PrintTemplate({
template,
settingContainer: `#${settingContainerId}`
})
hiprintTemplate.design(`#${designerContainerId}`)
setPaper(curPaperType.value, { width: curPaper.value.width, height: curPaper.value.height })
hiprintTemplate.zoom(scaleValue.value)
}
const setPaper = (type: string, value: { width: number; height: number }) => {
if (!hiprintTemplate) {
return
}
const width = Number(value.width)
const height = Number(value.height)
curPaper.value = { type, width, height }
hiprintTemplate.setPaper(width, height)
}
const setPaperOther = () => {
paperPopVisible.value = false
setPaper('other', { width: Number(paperWidth.value), height: Number(paperHeight.value) })
}
const changeScale = (isZoomIn: boolean) => {
if (!hiprintTemplate) {
return
}
let nextScale = scaleValue.value
if (isZoomIn) {
nextScale += 0.1
if (nextScale > scaleMax) nextScale = scaleMax
} else {
nextScale -= 0.1
if (nextScale < scaleMin) nextScale = scaleMin
}
scaleValue.value = nextScale
hiprintTemplate.zoom(nextScale)
}
const handleSave = async () => {
if (!hiprintTemplate) {
return
}
const templateJson = hiprintTemplate.getJson()
saveLoading.value = true
try {
await PrintTemplateApi.updatePrintTemplate({
id: currentRow.value.id,
templateCode: currentRow.value.templateCode,
templateName: currentRow.value.templateName,
templateType: currentRow.value.templateType,
templateJson: JSON.stringify(templateJson),
} as any)
message.success(t('common.updateSuccess'))
dialogVisible.value = false
emit('success')
} finally {
saveLoading.value = false
}
}
const resetState = () => {
scaleValue.value = 1
curPaper.value = {
type: 'A4',
width: 210,
height: 296.6
}
paperWidth.value = 220
paperHeight.value = 80
paperPopVisible.value = false
}
const open = async (row: any) => {
currentRow.value = row
dialogTitle.value = `${t('TemplateManagement.PrintTemplate.designTitle')}${row.templateName ? ' - ' + row.templateName : ''}`
currentTemplateJson.value = row.templateJson ? (typeof row.templateJson === 'string' ? JSON.parse(row.templateJson) : row.templateJson) : undefined
resetState()
await loadBarcodeDictData()
dialogVisible.value = true
await nextTick()
ensureInit()
buildLeftElement()
buildDesigner()
}
const emit = defineEmits(['success'])
defineExpose({ open })
</script>
<style scoped lang="scss">
.hiprint-preview {
width: 100%;
}
.hiprint-toolbar {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;
}
.hiprint-paper {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.hiprint-zoom {
display: flex;
align-items: center;
gap: 4px;
}
.zoom-value {
width: 56px;
text-align: center;
font-size: 13px;
color: var(--el-text-color-regular);
}
.paper-pop-title {
font-size: 14px;
font-weight: 600;
}
.paper-pop-form {
margin-top: 8px;
display: flex;
align-items: center;
gap: 8px;
}
.hiprint-body {
height: 75vh;
display: grid;
grid-template-columns: 220px 1fr 300px;
gap: 12px;
}
.hiprint-left,
.hiprint-center,
.hiprint-right {
background: var(--el-bg-color);
border: 1px solid var(--el-border-color-light);
border-radius: 8px;
overflow: auto;
}
.hiprint-center {
padding: 16px;
}
.hiprint-right {
padding: 12px;
}
.hiprint-title {
padding: 10px 10px 0;
font-weight: 600;
}
.hiprint-drag-wrap {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
padding: 10px;
}
.hiprint-item {
min-height: 56px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
border: 1px solid var(--el-border-color);
background: var(--el-fill-color-light);
color: var(--el-text-color-primary);
cursor: grab;
}
</style>

@ -0,0 +1,125 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="120px" v-loading="formLoading">
<el-form-item prop="templateCode">
<template #label>
<span>
{{ t('TemplateManagement.PrintTemplate.templateCode') }}
<el-tooltip :content="t('TemplateManagement.PrintTemplate.templateCodeTooltip')" placement="top">
<Icon icon="ep:question-filled" />
</el-tooltip>
</span>
</template>
<el-row :gutter="10" style="width: 100%;">
<el-col :xs="24" :sm="18" :md="16" :lg="14" :xl="12">
<el-input v-model="formData.templateCode" :placeholder="t('TemplateManagement.PrintTemplate.placeholderTemplateCode')" :disabled="formData.isAutoCode || formType === 'update'" />
</el-col>
<el-col :xs="24" :sm="6" :md="4" :lg="3" :xl="2">
<el-switch v-model="formData.isAutoCode" :disabled="formType === 'update'" />
</el-col>
</el-row>
</el-form-item>
<el-form-item :label="t('TemplateManagement.PrintTemplate.templateName')" prop="templateName">
<el-input v-model="formData.templateName" :placeholder="t('TemplateManagement.PrintTemplate.placeholderTemplateName')" />
</el-form-item>
<el-form-item :label="t('TemplateManagement.PrintTemplate.templateType')" prop="templateType">
<el-select v-model="formData.templateType" :placeholder="t('TemplateManagement.PrintTemplate.placeholderTemplateType')" class="!w-full">
<el-option v-for="dict in getIntDictOptions('print_template_type')" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item :label="t('TemplateManagement.PrintTemplate.isEnable')" prop="isEnable">
<el-switch v-model="formData.isEnable" />
</el-form-item>
<el-form-item :label="t('TemplateManagement.PrintTemplate.remark')" prop="remark">
<el-input v-model="formData.remark" :placeholder="t('TemplateManagement.PrintTemplate.placeholderRemark')" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">{{ t('common.ok') }}</el-button>
<el-button @click="dialogVisible = false">{{ t('common.cancel') }}</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { PrintTemplateApi, PrintTemplateVO } from '@/api/mes/printtemplate'
import { getIntDictOptions } from '@/utils/dict'
defineOptions({ name: 'PrintTemplateForm' })
const { t } = useI18n()
const message = useMessage()
const dialogVisible = ref(false)
const dialogTitle = ref('')
const formLoading = ref(false)
const formType = ref('')
const formData = ref({
id: undefined,
templateCode: undefined,
isAutoCode: true,
templateName: undefined,
templateType: undefined,
remark: undefined,
isEnable: true,
})
const formRules = computed(() => ({
templateCode: formData.value.isAutoCode ? [] : [{ required: true, message: t('TemplateManagement.PrintTemplate.validatorCodeRequired'), trigger: 'blur' }],
templateName: [{ required: true, message: t('TemplateManagement.PrintTemplate.validatorNameRequired'), trigger: 'blur' }],
templateType: [{ required: true, message: t('TemplateManagement.PrintTemplate.validatorTypeRequired'), trigger: 'change' }],
}))
const formRef = ref()
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
if (id) {
formLoading.value = true
try {
formData.value = await PrintTemplateApi.getPrintTemplate(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open })
const emit = defineEmits(['success'])
const submitForm = async () => {
await formRef.value.validate()
formLoading.value = true
try {
const data = formData.value as unknown as PrintTemplateVO
const { isAutoCode, ...submitData } = data as any
if (isAutoCode) {
delete submitData.templateCode
}
if (formType.value === 'create') {
await PrintTemplateApi.createPrintTemplate(submitData)
message.success(t('common.createSuccess'))
} else {
await PrintTemplateApi.updatePrintTemplate(submitData)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
emit('success')
} finally {
formLoading.value = false
}
}
const resetForm = () => {
formData.value = {
id: undefined,
templateCode: undefined,
isAutoCode: true,
templateName: undefined,
templateType: undefined,
remark: undefined,
isEnable: true,
}
formRef.value?.resetFields()
}
</script>

@ -0,0 +1,169 @@
<template>
<ContentWrap>
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" min-label-width="68px">
<el-form-item :label="t('TemplateManagement.PrintTemplate.templateCode')" prop="templateCode">
<el-input v-model="queryParams.templateCode"
:placeholder="t('TemplateManagement.PrintTemplate.placeholderTemplateCode')" clearable
@keyup.enter="handleQuery" class="!w-240px" />
</el-form-item>
<el-form-item :label="t('TemplateManagement.PrintTemplate.templateName')" prop="templateName">
<el-input v-model="queryParams.templateName"
:placeholder="t('TemplateManagement.PrintTemplate.placeholderTemplateName')" clearable
@keyup.enter="handleQuery" class="!w-240px" />
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> {{ t('common.query') }}
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> {{ t('common.reset') }}
</el-button>
<el-button type="primary" plain @click="openForm('create')">
<Icon icon="ep:plus" class="mr-5px" /> {{ t('action.add') }}
</el-button>
<el-button type="success" plain @click="handleExport" :loading="exportLoading">
<Icon icon="ep:download" class="mr-5px" /> {{ t('action.export') }}
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" row-key="id"
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" fixed="left" reserve-selection />
<el-table-column :label="t('TemplateManagement.PrintTemplate.templateCode')" align="center" prop="templateCode"
sortable />
<el-table-column :label="t('TemplateManagement.PrintTemplate.templateName')" align="center" prop="templateName"
sortable />
<el-table-column :label="t('TemplateManagement.PrintTemplate.templateType')" align="center" prop="templateType"
sortable>
<template #default="scope">
<dict-tag type="print_template_type" :value="scope.row.templateType" />
</template>
</el-table-column>
<el-table-column :label="t('TemplateManagement.PrintTemplate.isEnable')" align="center" prop="isEnable" sortable>
<template #default="scope">
<el-tag :type="scope.row.isEnable ? 'success' : 'danger'">
{{ scope.row.isEnable ? t('TemplateManagement.PrintTemplate.enabled') :
t('TemplateManagement.PrintTemplate.disabled') }}
</el-tag>
</template>
</el-table-column>
<el-table-column :label="t('TemplateManagement.PrintTemplate.templateJson')" align="center" prop="templateJson"
min-width="200px" :show-overflow-tooltip="true" />
<el-table-column :label="t('TemplateManagement.PrintTemplate.remark')" align="center" prop="remark" />
<el-table-column :label="t('TemplateManagement.PrintTemplate.createTime')" align="center" prop="createTime"
:formatter="dateFormatter" width="180px" sortable />
<el-table-column :label="t('TemplateManagement.PrintTemplate.operate')" align="center" min-width="160px">
<template #default="scope">
<el-button link type="primary" @click="openForm('update', scope.row.id)">
{{ t('action.edit') }}
</el-button>
<el-button link type="warning" @click="openDesigner(scope.row)">
{{ t('TemplateManagement.PrintTemplate.design') }}
</el-button>
<el-button link type="danger" @click="handleDelete(scope.row.id)">
{{ t('action.del') }}
</el-button>
</template>
</el-table-column>
</el-table>
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
@pagination="getList" />
</ContentWrap>
<PrintTemplateForm ref="formRef" @success="getList" />
<PrintTemplateDesigner ref="designerRef" @success="getList" />
</template>
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { PrintTemplateApi, PrintTemplateVO } from '@/api/mes/printtemplate'
import { DictTag } from '@/components/DictTag'
import PrintTemplateForm from './PrintTemplateForm.vue'
import PrintTemplateDesigner from './PrintTemplateDesigner.vue'
defineOptions({ name: 'PrintTemplate' })
const message = useMessage()
const { t } = useI18n()
const loading = ref(true)
const list = ref<PrintTemplateVO[]>([])
const total = ref(0)
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
templateCode: undefined,
templateName: undefined,
})
const queryFormRef = ref()
const exportLoading = ref(false)
const selectedIds = ref<number[]>([])
const getList = async () => {
loading.value = true
try {
const data = await PrintTemplateApi.getPrintTemplatePage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
const designerRef = ref()
const openDesigner = (row: any) => {
designerRef.value.open(row)
}
const handleDelete = async (id: number) => {
try {
await message.delConfirm()
await PrintTemplateApi.deletePrintTemplate(id)
message.success(t('common.delSuccess'))
await getList()
} catch { }
}
const handleExport = async () => {
try {
await message.exportConfirm()
exportLoading.value = true
const params = {
...queryParams,
ids: selectedIds.value.length ? selectedIds.value.join(',') : undefined,
}
const data = await PrintTemplateApi.exportPrintTemplate(params)
download.excel(data, t('TemplateManagement.PrintTemplate.exportFilename'))
} catch {
} finally {
exportLoading.value = false
}
}
const handleSelectionChange = (rows: any[]) => {
selectedIds.value = rows?.map((row) => row.id).filter((id) => id !== undefined) ?? []
}
onMounted(() => {
getList()
})
</script>
Loading…
Cancel
Save