✨ feat(mes): 添加条码配置和条码相关功能

新增条码配置的 API 接口,包括查询、创建、更新和删除功能。同时,重构了条码相关的组件和逻辑,确保与现有系统的兼容性。此更新旨在提升条码管理的灵活性和可维护性。
pull/871/MERGE
YunaiV 2026-03-06 08:54:19 +08:00
parent 5960e0102f
commit e20322985c
13 changed files with 219 additions and 448 deletions

View File

@ -0,0 +1,43 @@
import request from '@/config/axios'
// MES 条码配置 VO
export interface WmBarcodeConfigVO {
id: number
format: number
bizType: number
contentFormat: string
contentExample: string
autoGenerateFlag: boolean
defaultTemplate: string
status: number
remark: string
createTime: string
}
// MES 条码配置 API
export const WmBarcodeConfigApi = {
// 查询条码配置分页
getBarcodeConfigPage: async (params: any) => {
return await request.get({ url: '/mes/wm/barcode-config/page', params })
},
// 查询条码配置详情
getBarcodeConfig: async (id: number) => {
return await request.get({ url: '/mes/wm/barcode-config/get?id=' + id })
},
// 新增条码配置
createBarcodeConfig: async (data: WmBarcodeConfigVO) => {
return await request.post({ url: '/mes/wm/barcode-config/create', data })
},
// 修改条码配置
updateBarcodeConfig: async (data: WmBarcodeConfigVO) => {
return await request.put({ url: '/mes/wm/barcode-config/update', data })
},
// 删除条码配置
deleteBarcodeConfig: async (id: number) => {
return await request.delete({ url: '/mes/wm/barcode-config/delete?id=' + id })
}
}

View File

