添加条形码内容

This commit is contained in:
dxc
2026-02-02 15:06:20 +08:00
parent a1133aac94
commit cf6a4a8957
11 changed files with 449 additions and 34 deletions

View File

@ -91,8 +91,11 @@
</el-table-column>
</template>
<el-table-column label="操作" width="160" fixed="right" align="center">
<el-table-column label="操作" width="220" fixed="right" align="center">
<template #default="{ row }">
<el-button link type="warning" size="default" @click="handlePrint(row)">
<el-icon><Printer /></el-icon> 打印
</el-button>
<el-button link type="primary" size="default" @click="handleUpdate(row)">编辑</el-button>
<el-popconfirm title="确定删除该条记录吗?不可恢复。" @confirm="handleDelete(row)" width="220">
<template #reference>
@ -194,7 +197,15 @@
<div class="card-content">
<el-row :gutter="20">
<el-col :span="6"><el-form-item label="编码/SKU" prop="sku"><el-input v-model="form.sku" placeholder="选填" /></el-form-item></el-col>
<el-col :span="6">
<el-form-item label="编码/SKU" prop="sku">
<el-input
v-model="form.sku"
placeholder="系统自动生成 (0000000X)"
disabled
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="入库日期" prop="in_date">
<el-date-picker v-model="form.in_date" type="date" value-format="YYYY-MM-DD" style="width:100%" disabled />
@ -372,17 +383,46 @@
<div class="dialog-footer">
<el-button @click="visible = false" size="large">取消</el-button>
<el-button type="primary" :loading="submitting" @click="submitForm" size="large" class="confirm-btn">
{{ dialogStatus === 'create' ? '确认入库' : '保存修改' }}
{{ dialogStatus === 'create' ? '确认入库并打印' : '保存修改' }}
</el-button>
</div>
</template>
</el-dialog>
<el-dialog
v-model="printVisible"
title="标签打印预览"
width="400px"
destroy-on-close
append-to-body
>
<div style="text-align: center;">
<div v-loading="printLoading" class="preview-box">
<img v-if="previewUrl" :src="previewUrl" alt="Label Preview" style="width: 100%; border: 1px solid #ccc;" />
<div v-else class="empty-preview">正在生成预览...</div>
</div>
<div style="margin-top: 20px; font-size: 14px; color: #666;">
<p>打印机 IP: 192.168.9.205</p>
<p>尺寸: 40mm x 30mm</p>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="printVisible = false">取消</el-button>
<el-button type="primary" :loading="printing" @click="confirmPrint">
<el-icon><Printer /></el-icon> 确认打印
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, watch } from 'vue'
import { Plus, Setting, Refresh, Search, Lock, Box, House, InfoFilled, Link } from '@element-plus/icons-vue'
import { Plus, Setting, Refresh, Search, Lock, Box, House, InfoFilled, Link, Printer } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import dayjs from 'dayjs'
import {
@ -392,6 +432,7 @@ import {
deleteBuyInbound,
searchMaterialBase
} from '@/api/inbound/buy'
import { getLabelPreview, executePrint } from '@/api/common/print'
// ------------------------------------
// 状态与变量
@ -407,6 +448,13 @@ const formRef = ref()
const queryParams = reactive({ page: 1, pageSize: 15, keyword: '' })
const materialOptions = ref<any[]>([])
// 打印相关变量
const printVisible = ref(false)
const printLoading = ref(false)
const printing = ref(false)
const previewUrl = ref('')
const currentPrintData = ref<any>({})
const entryMode = ref('batch')
const modeLocked = ref(false)
@ -599,9 +647,6 @@ const querySearchCurrency = (queryString: string, cb: any) => {
const handleMaterialDropdownVisible = (visible: boolean) => {
if (visible) {
// 【修改点】: 展开时,如果列表为空,直接调用 API 加载前 20 条数据
// 同时也会把历史记录合并进去(在 handleSearchMaterial 内部处理或分开展示)
// 这里简单处理:直接调用 handleSearchMaterial('') 让后端返回默认数据
if (materialOptions.value.length === 0) {
handleSearchMaterial('')
}
@ -611,14 +656,11 @@ const handleMaterialDropdownVisible = (visible: boolean) => {
const handleSearchMaterial = async (query: string) => {
searchLoading.value = true
try {
// 即使 query 为空,后端现在也会返回数据
const res: any = await searchMaterialBase(query)
const apiResults = (res.data || []).map((i: any) => ({ ...i, isHistory: false }))
// 如果是空搜索,我们可以把本地历史记录插在前面,做个合并
if (!query) {
const history = getMaterialHistory()
// 简单去重:如果 API 返回的 ID 在历史记录里有,就跳过 API 的那个(优先显示历史标记)
const historyIds = new Set(history.map((h: any) => h.id))
const filteredApi = apiResults.filter((apiItem: any) => !historyIds.has(apiItem.id))
materialOptions.value = [...history, ...filteredApi]
@ -630,7 +672,6 @@ const handleSearchMaterial = async (query: string) => {
}
}
// 选中物料后 -> 存入历史 -> 填充表单
const onMaterialSelected = (val: number) => {
const item = materialOptions.value.find(i => i.id === val)
if (item) {
@ -758,7 +799,6 @@ const handleCreate = () => {
entryMode.value = 'batch'
form.batch_number = ''
visible.value = true
// 每次打开弹窗时,先清空选项,让下拉时触发“历史加载”
materialOptions.value = []
}
@ -806,7 +846,6 @@ const handleUpdate = (row: any) => {
form.detail_link = row.detail_link
form.arrival_photo = row.arrival_photo
// 编辑模式下,把当前物料塞入选项,防止显示为 ID
materialOptions.value = [{
id: row.base_id,
name: row.material_name,
@ -817,6 +856,9 @@ const handleUpdate = (row: any) => {
visible.value = true
}
// ------------------------------------
// 提交逻辑 (新增自动打印逻辑)
// ------------------------------------
const submitForm = async () => {
if (!formRef.value) return
await formRef.value.validate(async (valid: boolean) => {
@ -824,8 +866,24 @@ const submitForm = async () => {
submitting.value = true
try {
if (dialogStatus.value === 'create') {
await createBuyInbound(form)
// 1. 创建入库
const res: any = await createBuyInbound(form)
ElMessage.success('入库成功')
// 2. 自动打印 (使用返回的完整数据)
// res.data 包含了 newly created stock item, with generated ID and SKU
const newItem = res.data
if (newItem) {
ElMessage.info('正在发送打印指令...')
try {
await executePrint(newItem)
ElMessage.success('打印指令已发送')
} catch (printErr: any) {
console.error(printErr)
ElMessage.warning('入库成功,但自动打印失败:' + (printErr.msg || '未知错误'))
}
}
} else {
const payload = {
...form,
@ -836,7 +894,6 @@ const submitForm = async () => {
ElMessage.success('更新成功')
}
// 保存供应商等信息到历史记录
saveToHistory(HISTORY_KEYS.SUPPLIER, form.supplier_name)
saveToHistory(HISTORY_KEYS.PURCHASER, form.purchaser)
saveToHistory(HISTORY_KEYS.EMAIL, form.purchaser_email)
@ -862,6 +919,50 @@ const handleDelete = async (row: any) => {
}
}
// ------------------------------------
// 打印逻辑 (手动 & 预览)
// ------------------------------------
const handlePrint = async (row: any) => {
printVisible.value = true
printLoading.value = true
previewUrl.value = ''
const printData = {
global_print_id: row.global_print_id,
material_name: row.material_name,
spec_model: row.spec_model,
category: row.category,
material_type: row.material_type,
warehouse_loc: row.warehouse_loc,
serial_number: row.serial_number,
batch_number: row.batch_number,
sku: row.sku // 【重要】显式增加 SKU 字段传递给后端
}
currentPrintData.value = printData
try {
const res: any = await getLabelPreview(printData)
previewUrl.value = res.data
} catch(e) {
ElMessage.error('预览生成失败')
} finally {
printLoading.value = false
}
}
const confirmPrint = async () => {
printing.value = true
try {
await executePrint(currentPrintData.value)
ElMessage.success('指令已发送')
printVisible.value = false
} catch(e: any) {
ElMessage.error(e.msg || '打印失败')
} finally {
printing.value = false
}
}
const resetForm = () => {
materialOptions.value = []
Object.assign(form, {
@ -1031,4 +1132,14 @@ onMounted(() => fetchData())
.opt-name { font-weight: bold; }
.opt-spec { color: #8492a6; font-size: 13px; margin-right: 10px; }
.total-price-input :deep(.el-input__inner) { color: #F56C6C; font-weight: bold; }
.preview-box {
min-height: 150px;
display: flex;
justify-content: center;
align-items: center;
background: #f5f7fa;
border-radius: 4px;
}
.empty-preview { color: #909399; }
</style>