feat: 仓库简易打印、工作站标签打印、生产订单打印工序修复
- 仓库管理(mom-frontend-vue2): 新增简易标签打印,不依赖MinIO,使用前端qrcode+window.print - 工作站(erp-frontend-vue): 新增WorkstationLabelPrint组件,支持批量打印工作站标签 - 生产订单: handlePrint改用getProcessTasksByWorkorder,从工艺路线获取工序数据,解决无pro_task时打印无数据问题 Made-with: Cursor
This commit is contained in:
@@ -152,6 +152,10 @@ export function getWorkOrderDetail(workorderId: number): Promise<WorkOrder> {
|
|||||||
if (data.requestDate && !data.productionDate) {
|
if (data.requestDate && !data.productionDate) {
|
||||||
data.productionDate = data.requestDate
|
data.productionDate = data.requestDate
|
||||||
}
|
}
|
||||||
|
// 后端返回 salesOrderCode(pp_number),确保前端有值
|
||||||
|
if (data.ppNumber && !data.salesOrderCode) {
|
||||||
|
data.salesOrderCode = data.ppNumber
|
||||||
|
}
|
||||||
return data
|
return data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -260,7 +264,7 @@ export interface ProTask {
|
|||||||
updateTime?: string
|
updateTime?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 根据工单 ID 获取工序任务列表 */
|
/** 根据工单 ID 获取工序任务列表(pro_task 表,需已排产) */
|
||||||
export function getTaskListByWorkorder(workorderId: number): Promise<ProTask[]> {
|
export function getTaskListByWorkorder(workorderId: number): Promise<ProTask[]> {
|
||||||
return request.get('/mes/pro/protask/list', {
|
return request.get('/mes/pro/protask/list', {
|
||||||
params: { workorderId, pageNum: 1, pageSize: 500 }
|
params: { workorderId, pageNum: 1, pageSize: 500 }
|
||||||
@@ -269,6 +273,14 @@ export function getTaskListByWorkorder(workorderId: number): Promise<ProTask[]>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 根据工单 ID 获取工序列表(基于工艺路线,不依赖 pro_task,用于打印等) */
|
||||||
|
export function getProcessTasksByWorkorder(workorderId: number): Promise<ProTask[]> {
|
||||||
|
return request.get(`/mes/pro/workorder/${workorderId}/processTasks`).then((res: any) => {
|
||||||
|
const data = res.data ?? res
|
||||||
|
return Array.isArray(data) ? data : []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// ============ 工艺路线选择 API ============
|
// ============ 工艺路线选择 API ============
|
||||||
|
|
||||||
/** 工艺路线列表(选择弹窗,不默认过滤 enableFlag 以避免列表为空) */
|
/** 工艺路线列表(选择弹窗,不默认过滤 enableFlag 以避免列表为空) */
|
||||||
|
|||||||
170
erp-frontend-vue/src/components/print/WorkstationLabelPrint.vue
Normal file
170
erp-frontend-vue/src/components/print/WorkstationLabelPrint.vue
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:model-value="visible"
|
||||||
|
@update:model-value="$emit('update:visible', $event)"
|
||||||
|
fullscreen
|
||||||
|
destroy-on-close
|
||||||
|
:show-close="false"
|
||||||
|
class="workstation-label-print-dialog"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="label-print-actions">
|
||||||
|
<el-button type="primary" @click="doPrint">打印</el-button>
|
||||||
|
<el-button @click="$emit('update:visible', false)">关闭</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="label-print-content" ref="printContentRef">
|
||||||
|
<div v-if="workstations.length === 0" class="label-empty">暂无工作站数据</div>
|
||||||
|
<div v-else class="label-grid">
|
||||||
|
<div
|
||||||
|
v-for="(ws, idx) in workstations"
|
||||||
|
:key="ws.workstationId ?? idx"
|
||||||
|
class="label-card"
|
||||||
|
>
|
||||||
|
<div class="label-qr">
|
||||||
|
<QrCode :value="ws.workstationCode || String(ws.workstationId || '')" :size="72" />
|
||||||
|
</div>
|
||||||
|
<div class="label-name">{{ ws.workstationName || '-' }}</div>
|
||||||
|
<div class="label-code">{{ ws.workstationCode || '-' }}</div>
|
||||||
|
<div class="label-info">
|
||||||
|
<span v-if="ws.workshopName">车间:{{ ws.workshopName }}</span>
|
||||||
|
<span v-if="ws.processName">工序:{{ ws.processName }}</span>
|
||||||
|
<span v-if="ws.workstationAddress" class="label-address">{{ ws.workstationAddress }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import QrCode from './QrCode.vue'
|
||||||
|
import type { Workstation } from '@/api/masterdata/workstation'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
visible: boolean
|
||||||
|
workstations: Workstation[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
(e: 'update:visible', val: boolean): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function doPrint() {
|
||||||
|
window.print()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@media print {
|
||||||
|
body > *:not(.el-overlay) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
.el-overlay {
|
||||||
|
position: static !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
background: none !important;
|
||||||
|
}
|
||||||
|
.el-dialog__wrapper {
|
||||||
|
position: static !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
.el-dialog {
|
||||||
|
box-shadow: none !important;
|
||||||
|
border: none !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
.el-dialog__header,
|
||||||
|
.el-dialog__footer,
|
||||||
|
.label-print-actions {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
.el-dialog__body {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.label-print-content {
|
||||||
|
padding: 8mm !important;
|
||||||
|
}
|
||||||
|
.label-card {
|
||||||
|
page-break-inside: avoid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.label-print-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-print-content {
|
||||||
|
padding: 24px;
|
||||||
|
background: #fff;
|
||||||
|
font-family: 'Microsoft YaHei', 'SimSun', sans-serif;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-empty {
|
||||||
|
text-align: center;
|
||||||
|
padding: 48px;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 16px;
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-card {
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 12px;
|
||||||
|
text-align: center;
|
||||||
|
min-height: 120px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-qr {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-code {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #606266;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-info {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #909399;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-address {
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -32,6 +32,9 @@
|
|||||||
<el-button type="danger" :disabled="multiple" @click="handleDelete()">
|
<el-button type="danger" :disabled="multiple" @click="handleDelete()">
|
||||||
<el-icon><Delete /></el-icon>删除
|
<el-icon><Delete /></el-icon>删除
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button @click="handlePrintLabels">
|
||||||
|
<el-icon><Printer /></el-icon>打印标签
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table
|
<el-table
|
||||||
@@ -115,13 +118,20 @@
|
|||||||
<el-button type="primary" @click="submitForm">确定</el-button>
|
<el-button type="primary" @click="submitForm">确定</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 工作站标签打印 -->
|
||||||
|
<WorkstationLabelPrint
|
||||||
|
v-model:visible="labelPrintVisible"
|
||||||
|
:workstations="printWorkstations"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, onMounted } from 'vue'
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { Search, Refresh, Plus, Edit, Delete } from '@element-plus/icons-vue'
|
import { Search, Refresh, Plus, Edit, Delete, Printer } from '@element-plus/icons-vue'
|
||||||
|
import WorkstationLabelPrint from '@/components/print/WorkstationLabelPrint.vue'
|
||||||
import {
|
import {
|
||||||
listWorkstation,
|
listWorkstation,
|
||||||
getWorkstation,
|
getWorkstation,
|
||||||
@@ -144,6 +154,8 @@ const total = ref(0)
|
|||||||
const workstationList = ref<Workstation[]>([])
|
const workstationList = ref<Workstation[]>([])
|
||||||
const workshopOptions = ref<Workshop[]>([])
|
const workshopOptions = ref<Workshop[]>([])
|
||||||
const ids = ref<number[]>([])
|
const ids = ref<number[]>([])
|
||||||
|
const labelPrintVisible = ref(false)
|
||||||
|
const printWorkstations = ref<Workstation[]>([])
|
||||||
|
|
||||||
const queryParams = reactive({
|
const queryParams = reactive({
|
||||||
workstationCode: '',
|
workstationCode: '',
|
||||||
@@ -209,6 +221,17 @@ function handleSelectionChange(selection: Workstation[]) {
|
|||||||
multiple.value = selection.length === 0
|
multiple.value = selection.length === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handlePrintLabels() {
|
||||||
|
const selected = workstationList.value.filter((row) => ids.value.includes(row.workstationId!))
|
||||||
|
const toPrint = selected.length > 0 ? selected : workstationList.value
|
||||||
|
if (toPrint.length === 0) {
|
||||||
|
ElMessage.warning('请先选择要打印的工作站')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
printWorkstations.value = toPrint
|
||||||
|
labelPrintVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
function resetForm() {
|
function resetForm() {
|
||||||
form.workstationId = undefined
|
form.workstationId = undefined
|
||||||
form.workstationCode = ''
|
form.workstationCode = ''
|
||||||
|
|||||||
@@ -469,7 +469,7 @@ import {
|
|||||||
confirmWorkOrder,
|
confirmWorkOrder,
|
||||||
unconfirmWorkOrder,
|
unconfirmWorkOrder,
|
||||||
getIssueListByWorkorder,
|
getIssueListByWorkorder,
|
||||||
getTaskListByWorkorder,
|
getProcessTasksByWorkorder,
|
||||||
STATUS_MAP,
|
STATUS_MAP,
|
||||||
WORKORDER_TYPE_OPTIONS,
|
WORKORDER_TYPE_OPTIONS,
|
||||||
BUSINESS_STATUS_OPTIONS,
|
BUSINESS_STATUS_OPTIONS,
|
||||||
@@ -731,7 +731,7 @@ async function handleSave() {
|
|||||||
} else {
|
} else {
|
||||||
await updateWorkOrder(formData)
|
await updateWorkOrder(formData)
|
||||||
ElMessage.success('保存成功')
|
ElMessage.success('保存成功')
|
||||||
await loadBomList()
|
await loadWorkOrderData()
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error !== false) console.error('保存失败:', error)
|
if (error !== false) console.error('保存失败:', error)
|
||||||
@@ -800,6 +800,7 @@ const printConfig = computed<PrintConfig>(() => ({
|
|||||||
{ label: '跟单号', value: formData.salesOrderCode || '' },
|
{ label: '跟单号', value: formData.salesOrderCode || '' },
|
||||||
{ label: '物料编码', value: formData.productCode || '' },
|
{ label: '物料编码', value: formData.productCode || '' },
|
||||||
{ label: '工艺路线', value: formData.routeName || '' },
|
{ label: '工艺路线', value: formData.routeName || '' },
|
||||||
|
{ label: '加工时长', value: formData.processTime != null ? `${formData.processTime}分钟` : '' },
|
||||||
{ label: '单据日期', value: formData.orderDate || '' },
|
{ label: '单据日期', value: formData.orderDate || '' },
|
||||||
{ label: '操作员', value: formData.operatorName || '' },
|
{ label: '操作员', value: formData.operatorName || '' },
|
||||||
{ label: '物料名称', value: [formData.productName, formData.productSpc].filter(Boolean).join(' ') },
|
{ label: '物料名称', value: [formData.productName, formData.productSpc].filter(Boolean).join(' ') },
|
||||||
@@ -830,12 +831,12 @@ const printConfig = computed<PrintConfig>(() => ({
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
async function handlePrint() {
|
async function handlePrint() {
|
||||||
// 加载工序任务列表
|
// 加载工序列表(基于工艺路线,不依赖 pro_task,未排产也能显示)
|
||||||
if (formData.workorderId) {
|
if (formData.workorderId) {
|
||||||
try {
|
try {
|
||||||
taskList.value = await getTaskListByWorkorder(formData.workorderId)
|
taskList.value = await getProcessTasksByWorkorder(formData.workorderId)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载工序任务失败:', error)
|
console.error('加载工序列表失败:', error)
|
||||||
taskList.value = []
|
taskList.value = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -117,7 +119,29 @@ public class ProWorkorderController extends BaseController
|
|||||||
@GetMapping(value = "/{workorderId}")
|
@GetMapping(value = "/{workorderId}")
|
||||||
public AjaxResult getInfo(@PathVariable("workorderId") Long workorderId)
|
public AjaxResult getInfo(@PathVariable("workorderId") Long workorderId)
|
||||||
{
|
{
|
||||||
return AjaxResult.success(proWorkorderService.selectProWorkorderByWorkorderId(workorderId));
|
ProWorkorder wo = proWorkorderService.selectProWorkorderByWorkorderId(workorderId);
|
||||||
|
if (wo != null) {
|
||||||
|
wo.setProductionLine(wo.getAttr1());
|
||||||
|
if (wo.getAttr2() != null && !wo.getAttr2().isEmpty()) {
|
||||||
|
try {
|
||||||
|
wo.setProcessTime(Integer.parseInt(wo.getAttr2()));
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
wo.setProcessTime(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return AjaxResult.success(wo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取工单工序列表(基于工艺路线,不依赖 pro_task)
|
||||||
|
* 用于打印等场景,即使未排产也能显示工序信息
|
||||||
|
*/
|
||||||
|
@GetMapping(value = "/{workorderId}/processTasks")
|
||||||
|
public AjaxResult getProcessTasksByWorkorder(@PathVariable("workorderId") Long workorderId)
|
||||||
|
{
|
||||||
|
List<ProTask> tasks = proTaskService.selectProTaskProcessViewByWorkorder(workorderId);
|
||||||
|
return AjaxResult.success(tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,6 +152,11 @@ public class ProWorkorderController extends BaseController
|
|||||||
@PostMapping
|
@PostMapping
|
||||||
public AjaxResult add(@RequestBody ProWorkorder proWorkorder)
|
public AjaxResult add(@RequestBody ProWorkorder proWorkorder)
|
||||||
{
|
{
|
||||||
|
syncAttrFromForm(proWorkorder);
|
||||||
|
// 跟单编号为空时自动生成:XSDD-yyyyMMddHHmmss
|
||||||
|
if (StringUtils.isEmpty(proWorkorder.getPpNumber())) {
|
||||||
|
proWorkorder.setPpNumber("XSDD-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
|
||||||
|
}
|
||||||
// 工单编码自动生成(使用统一编码引擎)
|
// 工单编码自动生成(使用统一编码引擎)
|
||||||
if(StringUtils.isEmpty(proWorkorder.getWorkorderCode())){
|
if(StringUtils.isEmpty(proWorkorder.getWorkorderCode())){
|
||||||
proWorkorder.setWorkorderCode(autoCodeUtil.genSerialCode(UserConstants.SCGD_CODE, ""));
|
proWorkorder.setWorkorderCode(autoCodeUtil.genSerialCode(UserConstants.SCGD_CODE, ""));
|
||||||
@@ -162,6 +191,11 @@ public class ProWorkorderController extends BaseController
|
|||||||
@PutMapping
|
@PutMapping
|
||||||
public AjaxResult edit(@RequestBody ProWorkorder proWorkorder)
|
public AjaxResult edit(@RequestBody ProWorkorder proWorkorder)
|
||||||
{
|
{
|
||||||
|
syncAttrFromForm(proWorkorder);
|
||||||
|
// 跟单编号为空时自动生成:XSDD-yyyyMMddHHmmss
|
||||||
|
if (StringUtils.isEmpty(proWorkorder.getPpNumber())) {
|
||||||
|
proWorkorder.setPpNumber("XSDD-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
|
||||||
|
}
|
||||||
if(UserConstants.NOT_UNIQUE.equals(proWorkorderService.checkWorkorderCodeUnique(proWorkorder))){
|
if(UserConstants.NOT_UNIQUE.equals(proWorkorderService.checkWorkorderCodeUnique(proWorkorder))){
|
||||||
return AjaxResult.error("生产工单编号已存在!");
|
return AjaxResult.error("生产工单编号已存在!");
|
||||||
}
|
}
|
||||||
@@ -206,6 +240,16 @@ public class ProWorkorderController extends BaseController
|
|||||||
return toAjax(proWorkorderService.deleteProWorkorderByWorkorderIds(workorderIds));
|
return toAjax(proWorkorderService.deleteProWorkorderByWorkorderIds(workorderIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 将表单字段 productionLine、processTime 同步到 attr1、attr2 以便持久化 */
|
||||||
|
private void syncAttrFromForm(ProWorkorder proWorkorder) {
|
||||||
|
if (proWorkorder.getProductionLine() != null) {
|
||||||
|
proWorkorder.setAttr1(proWorkorder.getProductionLine());
|
||||||
|
}
|
||||||
|
if (proWorkorder.getProcessTime() != null) {
|
||||||
|
proWorkorder.setAttr2(String.valueOf(proWorkorder.getProcessTime()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据生产工单中的产品生成BOM物料行
|
* 根据生产工单中的产品生成BOM物料行
|
||||||
* @param workorderId
|
* @param workorderId
|
||||||
|
|||||||
@@ -52,6 +52,7 @@
|
|||||||
"js-cookie": "3.0.1",
|
"js-cookie": "3.0.1",
|
||||||
"jsencrypt": "3.0.0-rc.1",
|
"jsencrypt": "3.0.0-rc.1",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
|
"qrcode": "^1.5.3",
|
||||||
"quill": "1.3.7",
|
"quill": "1.3.7",
|
||||||
"screenfull": "5.0.2",
|
"screenfull": "5.0.2",
|
||||||
"sortablejs": "1.10.2",
|
"sortablejs": "1.10.2",
|
||||||
|
|||||||
51
mom-frontend-vue2/src/components/print/QrCode.vue
Normal file
51
mom-frontend-vue2/src/components/print/QrCode.vue
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<template>
|
||||||
|
<img v-if="dataUrl" :src="dataUrl" :width="size" :height="size" alt="QR Code" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import QRCode from 'qrcode'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'QrCode',
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: Number,
|
||||||
|
default: 80
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dataUrl: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value: 'generate',
|
||||||
|
size: 'generate'
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.generate()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async generate() {
|
||||||
|
if (!this.value) {
|
||||||
|
this.dataUrl = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.dataUrl = await QRCode.toDataURL(this.value, {
|
||||||
|
width: this.size * 2,
|
||||||
|
margin: 1,
|
||||||
|
errorCorrectionLevel: 'M'
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
console.error('QR code generation failed:', err)
|
||||||
|
this.dataUrl = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
173
mom-frontend-vue2/src/components/print/WarehouseLabelPrint.vue
Normal file
173
mom-frontend-vue2/src/components/print/WarehouseLabelPrint.vue
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:visible="visible"
|
||||||
|
@close="handleClose"
|
||||||
|
fullscreen
|
||||||
|
destroy-on-close
|
||||||
|
:show-close="false"
|
||||||
|
class="warehouse-label-print-dialog"
|
||||||
|
append-to-body
|
||||||
|
>
|
||||||
|
<template slot="title">
|
||||||
|
<div class="label-print-actions">
|
||||||
|
<el-button type="primary" size="small" @click="doPrint">打印</el-button>
|
||||||
|
<el-button size="small" @click="handleClose">关闭</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="label-print-content" ref="printContentRef">
|
||||||
|
<div v-if="warehouses.length === 0" class="label-empty">暂无仓库数据</div>
|
||||||
|
<div v-else class="label-grid">
|
||||||
|
<div
|
||||||
|
v-for="(wh, idx) in warehouses"
|
||||||
|
:key="wh.warehouseId || idx"
|
||||||
|
class="label-card"
|
||||||
|
>
|
||||||
|
<div class="label-qr">
|
||||||
|
<QrCode :value="wh.warehouseCode || String(wh.warehouseId || '')" :size="72" />
|
||||||
|
</div>
|
||||||
|
<div class="label-name">{{ wh.warehouseName || '-' }}</div>
|
||||||
|
<div class="label-code">{{ wh.warehouseCode || '-' }}</div>
|
||||||
|
<div class="label-info">
|
||||||
|
<span v-if="wh.location">位置:{{ wh.location }}</span>
|
||||||
|
<span v-if="wh.area != null">面积:{{ wh.area }} ㎡</span>
|
||||||
|
<span v-if="wh.charge">负责人:{{ wh.charge }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import QrCode from './QrCode.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WarehouseLabelPrint',
|
||||||
|
components: { QrCode },
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
warehouses: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
doPrint() {
|
||||||
|
window.print()
|
||||||
|
},
|
||||||
|
handleClose() {
|
||||||
|
this.$emit('update:visible', false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@media print {
|
||||||
|
body > *:not(.el-overlay) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
.el-overlay {
|
||||||
|
position: static !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
background: none !important;
|
||||||
|
}
|
||||||
|
.el-dialog__wrapper {
|
||||||
|
position: static !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
.el-dialog {
|
||||||
|
box-shadow: none !important;
|
||||||
|
border: none !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
.el-dialog__header,
|
||||||
|
.el-dialog__footer,
|
||||||
|
.label-print-actions {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
.el-dialog__body {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.label-print-content {
|
||||||
|
padding: 8mm !important;
|
||||||
|
}
|
||||||
|
.label-card {
|
||||||
|
page-break-inside: avoid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.label-print-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-print-content {
|
||||||
|
padding: 24px;
|
||||||
|
background: #fff;
|
||||||
|
font-family: 'Microsoft YaHei', 'SimSun', sans-serif;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-empty {
|
||||||
|
text-align: center;
|
||||||
|
padding: 48px;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 16px;
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-card {
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 12px;
|
||||||
|
text-align: center;
|
||||||
|
min-height: 120px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-qr {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-code {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #606266;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-info {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #909399;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true"/>
|
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true"/>
|
||||||
<el-table-column label="操作" align="center" width="200px" class-name="small-padding fixed-width">
|
<el-table-column label="操作" align="center" width="260px" class-name="small-padding fixed-width">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button
|
<el-button
|
||||||
size="mini"
|
size="mini"
|
||||||
@@ -123,6 +123,13 @@
|
|||||||
@click="handleHiPrint(scope.row)"
|
@click="handleHiPrint(scope.row)"
|
||||||
v-hasPermi="['mes:wm:warehouse:print']"
|
v-hasPermi="['mes:wm:warehouse:print']"
|
||||||
>标签打印</el-button>
|
>标签打印</el-button>
|
||||||
|
<el-button
|
||||||
|
size="mini"
|
||||||
|
type="text"
|
||||||
|
icon="el-icon-document"
|
||||||
|
@click="handleSimplePrint(scope.row)"
|
||||||
|
v-hasPermi="['mes:wm:warehouse:print']"
|
||||||
|
>简易打印</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -207,6 +214,12 @@
|
|||||||
<el-button @click="cancel">取 消</el-button>
|
<el-button @click="cancel">取 消</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 简易标签打印(不依赖MinIO) -->
|
||||||
|
<WarehouseLabelPrint
|
||||||
|
:visible.sync="printDialogVisible"
|
||||||
|
:warehouses="printWarehouses"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -215,13 +228,14 @@ import { listWarehouse, getWarehouse, delWarehouse, addWarehouse, updateWarehous
|
|||||||
import UserSingleSelect from "@/components/userSelect/single.vue"
|
import UserSingleSelect from "@/components/userSelect/single.vue"
|
||||||
import {genCode} from "@/api/system/autocode/rule"
|
import {genCode} from "@/api/system/autocode/rule"
|
||||||
import BarcodeImg from "@/components/barcodeImg/index.vue"
|
import BarcodeImg from "@/components/barcodeImg/index.vue"
|
||||||
|
import WarehouseLabelPrint from "@/components/print/WarehouseLabelPrint.vue"
|
||||||
import { getBarcodeUrl } from '@/api/mes/wm/barcode';
|
import { getBarcodeUrl } from '@/api/mes/wm/barcode';
|
||||||
import {print} from "../../../../utils/print"
|
import {print} from "../../../../utils/print"
|
||||||
import {getByTemplateType} from "@/api/print/template";
|
import {getByTemplateType} from "@/api/print/template";
|
||||||
import { hiprintMixin } from "../../../../mixins/hiprintMixin";
|
import { hiprintMixin } from "../../../../mixins/hiprintMixin";
|
||||||
export default {
|
export default {
|
||||||
name: "Warehouse",
|
name: "Warehouse",
|
||||||
components: { BarcodeImg ,UserSingleSelect} ,
|
components: { BarcodeImg, UserSingleSelect, WarehouseLabelPrint },
|
||||||
mixins: [hiprintMixin],
|
mixins: [hiprintMixin],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -246,6 +260,9 @@ export default {
|
|||||||
title: "",
|
title: "",
|
||||||
// 是否显示弹出层
|
// 是否显示弹出层
|
||||||
open: false,
|
open: false,
|
||||||
|
// 简易打印弹窗
|
||||||
|
printDialogVisible: false,
|
||||||
|
printWarehouses: [],
|
||||||
// 查询参数
|
// 查询参数
|
||||||
queryParams: {
|
queryParams: {
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
@@ -297,6 +314,11 @@ export default {
|
|||||||
this.getList();
|
this.getList();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
// 简易打印(不依赖MinIO,纯前端二维码+window.print)
|
||||||
|
handleSimplePrint(row) {
|
||||||
|
this.printWarehouses = [row]
|
||||||
|
this.printDialogVisible = true
|
||||||
|
},
|
||||||
// 使用HiPrint打印
|
// 使用HiPrint打印
|
||||||
async handleHiPrint(row) {
|
async handleHiPrint(row) {
|
||||||
let printData = row
|
let printData = row
|
||||||
|
|||||||
Reference in New Issue
Block a user