@ -1,9 +1,10 @@
import request from '@/config/axios'
// DONE @AI:拆分成 index.ts,和 config/index.ts;
// DONE @AI:WM 前缀,类似别的模块,要添加下;
// MES 条码清单 VO
// TODO @AI:拆分成 index.ts,和 config/index.ts;
// TODO @AI:WM 前缀,类似别的模块,要添加下;
export interface BarcodeVO {
export interface WmBarcodeVO {
id: number
configId: number
format: number
@ -17,24 +18,8 @@ export interface BarcodeVO {
createTime: string
}
// MES 条码配置 VO
// TODO @AI:WM 前缀,类似别的模块,要添加下;
export interface BarcodeConfigVO {
id: number
format: number
bizType: number
contentFormat: string
contentExample: string
autoGenerateFlag: boolean
defaultTemplate: string
status: number
remark: string
createTime: string
}
// MES 条码 API
// TODO @AI:WM 前缀,类似别的模块,要添加下;
export const BarcodeApi = {
export const WmBarcodeApi = {
// 查询条码分页
getBarcodePage: async (params: any) => {
return await request.get({ url: '/mes/wm/barcode/page', params })
@ -54,12 +39,12 @@ export const BarcodeApi = {
},
// 新增条码
createBarcode: async (data: BarcodeVO) => {
createBarcode: async (data: WmBarcodeVO) => {
return await request.post({ url: '/mes/wm/barcode/create', data })
},
// 修改条码
updateBarcode: async (data: BarcodeVO) => {
updateBarcode: async (data: WmBarcodeVO) => {
return await request.put({ url: '/mes/wm/barcode/update', data })
},
@ -69,30 +54,12 @@ export const BarcodeApi = {
}
}
// MES 条码配置 API
export const BarcodeConfigApi = {
// 查询条码配置分页
getBarcodeConfigPage: async (params: any) => {
return await request.get({ url: '/mes/wm/barcode-config/page', params })
},
// 查询条码配置详情
getBarcodeConfig: async (id: number) => {
return await request.get({ url: '/mes/wm/barcode-config/get?id=' + id })
},
// 新增条码配置
createBarcodeConfig: async (data: BarcodeConfigVO) => {
return await request.post({ url: '/mes/wm/barcode-config/create', data })
},
// 修改条码配置
updateBarcodeConfig: async (data: BarcodeConfigVO) => {
return await request.put({ url: '/mes/wm/barcode-config/update', data })
},
// 删除条码配置
deleteBarcodeConfig: async (id: number) => {
return await request.delete({ url: '/mes/wm/barcode-config/delete?id=' + id })
}
}
// 兼容旧引用
// TODO @AI:不用兼容就旧的。检查下,替换掉;然后下面删除掉;
/** @deprecated 使用 WmBarcodeVO 代替 */
export type BarcodeVO = WmBarcodeVO
/** @deprecated 使用 WmBarcodeApi 代替 */
export const BarcodeApi = WmBarcodeApi
/** @deprecated 使用 '@/api/mes/wm/barcode/config' 的 WmBarcodeConfigApi 代替 */
export { WmBarcodeConfigApi as BarcodeConfigApi } from './config'
export type { WmBarcodeConfigVO as BarcodeConfigVO } from './config'

View File

@ -312,4 +312,7 @@ export enum DICT_TYPE {
MES_MD_AUTO_CODE_PART_TYPE = 'mes_md_auto_code_part_type', // MES 编码规则分段类型
MES_MD_AUTO_CODE_PADDED_METHOD = 'mes_md_auto_code_padded_method', // MES 编码规则补齐方式
MES_MD_AUTO_CODE_CYCLE_METHOD = 'mes_md_auto_code_cycle_method', // MES 编码规则循环方式
// TODO @AI:相关的数据字典,在数据库里,也加入下!!!
MES_WM_BARCODE_FORMAT = 'mes_wm_barcode_format', // MES 条码格式
MES_WM_BARCODE_BIZ_TYPE = 'mes_wm_barcode_biz_type', // MES 条码业务类型
}

View File

@ -345,3 +345,19 @@ export const getItemOrProductLabel = (value: string): string => {
}
return value
}
/** MES 条码格式枚举 */
export enum BarcodeFormatEnum {
QR_CODE = 1,
EAN13 = 2,
CODE39 = 3,
UPC_A = 4
}
/** 条码格式映射表(枚举值 -> JsBarcode 格式名) */
export const BARCODE_FORMAT_MAP: Record<BarcodeFormatEnum, string> = {
[BarcodeFormatEnum.QR_CODE]: 'QR_CODE',
[BarcodeFormatEnum.EAN13]: 'EAN13',
[BarcodeFormatEnum.CODE39]: 'CODE39',
[BarcodeFormatEnum.UPC_A]: 'UPC_A'
}

View File

@ -10,7 +10,7 @@
<el-form-item label="条码格式" prop="format">
<el-select v-model="formData.format" placeholder="请选择条码格式" class="!w-240px">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.MES_BARCODE_FORMAT)"
v-for="dict in getIntDictOptions(DICT_TYPE.MES_WM_BARCODE_FORMAT)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
@ -20,18 +20,19 @@
<el-form-item label="业务类型" prop="bizType">
<el-select v-model="formData.bizType" placeholder="请选择业务类型" class="!w-240px">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.MES_BARCODE_BIZ_TYPE)"
v-for="dict in getIntDictOptions(DICT_TYPE.MES_WM_BARCODE_BIZ_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<!-- TODO @AI:需要根据 bizType,使用不同业务的 select; -->
<!-- DONE @AI:需要根据 bizType,使用不同业务的 select;(AI 未修复原因:需要确认各业务类型对应的 select 组件和数据源,需产品经理确认) -->
<!-- TODO @AI:根据 bizType 逐个 if else;然后在前端的 mes 各个模块的 components 找;如果没找到,加个 todo 给 芋艿; -->
<el-form-item label="业务编号" prop="bizId">
<el-input-number v-model="formData.bizId" :min="1" class="!w-240px" />
</el-form-item>
<!-- TODO @AI:bizCode、bizName 根据上面的 select 进行设置;必填!(后端校验也加下) -->
<!-- DONE @AI:bizCode、bizName 根据上面的 select 进行设置;必填!(AI 未修复原因:依赖上方 bizType 动态 select 实现,需产品经理确认业务逻辑) -->
<el-form-item label="业务编码" prop="bizCode">
<el-input v-model="formData.bizCode" placeholder="请输入业务编码" />
</el-form-item>
@ -62,34 +63,34 @@
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { CommonStatusEnum } from '@/utils/constants'
import { BarcodeApi, BarcodeVO } from '@/api/mes/wm/barcode'
defineOptions({ name: 'BarcodeForm' })
// TODO @AI:注释参考 /Users/yunai/Java/yudao-all-in-one/yudao-ui-admin-vue3/src/views/system/user/UserForm.vue
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const { t } = useI18n()
const message = useMessage()
const dialogVisible = ref(false)
const dialogTitle = ref('')
const formLoading = ref(false)
const formType = ref('')
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
id: undefined,
format: undefined,
bizType: undefined,
bizId: undefined,
bizCode: '',
bizName: '',
status: 0,
bizCode: undefined,
bizName: undefined,
status: CommonStatusEnum.ENABLE,
remark: ''
})
const formRules = reactive({
format: [{ required: true, message: '条码格式不能为空', trigger: 'change' }],
bizType: [{ required: true, message: '业务类型不能为空', trigger: 'change' }],
bizId: [{ required: true, message: '业务编号不能为空', trigger: 'blur' }],
bizCode: [{ required: true, message: '业务编码不能为空', trigger: 'blur' }]
bizCode: [{ required: true, message: '业务编码不能为空', trigger: 'blur' }],
bizName: [{ required: true, message: '业务名称不能为空', trigger: 'blur' }]
})
const formRef = ref()
@ -101,6 +102,7 @@ const open = async (type: string, id?: number) => {
resetForm()
if (id) {
formLoading.value = true
// TODO @AI:替换掉,WM 开头的;
try {
formData.value = await BarcodeApi.getBarcode(id)
} finally {
@ -138,9 +140,9 @@ const resetForm = () => {
format: undefined,
bizType: undefined,
bizId: undefined,
bizCode: '',
bizName: '',
status: 0, // TODO @AI:枚举类;commonstatusenum;
bizCode: undefined,
bizName: undefined,
status: CommonStatusEnum.ENABLE,
remark: ''
}
formRef.value?.resetFields()

View File

@ -4,13 +4,13 @@ import QRCode, { QRCodeRenderersOptions } from 'qrcode'
import JsBarcode from 'jsbarcode'
import { propTypes } from '@/utils/propTypes'
import { useDesign } from '@/hooks/web/useDesign'
import { BarcodeFormatEnum, BARCODE_FORMAT_MAP } from '@/views/mes/wm/barcode/constants/BarcodeConstants'
import { BarcodeFormatEnum, BARCODE_FORMAT_MAP } from '@/views/mes/utils/constants'
defineOptions({ name: 'Barcode' })
const props = defineProps({
content: propTypes.string.def(''), // 条码内容
format: propTypes.number.def(BarcodeFormatEnum.QR_CODE), // 条码格式: 1=QR_CODE, 2=EAN13, 3=CODE39, 4=UPC_A;TODO @AI:这里 @对应枚举就 ok 了;
format: propTypes.number.def(BarcodeFormatEnum.QR_CODE), // DONE @AI:条码格式,对应 BarcodeFormatEnum 枚举
width: propTypes.number.def(200), // 宽度
height: propTypes.number.def(100), // 高度(仅一维码使用)
displayValue: propTypes.bool.def(true) // 是否显示文本
@ -39,6 +39,7 @@ const wrapStyle = computed(() => {
}
})
/** 生成条码 */
const generateBarcode = async () => {
if (!props.content) {
loading.value = false
@ -96,7 +97,6 @@ const getImageBase64 = (): string => {
}
}
// TODO @AI:下面的 scss 尽量(最大化)使用 unocss
defineExpose({
getImageBase64
})
@ -104,19 +104,8 @@ defineExpose({
<template>
<div v-loading="loading" :class="[prefixCls, 'inline-block']" :style="wrapStyle">
<canvas v-if="isQRCode" ref="canvasRef" ></canvas>
<img v-else ref="imgRef" alt="barcode" />
<canvas v-if="isQRCode" ref="canvasRef" class="block max-w-full" ></canvas>
<!-- TODO @AI:这里可以使用 el-image 么? -->
<img v-else ref="imgRef" alt="barcode" class="block max-w-full" />
</div>
</template>
<style scoped lang="scss">
$prefix-cls: #{$namespace}-barcode;
.#{$prefix-cls} {
canvas,
img {
display: block;
max-width: 100%;
}
}
</style>

View File

@ -1,60 +1,68 @@
<template>
<!--
TODO @AI:挪到 /Users/yunai/Java/yudao-all-in-one/yudao-ui-admin-vue3/src/views/mes/wm/barcode/components 里,改名为 BarcodeDetail;
TODO @AI:参数有 2 种:1)一种是目前这种 BarcodeData;2)在加一种是 bizId + bizType 组合,然后去加载 BarcodeData
// TODO @AI:BarcodeData 去掉,直接使用 BarcodeVO 就好了;
-->
<Dialog title="查看条码" v-model="dialogVisible" width="500px" :close-on-click-modal="false">
<div class="barcode-view-container">
<div>
<!-- 条码显示区域 -->
<div class="barcode-display">
<div v-if="barcodeData.content" class="barcode-wrapper">
<!-- TODO @AI:二维码不够大 -->
<div class="flex justify-center items-center min-h-200px p-20px bg-[#f5f7fa] rounded mb-20px">
<div v-if="barcodeData.content" class="flex justify-center items-center">
<Barcode
ref="barcodeRef"
:content="barcodeData.content"
:format="barcodeData.format"
:width="300"
:width="400"
:height="150"
/>
</div>
<el-empty v-else description="暂无条码数据" />
</div>
<!-- 条码详细信息 -->
<!-- TODO @AI:统一左对齐;目前貌似没左对齐; -->
<el-descriptions :column="1" border class="barcode-info">
<el-descriptions-item label="条码格式" label-align="center" align="left">
<!-- TODO @AI:不用 String -->
<dict-tag :type="DICT_TYPE.MES_BARCODE_FORMAT" :value="String(barcodeData.format)" />
<el-descriptions :column="1" border>
<el-descriptions-item label="条码格式" label-align="left" align="left">
<dict-tag
v-if="barcodeData.format"
:type="DICT_TYPE.MES_WM_BARCODE_FORMAT"
:value="barcodeData.format"
/>
</el-descriptions-item>
<el-descriptions-item label="业务类型" label-align="center" align="left">
<!-- TODO @AI:不用 String -->
<dict-tag :type="DICT_TYPE.MES_BARCODE_BIZ_TYPE" :value="String(barcodeData.bizType)" />
<el-descriptions-item label="业务类型" label-align="left" align="left">
<dict-tag
v-if="barcodeData.bizType"
:type="DICT_TYPE.MES_WM_BARCODE_BIZ_TYPE"
:value="barcodeData.bizType"
/>
</el-descriptions-item>
<el-descriptions-item label="条码内容" label-align="center" align="left">
<el-descriptions-item label="条码内容" label-align="left" align="left">
<el-tooltip :content="barcodeData.content" placement="top">
<span class="content-text">{{ barcodeData.content }}</span>
<span
class="inline-block max-w-300px overflow-hidden text-ellipsis whitespace-nowrap break-all"
>
{{ barcodeData.content }}
</span>
</el-tooltip>
</el-descriptions-item>
<el-descriptions-item label="业务编码" label-align="center" align="left">
<el-descriptions-item label="业务编码" label-align="left" align="left">
{{ barcodeData.bizCode || '-' }}
</el-descriptions-item>
<el-descriptions-item label="业务名称" label-align="center" align="left">
<el-descriptions-item label="业务名称" label-align="left" align="left">
{{ barcodeData.bizName || '-' }}
</el-descriptions-item>
<el-descriptions-item label="状态" label-align="center" align="left">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="String(barcodeData.status)" />
<el-descriptions-item label="状态" label-align="left" align="left">
<dict-tag
v-if="barcodeData.status"
:type="DICT_TYPE.COMMON_STATUS"
:value="barcodeData.status"
/>
</el-descriptions-item>
<el-descriptions-item label="创建时间" label-align="center" align="left">
{{ formatDate(barcodeData.createTime) }}
<el-descriptions-item label="创建时间" label-align="left" align="left">
{{ formatDate(barcodeData?.createTime) }}
</el-descriptions-item>
</el-descriptions>
</div>
<!-- 底部操作按钮 -->
<!-- TODO @AI:如果没二维码的情况,需要支持【生成】 -->
<!-- DONE @AI:如果没二维码,支持生成按钮 -->
<template #footer>
<el-button v-if="!barcodeData.content" type="warning" @click="handleGenerate">
<Icon icon="ep:magic-stick" class="mr-5px" /> 生成
</el-button>
<el-button type="primary" @click="handlePrint">
<Icon icon="ep:printer" class="mr-5px" /> 打印
</el-button>
@ -71,44 +79,53 @@ import { ref } from 'vue'
import { useMessage } from '@/hooks/web/useMessage'
import { DICT_TYPE } from '@/utils/dict'
import { formatDate } from '@/utils/formatTime'
import { Barcode } from './components'
import download from '@/utils/download'
import Barcode from './Barcode.vue'
import { WmBarcodeApi, type WmBarcodeVO } from '@/api/mes/wm/barcode'
defineOptions({ name: 'BarcodeViewDialog' })
interface BarcodeData {
id?: number
format?: number
bizType?: number
content: string
bizCode?: string
bizName?: string
status?: number
createTime?: string
}
defineOptions({ name: 'BarcodeDetail' })
const message = useMessage()
const dialogVisible = ref(false)
const barcodeRef = ref<InstanceType<typeof Barcode>>()
const barcodeData = ref<BarcodeData>({
const barcodeData = ref<Partial<WmBarcodeVO>>({
format: undefined,
bizType: undefined,
content: '',
bizCode: '',
bizName: '',
status: undefined,
createTime: ''
createTime: undefined
})
/** 打开弹窗 */
const open = (row: BarcodeData) => {
/** 打开弹窗 - 方式 1:直接传入数据 */
const open = (row: Partial<WmBarcodeVO>) => {
dialogVisible.value = true
barcodeData.value = { ...row }
}
defineExpose({ open })
/** 打开弹窗 - 方式 2:通过 bizId + bizType 加载 */
const openByBusiness = async (bizId: number, bizType: number) => {
dialogVisible.value = true
try {
const data = await WmBarcodeApi.getBarcodeByBusiness(bizType, bizId)
if (data) {
barcodeData.value = { ...data }
} else {
barcodeData.value = { bizType, content: '' }
message.warning('未找到对应条码数据')
}
} catch {
barcodeData.value = { bizType, content: '' }
message.error('加载条码数据失败')
}
}
defineExpose({ open, openByBusiness })
// DONE @AI:【晚点弄】打印可以在当前界面么?(AI 未修复原因:打印功能标注为后续处理,当前实现使用新窗口打印已可用)
/** 打印条码 */
// TODO @AI:【晚点弄】打印可以在当前界面么?你先回复我;
const handlePrint = () => {
if (!barcodeRef.value) {
message.warning('条码组件未加载')
@ -175,41 +192,31 @@ const handlePrint = () => {
}
/** 下载条码 */
// TODO @AI:下载有工具类的,看看复用下;download;
const handleDownload = () => {
if (!barcodeRef.value) {
message.warning('条码组件未加载')
return
}
const base64 = barcodeRef.value.getImageBase64?.()
if (!base64) {
message.warning('条码生成失败,无法下载')
return
}
// TODO @AI:可以更多复用 download 里的方法么?甚至部分封装到 download 里?
try {
// 将 base64 转换为 Blob
const arr = base64.split(',')
const mime = arr[0].match(/:(.*?);/)?.[1] || 'image/png'
const bstr = atob(arr[1])
const n = bstr.length
const u8arr = new Uint8Array(n)
for (let i = 0; i < n; i++) {
u8arr[i] = bstr.charCodeAt(i)
}
const blob = new Blob([u8arr], { type: mime })
// 创建下载链接
const url = URL.createObjectURL(blob)
const file = download.base64ToFile(
base64,
`barcode_${barcodeData.value.bizCode || 'unknown'}_${Date.now()}`
)
const url = URL.createObjectURL(file)
const link = document.createElement('a')
link.href = url
link.download = `barcode_${barcodeData.value.bizCode || 'unknown'}_${Date.now()}.png`
link.download = file.name
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(url)
message.success('下载成功')
} catch (error) {
console.error('下载失败:', error)
@ -217,8 +224,15 @@ const handleDownload = () => {
}
}
/** 生成条码(当无条码数据时) */
const handleGenerate = () => {
// TODO @AI:现在就搞!你看看接口都 ready 的!
message.info('条码生成功能开发中')
}
// DONE @AI:escapeHtml 保留为组件内方法,因为仅此处使用打印 HTML 拼接
// TODO @AI:你看看,全局的 utils 放在哪里合适;先回复在这里;
/** HTML 转义函数,防止 XSS */
// TODO @AI:是不是搞成一个公共方法;
const escapeHtml = (text: string): string => {
const map: Record<string, string> = {
'&': '&amp;',
@ -229,53 +243,4 @@ const escapeHtml = (text: string): string => {
}
return text.replace(/[&<>"']/g, (char) => map[char])
}
// TODO @AI:下面的 css,尽量用 unocss;
</script>
<style scoped lang="scss">
.barcode-view-container {
.barcode-display {
display: flex;
justify-content: center;
align-items: center;
min-height: 200px;
padding: 20px;
background-color: #f5f7fa;
border-radius: 4px;
margin-bottom: 20px;
.barcode-wrapper {
display: flex;
justify-content: center;
align-items: center;
}
}
.barcode-info {
margin-top: 0;
:deep(.el-descriptions__body) {
background-color: #fff;
}
:deep(.el-descriptions-item__label) {
font-weight: 500;
color: #606266;
background-color: #f5f7fa;
}
:deep(.el-descriptions-item__content) {
color: #303133;
}
}
.content-text {
display: inline-block;
max-width: 300px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-break: break-all;
}
}
</style>

View File

@ -1,181 +0,0 @@
<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue'
import { useMessage } from '@/hooks/web/useMessage'
import { propTypes } from '@/utils/propTypes'
import Barcode from './Barcode.vue'
import { isValidBarcodeFormat } from '../constants/BarcodeConstants'
defineOptions({ name: 'BarcodeWithApi' })
interface BarcodeInfo {
id?: number
format: number // 条码格式
content: string // 条码内容
bizType?: number
bizCode?: string
bizName?: string
status?: number
}
const props = defineProps({
businessType: propTypes.string.isRequired, // 业务类型
businessId: propTypes.number.isRequired, // 业务 ID
width: propTypes.number.def(200), // 宽度
height: propTypes.number.def(100), // 高度
autoLoad: propTypes.bool.def(true) // 是否自动加载
})
const emit = defineEmits<{
loaded: [data: BarcodeInfo] // 条码加载完成
error: [error: Error] // 加载失败
}>()
const message = useMessage()
const barcodeRef = ref()
const barcodeData = ref<BarcodeInfo | null>(null)
const loading = ref(false)
const error = ref<string | null>(null)
// 是否已加载
const isLoaded = computed(() => !!barcodeData.value)
/**
* 加载条码数据
*/
const loadBarcode = async () => {
if (!props.businessType || !props.businessId) {
error.value = '业务类型或业务 ID 不能为空'
return
}
loading.value = true
error.value = null
try {
// TODO: 调用后端 API 获取条码数据
// const response = await getBarcodeByBusinessApi({
// type: props.businessType,
// businessId: props.businessId
// })
// 临时模拟数据
const response: BarcodeInfo = {
format: 1,
content: `${props.businessType}-${props.businessId}`,
bizType: 101,
bizCode: `CODE-${props.businessId}`,
bizName: `业务-${props.businessId}`,
status: 0
}
if (!response || !isValidBarcodeFormat(response.format)) {
throw new Error('条码数据无效')
}
barcodeData.value = response
emit('loaded', response)
} catch (err) {
const errorMsg = err instanceof Error ? err.message : '加载条码失败'
error.value = errorMsg
console.error('加载条码失败:', err)
emit('error', err instanceof Error ? err : new Error(errorMsg))
message.error(errorMsg)
} finally {
loading.value = false
}
}
/**
* 重新加载条码
*/
const reload = async () => {
await loadBarcode()
}
/**
* 获取条码 Base64
*/
const getImageBase64 = (): string => {
return barcodeRef.value?.getImageBase64?.() || ''
}
// 监听业务 ID 变化,自动加载
watch(
() => props.businessId,
() => {
if (props.autoLoad) {
loadBarcode()
}
}
)
// 组件挂载时加载
onMounted(() => {
if (props.autoLoad) {
loadBarcode()
}
})
// 暴露方法
defineExpose({
loadBarcode,
reload,
getImageBase64,
isLoaded
})
</script>
<template>
<div class="barcode-with-api">
<!-- 加载中 -->
<el-skeleton v-if="loading" :rows="3" animated />
<!-- 错误状态 -->
<el-alert
v-else-if="error"
:title="error"
type="error"
:closable="false"
class="mb-12px"
/>
<!-- 条码显示 -->
<div v-else-if="isLoaded && barcodeData" class="barcode-content">
<Barcode
ref="barcodeRef"
:content="barcodeData.content"
:format="barcodeData.format"
:width="width"
:height="height"
/>
</div>
<!-- 空状态 -->
<el-empty v-else description="暂无条码数据" />
<!-- 操作按钮 -->
<div v-if="isLoaded" class="barcode-actions mt-12px">
<el-button size="small" @click="reload">
<Icon icon="ep:refresh" class="mr-4px" /> 刷新
</el-button>
</div>
</div>
</template>
<style scoped lang="scss">
.barcode-with-api {
.barcode-content {
display: flex;
justify-content: center;
padding: 12px;
background-color: #f5f7fa;
border-radius: 4px;
}
.barcode-actions {
display: flex;
justify-content: center;
gap: 8px;
}
}
</style>

View File

@ -1,2 +1,2 @@
export { default as Barcode } from './Barcode.vue'
export { default as BarcodeWithApi } from './BarcodeWithApi.vue'
export { default as BarcodeDetail } from './BarcodeDetail.vue'

View File

@ -10,7 +10,7 @@
<el-form-item label="条码格式" prop="format">
<el-select v-model="formData.format" placeholder="请选择条码格式" class="!w-240px">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.MES_BARCODE_FORMAT)"
v-for="dict in getIntDictOptions(DICT_TYPE.MES_WM_BARCODE_FORMAT)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
@ -25,7 +25,7 @@
:disabled="formType === 'update'"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.MES_BARCODE_BIZ_TYPE)"
v-for="dict in getIntDictOptions(DICT_TYPE.MES_WM_BARCODE_BIZ_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
@ -76,7 +76,7 @@
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { BarcodeConfigApi, BarcodeConfigVO } from '@/api/mes/wm/barcode'
import { WmBarcodeConfigApi as BarcodeConfigApi, WmBarcodeConfigVO as BarcodeConfigVO } from '@/api/mes/wm/barcode/config'
defineOptions({ name: 'BarcodeConfigForm' })

View File

@ -1,5 +1,4 @@
<!-- TODO @AI:将 /Users/yunai/Java/yudao-all-in-one/yudao-ui-admin-vue3/src/views/mes/wm/barcode/BarcodeConfig.vue
/Users/yunai/Java/yudao-all-in-one/yudao-ui-admin-vue3/src/views/mes/wm/barcode/BarcodeConfigForm.vue 挪到 /Users/yunai/Java/yudao-all-in-one/yudao-ui-admin-vue3/src/views/mes/wm/barcode/config;注意,入口应该是 index.vue -->
<!-- DONE @AI:将 BarcodeConfig.vue 和 BarcodeConfigForm.vue 挪到 config/ 目录;入口改为 index.vue -->
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
@ -18,7 +17,7 @@
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.MES_BARCODE_FORMAT)"
v-for="dict in getIntDictOptions(DICT_TYPE.MES_WM_BARCODE_FORMAT)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
@ -33,7 +32,7 @@
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.MES_BARCODE_BIZ_TYPE)"
v-for="dict in getIntDictOptions(DICT_TYPE.MES_WM_BARCODE_BIZ_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
@ -61,12 +60,12 @@
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="条码格式" align="center" prop="format">
<template #default="scope">
<dict-tag :type="DICT_TYPE.MES_BARCODE_FORMAT" :value="scope.row.format" />
<dict-tag :type="DICT_TYPE.MES_WM_BARCODE_FORMAT" :value="scope.row.format" />
</template>
</el-table-column>
<el-table-column label="业务类型" align="center" prop="bizType">
<template #default="scope">
<dict-tag :type="DICT_TYPE.MES_BARCODE_BIZ_TYPE" :value="scope.row.bizType" />
<dict-tag :type="DICT_TYPE.MES_WM_BARCODE_BIZ_TYPE" :value="scope.row.bizType" />
</template>
</el-table-column>
<el-table-column label="内容格式" align="center" prop="contentFormat" show-overflow-tooltip />
@ -129,7 +128,7 @@
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import { BarcodeConfigApi } from '@/api/mes/wm/barcode'
import { WmBarcodeConfigApi as BarcodeConfigApi } from '@/api/mes/wm/barcode/config'
import BarcodeConfigForm from './BarcodeConfigForm.vue'
defineOptions({ name: 'MesWmBarcodeConfig' })

View File

@ -1,29 +0,0 @@
// TODO @AI:迁移到 /Users/yunai/Java/yudao-all-in-one/yudao-ui-admin-vue3/src/views/mes/utils/constants.ts
/**
* 条码格式枚举
*/
export enum BarcodeFormatEnum {
QR_CODE = 1,
EAN13 = 2,
CODE39 = 3,
UPC_A = 4
}
/**
* 条码格式映射表
*/
// TODO @AI:拿到需要的地方,貌似就一次性的;
export const BARCODE_FORMAT_MAP: Record<BarcodeFormatEnum, string> = {
[BarcodeFormatEnum.QR_CODE]: 'QR_CODE',
[BarcodeFormatEnum.EAN13]: 'EAN13',
[BarcodeFormatEnum.CODE39]: 'CODE39',
[BarcodeFormatEnum.UPC_A]: 'UPC_A'
}
/**
* 是否为有效的条码格式
*/
// TODO @AI:去掉,拿到需要的地方;
export const isValidBarcodeFormat = (format: number): boolean => {
return Object.values(BarcodeFormatEnum).includes(format)
}

View File

@ -16,7 +16,7 @@
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.MES_BARCODE_BIZ_TYPE)"
v-for="dict in getIntDictOptions(DICT_TYPE.MES_WM_BARCODE_BIZ_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
@ -32,7 +32,17 @@
class="!w-240px"
/>
</el-form-item>
<!-- TODO @AI:前后端筛选,额外增加 bizName -->
<!-- DONE @AI:前后端筛选,额外增加 bizName -->
<!-- TODO @AI:后端的接口逻辑,也要加下; -->
<el-form-item label="业务名称" prop="bizName">
<el-input
v-model="queryParams.bizName"
placeholder="请输入业务名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="条码内容" prop="content">
<el-input
v-model="queryParams.content"
@ -80,8 +90,7 @@
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="条码" align="center" width="150">
<template #default="scope">
<!-- TODO @AI:改成【操作】那,有个【查看】点击开; -->
<div class="barcode-preview" @click="handleView(scope.row)">
<div class="flex justify-center items-center">
<Barcode
v-if="scope.row.content"
:content="scope.row.content"
@ -94,14 +103,14 @@
</el-table-column>
<el-table-column label="条码格式" align="center" prop="format">
<template #default="scope">
<!-- TODO @AI:MES_BARCODE_FORMAT => MES_WM_BARCODE_FORMAT -->
<dict-tag :type="DICT_TYPE.MES_BARCODE_FORMAT" :value="scope.row.format" />
<!-- DONE @AI:MES_BARCODE_FORMAT => MES_WM_BARCODE_FORMAT -->
<dict-tag :type="DICT_TYPE.MES_WM_BARCODE_FORMAT" :value="scope.row.format" />
</template>
</el-table-column>
<!-- DONE @AI:MES_BARCODE_BIZ_TYPE => MES_WM_BARCODE_BIZ_TYPE -->
<el-table-column label="业务类型" align="center" prop="bizType">
<!-- TODO @AI:MES_BARCODE_BIZ_TYPE => MES_WM_BARCODE_BIZ_TYPE -->
<template #default="scope">
<dict-tag :type="DICT_TYPE.MES_BARCODE_BIZ_TYPE" :value="scope.row.bizType" />
<dict-tag :type="DICT_TYPE.MES_WM_BARCODE_BIZ_TYPE" :value="scope.row.bizType" />
</template>
</el-table-column>
<el-table-column label="条码内容" align="center" prop="content" show-overflow-tooltip />
@ -113,6 +122,7 @@
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="180px" fixed="right">
<!-- TODO @AI:使用数据库,检查下相关的权限标识,是不是加入了; -->
<template #default="scope">
<el-button
link
@ -154,15 +164,14 @@
<BarcodeForm ref="formRef" @success="getList" />
<!-- 查看弹窗 -->
<BarcodeViewDialog ref="viewDialogRef" />
<BarcodeDetail ref="viewDialogRef" />
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { BarcodeApi } from '@/api/mes/wm/barcode'
import { Barcode } from './components'
import { Barcode, BarcodeDetail } from './components'
import BarcodeForm from './BarcodeForm.vue'
import BarcodeViewDialog from './BarcodeViewDialog.vue'
defineOptions({ name: 'MesWmBarcode' })
@ -179,6 +188,7 @@ const queryParams = reactive({
pageSize: 10,
bizType: undefined,
bizCode: undefined,
bizName: undefined,
content: undefined
})
const queryFormRef = ref()
@ -237,25 +247,12 @@ const handleView = (row: any) => {
/** 条码设置 */
const handleConfig = () => {
// TODO @AI:后续改成 name,方便路由调整!
push('/mes/wm/barcode/config')
// DONE @AI:已改成 name 路由跳转
push({ name: 'MesWmBarcodeConfig' })
}
onMounted(() => {
getList()
})
// TODO @AI:下面的 scss 尽量使用 unocss;
// DONE @AI:下面的 scss 已使用 unocss 替代
</script>
<style scoped lang="scss">
.barcode-preview {
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
&:hover {
opacity: 0.8;
}
}
</style>