feat: 采购到货单、库区标签打印、EBOM表格优化
- 采购到货单: 新建 arrivalnotice API,重构 Checkin 页面对齐到货通知功能 - 库区标签打印: 新增 LocationLabelPrint 组件,支持单个/批量打印(4列排版) - EBOM: 表格表头和内容不换行,过长截断显示 Made-with: Cursor
This commit is contained in:
141
erp-frontend-vue/src/api/arrivalnotice.ts
Normal file
141
erp-frontend-vue/src/api/arrivalnotice.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import request from './request'
|
||||
|
||||
/** 到货通知单 */
|
||||
export interface ArrivalNotice {
|
||||
noticeId?: number
|
||||
noticeCode?: string
|
||||
noticeName?: string
|
||||
poCode?: string
|
||||
vendorId?: number
|
||||
vendorCode?: string
|
||||
vendorName?: string
|
||||
vendorNick?: string
|
||||
arrivalDate?: string
|
||||
contact?: string
|
||||
tel?: string
|
||||
status?: string
|
||||
remark?: string
|
||||
createTime?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
/** 到货通知单行 */
|
||||
export interface ArrivalNoticeLine {
|
||||
lineId?: number
|
||||
noticeId?: number
|
||||
itemId?: number
|
||||
itemCode?: string
|
||||
itemName?: string
|
||||
specification?: string
|
||||
unitOfMeasure?: string
|
||||
unitName?: string
|
||||
quantityArrival?: number
|
||||
quantityQuanlified?: number
|
||||
iqcCheck?: string
|
||||
iqcId?: number
|
||||
iqcCode?: string
|
||||
remark?: string
|
||||
createTime?: string
|
||||
updateTime?: string
|
||||
}
|
||||
|
||||
/** 到货通知单查询参数 */
|
||||
export interface ArrivalNoticeQuery {
|
||||
noticeCode?: string
|
||||
noticeName?: string
|
||||
poCode?: string
|
||||
vendorId?: number
|
||||
vendorName?: string
|
||||
arrivalDate?: string
|
||||
status?: string
|
||||
pageNum?: number
|
||||
pageSize?: number
|
||||
}
|
||||
|
||||
export interface ArrivalNoticeListResponse {
|
||||
rows: ArrivalNotice[]
|
||||
total: number
|
||||
}
|
||||
|
||||
export interface ArrivalNoticeLineListResponse {
|
||||
rows: ArrivalNoticeLine[]
|
||||
total: number
|
||||
}
|
||||
|
||||
const NOTICE_BASE = '/mes/wm/arrivalnotice'
|
||||
const LINE_BASE = '/mes/wm/arrivalnoticeline'
|
||||
|
||||
// ============ 到货通知单 ============
|
||||
|
||||
/** 查询到货通知单列表 */
|
||||
export function listArrivalNotice(query?: ArrivalNoticeQuery): Promise<ArrivalNoticeListResponse> {
|
||||
return request.get(`${NOTICE_BASE}/list`, { params: query }).then((res: any) => ({
|
||||
rows: res.rows || [],
|
||||
total: res.total || 0
|
||||
}))
|
||||
}
|
||||
|
||||
/** 查询到货通知单详情 */
|
||||
export function getArrivalNotice(noticeId: number): Promise<ArrivalNotice> {
|
||||
return request.get(`${NOTICE_BASE}/${noticeId}`).then((res: any) => res.data)
|
||||
}
|
||||
|
||||
/** 新增到货通知单 */
|
||||
export function addArrivalNotice(data: Partial<ArrivalNotice>): Promise<{ data?: number }> {
|
||||
return request.post(NOTICE_BASE, data)
|
||||
}
|
||||
|
||||
/** 修改到货通知单 */
|
||||
export function updateArrivalNotice(data: Partial<ArrivalNotice>): Promise<void> {
|
||||
return request.put(NOTICE_BASE, data)
|
||||
}
|
||||
|
||||
/** 删除到货通知单 */
|
||||
export function deleteArrivalNotice(noticeId: number | number[]): Promise<void> {
|
||||
const ids = Array.isArray(noticeId) ? noticeId.join(',') : String(noticeId)
|
||||
return request.delete(`${NOTICE_BASE}/${ids}`)
|
||||
}
|
||||
|
||||
/** 生成到货通知单编码 */
|
||||
export function genArrivalNoticeCode(): Promise<string> {
|
||||
return request.get('/system/autocode/get/ARRIVALNOTICE_CODE').then((res: any) => res.data ?? res.msg ?? '')
|
||||
}
|
||||
|
||||
// ============ 到货通知单行 ============
|
||||
|
||||
/** 查询到货通知单行列表 */
|
||||
export function listArrivalNoticeLine(query?: { noticeId?: number; pageNum?: number; pageSize?: number }): Promise<ArrivalNoticeLineListResponse> {
|
||||
return request.get(`${LINE_BASE}/list`, { params: query }).then((res: any) => ({
|
||||
rows: res.rows || [],
|
||||
total: res.total || 0
|
||||
}))
|
||||
}
|
||||
|
||||
/** 查询到货通知单行详情 */
|
||||
export function getArrivalNoticeLine(lineId: number): Promise<ArrivalNoticeLine> {
|
||||
return request.get(`${LINE_BASE}/${lineId}`).then((res: any) => res.data)
|
||||
}
|
||||
|
||||
/** 新增到货通知单行 */
|
||||
export function addArrivalNoticeLine(data: Partial<ArrivalNoticeLine>): Promise<void> {
|
||||
return request.post(LINE_BASE, data)
|
||||
}
|
||||
|
||||
/** 修改到货通知单行 */
|
||||
export function updateArrivalNoticeLine(data: Partial<ArrivalNoticeLine>): Promise<void> {
|
||||
return request.put(LINE_BASE, data)
|
||||
}
|
||||
|
||||
/** 删除到货通知单行 */
|
||||
export function deleteArrivalNoticeLine(lineId: number | number[]): Promise<void> {
|
||||
const ids = Array.isArray(lineId) ? lineId.join(',') : String(lineId)
|
||||
return request.delete(`${LINE_BASE}/${ids}`)
|
||||
}
|
||||
|
||||
// ============ 状态映射 ============
|
||||
|
||||
export const ARRIVAL_NOTICE_STATUS_MAP: Record<string, { label: string; type: string }> = {
|
||||
PREPARE: { label: '开立', type: 'info' },
|
||||
APPROVING: { label: '提交中', type: 'warning' },
|
||||
APPROVED: { label: '待入库', type: 'success' }
|
||||
}
|
||||
@@ -2,61 +2,63 @@
|
||||
<div class="page-container">
|
||||
<el-card class="search-card" shadow="never">
|
||||
<el-form :model="queryParams" inline>
|
||||
<el-form-item label="跟单编号">
|
||||
<el-input v-model="queryParams.trackCode" placeholder="请输入" clearable style="width: 140px;" />
|
||||
<el-form-item label="通知单编号">
|
||||
<el-input v-model="queryParams.noticeCode" placeholder="请输入" clearable style="width: 140px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="单据编码">
|
||||
<el-input v-model="queryParams.checkinCode" placeholder="请输入" clearable style="width: 140px;" />
|
||||
<el-form-item label="通知单名称">
|
||||
<el-input v-model="queryParams.noticeName" placeholder="请输入" clearable style="width: 140px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="供应商">
|
||||
<el-input v-model="queryParams.supplierName" placeholder="请输入" clearable style="width: 150px;" />
|
||||
<el-form-item label="采购订单编号">
|
||||
<el-input v-model="queryParams.poCode" placeholder="请输入" clearable style="width: 140px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="日期">
|
||||
<el-date-picker v-model="dateRange" type="daterange" range-separator="-" start-placeholder="开始" end-placeholder="结束" value-format="YYYY-MM-DD" style="width: 220px;" />
|
||||
<el-form-item label="供应商名称">
|
||||
<el-input v-model="queryParams.vendorName" placeholder="请输入" clearable style="width: 150px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="到货日期">
|
||||
<el-date-picker v-model="queryParams.arrivalDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" clearable style="width: 140px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="单据状态">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择" clearable style="width: 120px;">
|
||||
<el-option label="开立" value="PREPARE" />
|
||||
<el-option label="提交中" value="APPROVING" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleQuery"><el-icon><Search /></el-icon>搜索</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-card class="table-card" shadow="never">
|
||||
<div class="toolbar">
|
||||
<el-button @click="handleDocument">单据</el-button>
|
||||
<el-button @click="handleQueryAll">查询所有</el-button>
|
||||
<el-button type="primary" @click="handleAdd"><el-icon><Plus /></el-icon>新增</el-button>
|
||||
<el-button type="success" @click="handleExport"><el-icon><Download /></el-icon>导出</el-button>
|
||||
<el-button @click="handleExport"><el-icon><Download /></el-icon>导出</el-button>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="tableData" stripe border>
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column label="跟单编号" width="130">
|
||||
<template #default="{ row }">{{ row.lines?.[0]?.trackCode || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="checkinCode" label="单据编码" width="130">
|
||||
<el-table-column label="通知单编号" width="150">
|
||||
<template #default="{ row }">
|
||||
<el-link type="primary">{{ row.checkinCode }}</el-link>
|
||||
<el-link type="primary" @click="handleView(row)">{{ row.noticeCode }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="noticeName" label="通知单名称" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="poCode" label="采购订单号" width="130" show-overflow-tooltip />
|
||||
<el-table-column prop="vendorName" label="供应商名称" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="contact" label="联系人" width="100" />
|
||||
<el-table-column prop="tel" label="联系方式" width="120" />
|
||||
<el-table-column prop="arrivalDate" label="到货日期" width="110" />
|
||||
<el-table-column prop="status" label="单据状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === '审核' ? 'success' : 'info'">{{ row.status }}</el-tag>
|
||||
<el-tag :type="statusTagType(row.status)">{{ statusLabel(row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="supplierName" label="供应商" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column label="物料名称" width="150">
|
||||
<template #default="{ row }">{{ row.lines?.[0]?.itemName || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="物料编码" width="120">
|
||||
<template #default="{ row }">{{ row.lines?.[0]?.itemCode || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="totalQuantity" label="到货数量" width="100" align="right" />
|
||||
<el-table-column prop="stockedQuantity" label="入库数量" width="100" align="right" />
|
||||
<el-table-column prop="checkinDate" label="单据日期" width="110" />
|
||||
<el-table-column label="操作" width="120" fixed="right">
|
||||
<el-table-column label="操作" width="160" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link>查看</el-button>
|
||||
<el-popconfirm title="确定删除吗?" @confirm="handleDelete(row)">
|
||||
<el-button type="primary" link @click="handleView(row)">查看</el-button>
|
||||
<el-button v-if="row.status === 'PREPARE'" type="primary" link @click="handleEdit(row)">修改</el-button>
|
||||
<el-popconfirm v-if="row.status === 'PREPARE'" title="确定删除吗?" @confirm="handleDelete(row)">
|
||||
<template #reference>
|
||||
<el-button type="danger" link>删除</el-button>
|
||||
</template>
|
||||
@@ -66,35 +68,238 @@
|
||||
</el-table>
|
||||
|
||||
<div class="table-footer">
|
||||
<div class="summary">
|
||||
到货数量: <span class="value">{{ totalCheckinQty.toLocaleString() }}</span>
|
||||
入库数量: <span class="value">{{ totalStockedQty.toLocaleString() }}</span>
|
||||
</div>
|
||||
<el-pagination v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize" :page-sizes="[10, 20, 50, 100]" :total="total" layout="total, sizes, prev, pager, next, jumper" @size-change="loadData" @current-change="loadData" />
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 新增/修改/查看弹窗 -->
|
||||
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="960px" destroy-on-close @close="handleDialogClose">
|
||||
<el-form ref="formRef" :model="form" :rules="formRules" :disabled="dialogMode === 'view'" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="通知单编号" prop="noticeCode">
|
||||
<el-input v-model="form.noticeCode" placeholder="可自动生成" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-form-item v-if="dialogMode !== 'view' && form.status === 'PREPARE'" label-width="80">
|
||||
<el-switch v-model="autoGenCode" active-text="自动生成" @change="handleAutoGenChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="通知单名称" prop="noticeName">
|
||||
<el-input v-model="form.noticeName" placeholder="请输入通知单名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="采购订单编号" prop="poCode">
|
||||
<el-input v-model="form.poCode" placeholder="请输入采购订单编号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
<el-form-item label="供应商" prop="vendorName">
|
||||
<el-input v-model="form.vendorName" readonly placeholder="请选择供应商" @click="openSupplierDialog">
|
||||
<template #append>
|
||||
<el-button :icon="Search" @click="openSupplierDialog" :disabled="dialogMode === 'view'" />
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="到货日期" prop="arrivalDate">
|
||||
<el-date-picker v-model="form.arrivalDate" type="date" placeholder="请选择到货日期" value-format="YYYY-MM-DD" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="联系人" prop="contact">
|
||||
<el-input v-model="form.contact" placeholder="请输入联系人" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="联系方式" prop="tel">
|
||||
<el-input v-model="form.tel" placeholder="请输入联系方式" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<!-- 物料明细区(noticeId 存在时显示) -->
|
||||
<el-divider v-if="form.noticeId" content-position="center">物料信息</el-divider>
|
||||
<div v-if="form.noticeId" class="line-section">
|
||||
<div class="line-toolbar">
|
||||
<el-button v-if="dialogMode !== 'view'" type="primary" size="small" @click="handleAddLine">
|
||||
<el-icon><Plus /></el-icon>新增
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table :data="lineList" stripe border>
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column label="物料编码" width="130">
|
||||
<template #default="{ row, $index }">
|
||||
<div v-if="dialogMode !== 'view'" class="inline-select">
|
||||
<span class="mono">{{ row.itemCode || '请选择' }}</span>
|
||||
<el-button type="primary" link @click="openItemDialog($index)">选择</el-button>
|
||||
</div>
|
||||
<span v-else>{{ row.itemCode || '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="itemName" label="物料名称" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="specification" label="规格型号" width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="unitName" label="单位" width="70" align="center" />
|
||||
<el-table-column label="到货数量" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-input-number v-if="dialogMode !== 'view'" v-model="row.quantityArrival" :min="0" :precision="2" size="small" style="width: 90px" />
|
||||
<span v-else>{{ row.quantityArrival ?? '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否检验" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-select v-if="dialogMode !== 'view'" v-model="row.iqcCheck" size="small" style="width: 70px">
|
||||
<el-option label="是" value="Y" />
|
||||
<el-option label="否" value="N" />
|
||||
</el-select>
|
||||
<span v-else>{{ row.iqcCheck === 'Y' ? '是' : '否' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="quantityQuanlified" label="合格数量" width="90" align="right" />
|
||||
<el-table-column prop="iqcCode" label="检验单号" width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="remark" label="备注" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column v-if="dialogMode !== 'view'" label="操作" width="80" fixed="right">
|
||||
<template #default="{ $index }">
|
||||
<el-button type="danger" link @click="handleDeleteLine($index)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button v-if="dialogMode !== 'view' && form.status === 'PREPARE'" type="primary" :loading="saveLoading" @click="handleSave">保存</el-button>
|
||||
<el-button v-if="dialogMode !== 'view' && form.status === 'PREPARE' && form.noticeId" type="success" :loading="saveLoading" @click="handleSubmit">提交</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 选择供应商弹窗 -->
|
||||
<el-dialog v-model="showSupplierDialog" title="选择供应商" width="900px" destroy-on-close>
|
||||
<div class="dialog-search">
|
||||
<el-input v-model="supplierSearch.supplierName" placeholder="供应商名称" clearable style="width: 200px; margin-right: 8px" />
|
||||
<el-input v-model="supplierSearch.contact1" placeholder="业务联系人" clearable style="width: 160px; margin-right: 8px" />
|
||||
<el-button type="primary" @click="loadSuppliers">搜索</el-button>
|
||||
<el-button @click="resetSupplierSearch">查询所有</el-button>
|
||||
</div>
|
||||
<el-table :data="supplierList" stripe border max-height="400" highlight-current-row>
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="supplierName" label="供应商名称" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="tel" label="电话" width="130" />
|
||||
<el-table-column prop="contact1" label="业务联系人" width="120" />
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="handleSelectSupplier(row)">选择</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 选择物料弹窗 -->
|
||||
<el-dialog v-model="showItemDialog" title="选择物料" width="960px" destroy-on-close @open="handleItemDialogOpen">
|
||||
<div class="item-dialog-body">
|
||||
<div class="category-tree">
|
||||
<el-input v-model="itemTypeFilter" placeholder="请输入分类名称" clearable size="small" style="margin-bottom: 8px" />
|
||||
<el-scrollbar height="360px">
|
||||
<el-tree
|
||||
ref="itemTreeRef"
|
||||
:data="itemTypeTree"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
node-key="id"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
:filter-node-method="filterTreeNode"
|
||||
@node-click="handleTreeNodeClick"
|
||||
/>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<div class="item-table-area">
|
||||
<div class="item-search-area">
|
||||
<el-input v-model="itemSearch.itemCode" placeholder="物料编码" clearable style="width: 140px" @keyup.enter="loadItemTableData" />
|
||||
<el-input v-model="itemSearch.itemName" placeholder="物料名称" clearable style="width: 140px" @keyup.enter="loadItemTableData" />
|
||||
<el-button type="primary" @click="loadItemTableData">搜索</el-button>
|
||||
<el-button @click="resetItemSearch">重置</el-button>
|
||||
</div>
|
||||
<div class="item-toolbar">
|
||||
<el-button size="small" @click="loadAllItems">查询所有</el-button>
|
||||
</div>
|
||||
<el-table :data="itemTableData" v-loading="itemTableLoading" stripe border max-height="340" highlight-current-row>
|
||||
<el-table-column prop="itemCode" label="物料编码" width="130" />
|
||||
<el-table-column prop="itemName" label="物料名称" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="specification" label="型号规格" width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="unitName" label="主计量" width="80" align="center" />
|
||||
<el-table-column label="操作" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="handleSelectItem(row)">选择</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ref, reactive, computed, watch, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Search, Plus, Download } from '@element-plus/icons-vue'
|
||||
import { getCheckinList, deleteCheckin, type Checkin, type CheckinQuery } from '@/api/checkin'
|
||||
import {
|
||||
listArrivalNotice,
|
||||
getArrivalNotice,
|
||||
addArrivalNotice,
|
||||
updateArrivalNotice,
|
||||
deleteArrivalNotice,
|
||||
listArrivalNoticeLine,
|
||||
addArrivalNoticeLine,
|
||||
updateArrivalNoticeLine,
|
||||
deleteArrivalNoticeLine,
|
||||
genArrivalNoticeCode,
|
||||
ARRIVAL_NOTICE_STATUS_MAP,
|
||||
type ArrivalNotice,
|
||||
type ArrivalNoticeLine,
|
||||
type ArrivalNoticeQuery
|
||||
} from '@/api/arrivalnotice'
|
||||
import { getPoSupplierList, type PoSupplier } from '@/api/purchaseOrder'
|
||||
import { listMdItem, type MdItem } from '@/api/masterdata/item'
|
||||
import { listItemType, handleTree, type ItemType } from '@/api/masterdata/itemtype'
|
||||
|
||||
const queryParams = reactive<CheckinQuery>({ trackCode: '', checkinCode: '', supplierName: '', pageNum: 1, pageSize: 10 })
|
||||
const dateRange = ref<string[]>([])
|
||||
const queryParams = reactive<ArrivalNoticeQuery>({
|
||||
noticeCode: '',
|
||||
noticeName: '',
|
||||
poCode: '',
|
||||
vendorName: '',
|
||||
arrivalDate: '',
|
||||
status: '',
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
const loading = ref(false)
|
||||
const tableData = ref<Checkin[]>([])
|
||||
const tableData = ref<ArrivalNotice[]>([])
|
||||
const total = ref(0)
|
||||
|
||||
const totalCheckinQty = computed(() => tableData.value.reduce((sum, r) => sum + (r.totalQuantity || 0), 0))
|
||||
const totalStockedQty = computed(() => tableData.value.reduce((sum, r) => sum + (r.stockedQuantity || 0), 0))
|
||||
const statusLabel = (status?: string) => ARRIVAL_NOTICE_STATUS_MAP[status || '']?.label ?? status ?? '-'
|
||||
const statusTagType = (status?: string) => ARRIVAL_NOTICE_STATUS_MAP[status || '']?.type ?? 'info'
|
||||
|
||||
const loadData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getCheckinList(queryParams)
|
||||
const res = await listArrivalNotice(queryParams)
|
||||
tableData.value = res.rows
|
||||
total.value = res.total
|
||||
} finally {
|
||||
@@ -103,11 +308,383 @@ const loadData = async () => {
|
||||
}
|
||||
|
||||
const handleQuery = () => { queryParams.pageNum = 1; loadData() }
|
||||
const handleQueryAll = () => { queryParams.trackCode = ''; queryParams.checkinCode = ''; queryParams.supplierName = ''; dateRange.value = []; handleQuery() }
|
||||
const handleDocument = () => ElMessage.info('单据视图切换')
|
||||
const handleAdd = () => ElMessage.info('新增采购到货单')
|
||||
const handleDelete = async (row: Checkin) => { await deleteCheckin(String(row.checkinId)); ElMessage.success('删除成功'); loadData() }
|
||||
const handleExport = () => ElMessage.info('导出功能开发中')
|
||||
const handleReset = () => {
|
||||
queryParams.noticeCode = ''
|
||||
queryParams.noticeName = ''
|
||||
queryParams.poCode = ''
|
||||
queryParams.vendorName = ''
|
||||
queryParams.arrivalDate = ''
|
||||
queryParams.status = ''
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
// ============ 弹窗 ============
|
||||
const dialogVisible = ref(false)
|
||||
const dialogTitle = ref('')
|
||||
const dialogMode = ref<'add' | 'edit' | 'view'>('add')
|
||||
const formRef = ref()
|
||||
const saveLoading = ref(false)
|
||||
const autoGenCode = ref(false)
|
||||
|
||||
const form = reactive<Partial<ArrivalNotice>>({
|
||||
noticeId: undefined,
|
||||
noticeCode: '',
|
||||
noticeName: '',
|
||||
poCode: '',
|
||||
vendorId: undefined,
|
||||
vendorCode: '',
|
||||
vendorName: '',
|
||||
vendorNick: '',
|
||||
arrivalDate: '',
|
||||
contact: '',
|
||||
tel: '',
|
||||
status: 'PREPARE',
|
||||
remark: ''
|
||||
})
|
||||
|
||||
const formRules = {
|
||||
noticeCode: [{ required: true, message: '通知单编号不能为空', trigger: 'blur' }],
|
||||
noticeName: [{ required: true, message: '通知单名称不能为空', trigger: 'blur' }],
|
||||
vendorName: [{ required: true, message: '请选择供应商', trigger: 'blur' }],
|
||||
arrivalDate: [{ required: true, message: '请选择到货日期', trigger: 'change' }]
|
||||
}
|
||||
|
||||
// 明细行(本地编辑用,保存时同步到后端)
|
||||
const lineList = ref<ArrivalNoticeLine[]>([])
|
||||
|
||||
function resetForm() {
|
||||
Object.assign(form, {
|
||||
noticeId: undefined,
|
||||
noticeCode: '',
|
||||
noticeName: '',
|
||||
poCode: '',
|
||||
vendorId: undefined,
|
||||
vendorCode: '',
|
||||
vendorName: '',
|
||||
vendorNick: '',
|
||||
arrivalDate: '',
|
||||
contact: '',
|
||||
tel: '',
|
||||
status: 'PREPARE',
|
||||
remark: ''
|
||||
})
|
||||
lineList.value = []
|
||||
autoGenCode.value = false
|
||||
}
|
||||
|
||||
async function loadLines() {
|
||||
if (!form.noticeId) return
|
||||
const res = await listArrivalNoticeLine({ noticeId: form.noticeId, pageNum: 1, pageSize: 500 })
|
||||
lineList.value = res.rows.map(r => ({ ...r, iqcCheck: r.iqcCheck || 'N' }))
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
resetForm()
|
||||
dialogTitle.value = '新增采购到货单'
|
||||
dialogMode.value = 'add'
|
||||
form.arrivalDate = new Date().toISOString().split('T')[0]
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
async function handleEdit(row: ArrivalNotice) {
|
||||
resetForm()
|
||||
const data = await getArrivalNotice(row.noticeId!)
|
||||
Object.assign(form, data)
|
||||
await loadLines()
|
||||
dialogTitle.value = '修改采购到货单'
|
||||
dialogMode.value = 'edit'
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
async function handleView(row: ArrivalNotice) {
|
||||
resetForm()
|
||||
const data = await getArrivalNotice(row.noticeId!)
|
||||
Object.assign(form, data)
|
||||
await loadLines()
|
||||
dialogTitle.value = '查看到货通知单'
|
||||
dialogMode.value = 'view'
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function handleDialogClose() {
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
async function handleAutoGenChange(val: boolean) {
|
||||
if (val) {
|
||||
try {
|
||||
form.noticeCode = await genArrivalNoticeCode()
|
||||
} catch (e) {
|
||||
ElMessage.error('生成编码失败')
|
||||
autoGenCode.value = false
|
||||
}
|
||||
} else {
|
||||
form.noticeCode = ''
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
try {
|
||||
await formRef.value?.validate()
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
saveLoading.value = true
|
||||
try {
|
||||
if (form.noticeId) {
|
||||
await updateArrivalNotice(form)
|
||||
// 保存明细变更
|
||||
await saveLines()
|
||||
ElMessage.success('保存成功')
|
||||
await loadLines()
|
||||
} else {
|
||||
await addArrivalNotice(form)
|
||||
// 后端可能不返回 noticeId,通过 noticeCode 查询获取
|
||||
const listRes = await listArrivalNotice({ noticeCode: form.noticeCode, pageNum: 1, pageSize: 1 })
|
||||
const added = listRes.rows[0]
|
||||
if (added?.noticeId) {
|
||||
form.noticeId = added.noticeId
|
||||
await saveLines()
|
||||
}
|
||||
ElMessage.success('新增成功')
|
||||
if (form.noticeId) await loadLines()
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('保存失败:', e)
|
||||
} finally {
|
||||
saveLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function saveLines() {
|
||||
if (!form.noticeId) return
|
||||
const lines = lineList.value.filter(l => l.itemCode && (l.quantityArrival ?? 0) > 0)
|
||||
for (const line of lines) {
|
||||
const payload = { ...line, noticeId: form.noticeId, iqcCheck: line.iqcCheck || 'N' }
|
||||
if (line.lineId) {
|
||||
await updateArrivalNoticeLine(payload)
|
||||
} else {
|
||||
await addArrivalNoticeLine(payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
await formRef.value?.validate()
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
if (!form.noticeId) {
|
||||
ElMessage.warning('请先保存表头后再提交')
|
||||
return
|
||||
}
|
||||
const res = await listArrivalNoticeLine({ noticeId: form.noticeId, pageNum: 1, pageSize: 1 })
|
||||
if (!res.rows.length) {
|
||||
ElMessage.warning('请至少添加一条物料明细')
|
||||
return
|
||||
}
|
||||
saveLoading.value = true
|
||||
try {
|
||||
form.status = 'APPROVING'
|
||||
await updateArrivalNotice(form)
|
||||
ElMessage.success('提交成功')
|
||||
dialogVisible.value = false
|
||||
loadData()
|
||||
} catch (e) {
|
||||
console.error('提交失败:', e)
|
||||
} finally {
|
||||
saveLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDelete(row: ArrivalNotice) {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定删除该到货通知单吗?', '提示', { type: 'warning' })
|
||||
await deleteArrivalNotice(row.noticeId!)
|
||||
ElMessage.success('删除成功')
|
||||
loadData()
|
||||
} catch (e) {
|
||||
if (e !== 'cancel') console.error('删除失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
function handleExport() {
|
||||
ElMessage.info('导出功能开发中')
|
||||
}
|
||||
|
||||
// ============ 明细行操作 ============
|
||||
function handleAddLine() {
|
||||
if (!form.noticeId) {
|
||||
ElMessage.warning('请先保存表头后再添加物料')
|
||||
return
|
||||
}
|
||||
lineList.value.push({
|
||||
noticeId: form.noticeId,
|
||||
itemId: undefined,
|
||||
itemCode: '',
|
||||
itemName: '',
|
||||
specification: '',
|
||||
unitOfMeasure: '',
|
||||
unitName: '',
|
||||
quantityArrival: 0,
|
||||
quantityQuanlified: 0,
|
||||
iqcCheck: 'N',
|
||||
iqcCode: '',
|
||||
remark: ''
|
||||
})
|
||||
}
|
||||
|
||||
function handleDeleteLine(index: number) {
|
||||
const line = lineList.value[index]
|
||||
if (line.lineId) {
|
||||
deleteArrivalNoticeLine(line.lineId).then(() => {
|
||||
lineList.value.splice(index, 1)
|
||||
}).catch(() => {})
|
||||
} else {
|
||||
lineList.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// ============ 供应商选择 ============
|
||||
const showSupplierDialog = ref(false)
|
||||
const supplierSearch = reactive({ supplierName: '', contact1: '' })
|
||||
const supplierList = ref<PoSupplier[]>([])
|
||||
|
||||
function openSupplierDialog() {
|
||||
if (dialogMode.value === 'view') return
|
||||
showSupplierDialog.value = true
|
||||
loadSuppliers()
|
||||
}
|
||||
|
||||
async function loadSuppliers() {
|
||||
const res = await getPoSupplierList({
|
||||
supplierName: supplierSearch.supplierName || undefined,
|
||||
contact1: supplierSearch.contact1 || undefined
|
||||
})
|
||||
supplierList.value = res.rows
|
||||
}
|
||||
|
||||
function resetSupplierSearch() {
|
||||
supplierSearch.supplierName = ''
|
||||
supplierSearch.contact1 = ''
|
||||
loadSuppliers()
|
||||
}
|
||||
|
||||
function handleSelectSupplier(row: PoSupplier) {
|
||||
form.vendorId = row.supplierId
|
||||
form.vendorCode = row.supplierCode
|
||||
form.vendorName = row.supplierName
|
||||
form.vendorNick = row.supplierNick
|
||||
showSupplierDialog.value = false
|
||||
}
|
||||
|
||||
// ============ 物料选择 ============
|
||||
const showItemDialog = ref(false)
|
||||
const itemTableLoading = ref(false)
|
||||
const itemTableData = ref<MdItem[]>([])
|
||||
const editingLineIndex = ref(-1)
|
||||
const itemTreeRef = ref()
|
||||
const itemTypeTree = ref<any[]>([])
|
||||
const itemTypeFilter = ref('')
|
||||
const itemSearch = reactive<{ itemCode: string; itemName: string; itemTypeId: number | undefined }>({
|
||||
itemCode: '',
|
||||
itemName: '',
|
||||
itemTypeId: undefined
|
||||
})
|
||||
|
||||
watch(itemTypeFilter, (val) => {
|
||||
itemTreeRef.value?.filter(val)
|
||||
})
|
||||
|
||||
function filterTreeNode(value: string, data: any) {
|
||||
if (!value) return true
|
||||
return (data.label || '').includes(value)
|
||||
}
|
||||
|
||||
function openItemDialog(idx: number) {
|
||||
editingLineIndex.value = idx
|
||||
showItemDialog.value = true
|
||||
}
|
||||
|
||||
async function handleItemDialogOpen() {
|
||||
itemSearch.itemCode = ''
|
||||
itemSearch.itemName = ''
|
||||
itemSearch.itemTypeId = undefined
|
||||
itemTypeFilter.value = ''
|
||||
await Promise.all([loadItemTypeTree(), loadItemTableData()])
|
||||
}
|
||||
|
||||
async function loadItemTypeTree() {
|
||||
try {
|
||||
const res = await listItemType()
|
||||
const flat = (res as any).data || res || []
|
||||
const list = Array.isArray(flat) ? flat : []
|
||||
const tree = handleTree(list)
|
||||
function toTreeNodes(items: ItemType[]): any[] {
|
||||
return items.map(item => ({
|
||||
id: item.itemTypeId,
|
||||
label: item.itemTypeName || '',
|
||||
raw: item,
|
||||
children: item.children?.length ? toTreeNodes(item.children) : undefined
|
||||
}))
|
||||
}
|
||||
itemTypeTree.value = toTreeNodes(tree)
|
||||
} catch (e) {
|
||||
itemTypeTree.value = []
|
||||
}
|
||||
}
|
||||
|
||||
async function loadItemTableData() {
|
||||
itemTableLoading.value = true
|
||||
try {
|
||||
const params: any = { enableFlag: 'Y', pageNum: 1, pageSize: 100 }
|
||||
if (itemSearch.itemCode) params.itemCode = itemSearch.itemCode
|
||||
if (itemSearch.itemName) params.itemName = itemSearch.itemName
|
||||
if (itemSearch.itemTypeId) params.itemTypeId = itemSearch.itemTypeId
|
||||
const res = await listMdItem(params)
|
||||
itemTableData.value = res.rows || []
|
||||
} catch (e) {
|
||||
itemTableData.value = []
|
||||
} finally {
|
||||
itemTableLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleTreeNodeClick(node: any) {
|
||||
itemSearch.itemTypeId = node.id
|
||||
loadItemTableData()
|
||||
}
|
||||
|
||||
function resetItemSearch() {
|
||||
itemSearch.itemCode = ''
|
||||
itemSearch.itemName = ''
|
||||
itemSearch.itemTypeId = undefined
|
||||
itemTreeRef.value?.setCurrentKey(null)
|
||||
loadItemTableData()
|
||||
}
|
||||
|
||||
function loadAllItems() {
|
||||
itemSearch.itemCode = ''
|
||||
itemSearch.itemName = ''
|
||||
itemSearch.itemTypeId = undefined
|
||||
itemTypeFilter.value = ''
|
||||
itemTreeRef.value?.setCurrentKey(null)
|
||||
loadItemTableData()
|
||||
}
|
||||
|
||||
function handleSelectItem(selected: MdItem) {
|
||||
const idx = editingLineIndex.value
|
||||
if (idx < 0 || !lineList.value[idx]) return
|
||||
const row = lineList.value[idx]
|
||||
row.itemId = selected.itemId
|
||||
row.itemCode = selected.itemCode ?? ''
|
||||
row.itemName = selected.itemName ?? ''
|
||||
row.specification = selected.specification ?? ''
|
||||
row.unitOfMeasure = selected.unitOfMeasure ?? ''
|
||||
row.unitName = selected.unitName ?? selected.unitOfMeasure ?? ''
|
||||
showItemDialog.value = false
|
||||
}
|
||||
|
||||
onMounted(() => loadData())
|
||||
</script>
|
||||
@@ -118,6 +695,20 @@ onMounted(() => loadData())
|
||||
.search-card :deep(.el-card__body) { padding-bottom: 0; }
|
||||
.toolbar { margin-bottom: 16px; display: flex; gap: 8px; }
|
||||
.table-footer { margin-top: 16px; display: flex; justify-content: space-between; align-items: center; }
|
||||
.summary { font-size: 14px; color: #606266; }
|
||||
.summary .value { color: #409eff; font-weight: bold; margin-right: 20px; }
|
||||
.dialog-search { display: flex; align-items: center; flex-wrap: wrap; gap: 4px; margin-bottom: 16px; }
|
||||
.inline-select { display: flex; align-items: center; gap: 4px; }
|
||||
.inline-select .mono { font-family: monospace; color: #606266; }
|
||||
.line-section { margin-top: 12px; }
|
||||
.line-toolbar { margin-bottom: 8px; }
|
||||
|
||||
.item-dialog-body { display: flex; gap: 12px; min-height: 420px; }
|
||||
.category-tree {
|
||||
width: 180px;
|
||||
min-width: 180px;
|
||||
border-right: 1px solid #e4e7ed;
|
||||
padding-right: 12px;
|
||||
}
|
||||
.item-table-area { flex: 1; overflow: hidden; }
|
||||
.item-search-area { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; }
|
||||
.item-toolbar { margin-bottom: 8px; }
|
||||
</style>
|
||||
|
||||
@@ -162,9 +162,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table :data="lines" border size="small" max-height="420">
|
||||
<el-table :data="lines" border size="small" max-height="420" class="ebom-table ebom-table-nowrap">
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="物料编码" min-width="160">
|
||||
<el-table-column label="物料编码" min-width="160" show-overflow-tooltip>
|
||||
<template #default="{ row, $index }">
|
||||
<div class="inline-select">
|
||||
<span class="mono">{{ row.bomItemCode || '请添加物料' }}</span>
|
||||
@@ -260,12 +260,12 @@
|
||||
<el-button @click="queryAllProducts">查询所有</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="productList" border size="small" max-height="380" v-loading="productLoading">
|
||||
<el-table-column prop="itemCode" label="物料编码" width="130" />
|
||||
<el-table :data="productList" border size="small" max-height="380" v-loading="productLoading" class="ebom-table ebom-table-nowrap">
|
||||
<el-table-column prop="itemCode" label="物料编码" width="130" show-overflow-tooltip />
|
||||
<el-table-column prop="itemName" label="物料名称" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="specification" label="型号规格" width="130" show-overflow-tooltip />
|
||||
<el-table-column prop="unitName" label="主计量" width="70" align="center" />
|
||||
<el-table-column label="供应方式" width="80" align="center">
|
||||
<el-table-column label="供应方式" width="80" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ getSupplyLabel(row.itemOrProduct) }}
|
||||
</template>
|
||||
@@ -323,12 +323,12 @@
|
||||
<el-button @click="queryAllItems">查询所有</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="itemList" border size="small" max-height="380" v-loading="itemLoading">
|
||||
<el-table-column prop="itemCode" label="物料编码" width="130" />
|
||||
<el-table :data="itemList" border size="small" max-height="380" v-loading="itemLoading" class="ebom-table ebom-table-nowrap">
|
||||
<el-table-column prop="itemCode" label="物料编码" width="130" show-overflow-tooltip />
|
||||
<el-table-column prop="itemName" label="物料名称" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="specification" label="型号规格" width="130" show-overflow-tooltip />
|
||||
<el-table-column prop="unitName" label="主计量" width="70" align="center" />
|
||||
<el-table-column label="供应方式" width="80" align="center">
|
||||
<el-table-column label="供应方式" width="80" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ getSupplyLabel(row.itemOrProduct) }}
|
||||
</template>
|
||||
@@ -700,10 +700,11 @@ async function loadItemTypeTree() {
|
||||
}
|
||||
}
|
||||
|
||||
/** 左侧分类树显示物料/产品名称(itemTypeName),参考物料管理 masterdata/item */
|
||||
function toTreeNode(item: ItemType): any {
|
||||
return {
|
||||
id: item.itemTypeId,
|
||||
label: `${item.itemTypeCode || ''}-${item.itemTypeName || ''}`.replace(/^-/, ''),
|
||||
label: item.itemTypeName || item.itemTypeCode || '',
|
||||
children: item.children?.map(toTreeNode) || []
|
||||
}
|
||||
}
|
||||
@@ -732,6 +733,7 @@ function openProductDialog() {
|
||||
searchProductItems()
|
||||
}
|
||||
|
||||
/** 从物料编码选择打开:显示产品/半成品分类下的数据(含未启用) */
|
||||
async function searchProductItems() {
|
||||
productLoading.value = true
|
||||
try {
|
||||
@@ -739,7 +741,7 @@ async function searchProductItems() {
|
||||
itemCode: productQuery.itemCode || undefined,
|
||||
itemName: productQuery.itemName || undefined,
|
||||
itemTypeId: productQuery.itemTypeId,
|
||||
enableFlag: 'Y',
|
||||
itemOrProduct: 'PRODUCT',
|
||||
pageNum: productQuery.pageNum,
|
||||
pageSize: productQuery.pageSize
|
||||
})
|
||||
@@ -799,6 +801,7 @@ function openItemDialog(idx: number) {
|
||||
searchSubItems()
|
||||
}
|
||||
|
||||
/** 从新增物料按钮打开:显示原材料分类下的数据 */
|
||||
async function searchSubItems() {
|
||||
itemLoading.value = true
|
||||
try {
|
||||
@@ -806,6 +809,7 @@ async function searchSubItems() {
|
||||
itemCode: itemQuery.itemCode || undefined,
|
||||
itemName: itemQuery.itemName || undefined,
|
||||
itemTypeId: itemQuery.itemTypeId,
|
||||
itemOrProduct: 'ITEM',
|
||||
enableFlag: 'Y',
|
||||
pageNum: itemQuery.pageNum,
|
||||
pageSize: itemQuery.pageSize
|
||||
@@ -959,4 +963,19 @@ onMounted(() => {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* 表格表头和内容不换行,过长截断显示 */
|
||||
.ebom-table-nowrap :deep(.el-table__header th),
|
||||
.ebom-table-nowrap :deep(.el-table__body td) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ebom-table-nowrap :deep(.el-table__body td .cell) {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
min-width: 0;
|
||||
}
|
||||
.ebom-table-nowrap :deep(.el-table__body td .el-input),
|
||||
.ebom-table-nowrap :deep(.el-table__body td .el-input-number) {
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -101,9 +101,10 @@
|
||||
border
|
||||
style="width: 100%"
|
||||
:max-height="tableHeight"
|
||||
class="ebom-table ebom-table-nowrap"
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="60" align="center" fixed="left" />
|
||||
<el-table-column prop="bomCode" label="单据编码" width="140" fixed="left">
|
||||
<el-table-column prop="bomCode" label="单据编码" width="140" fixed="left" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-link type="primary" @click="handleView(row)">{{ row.bomCode }}</el-link>
|
||||
</template>
|
||||
@@ -114,7 +115,7 @@
|
||||
<el-table-column prop="version" label="版本号" width="80" align="center" />
|
||||
<el-table-column prop="versionDesc" label="版本说明" width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="drawingNo" label="图纸号" width="100" show-overflow-tooltip />
|
||||
<el-table-column prop="status" label="单据状态" width="80" align="center">
|
||||
<el-table-column prop="status" label="单据状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="getStatusType(row.status)" size="small">{{ getStatusLabel(row.status) }}</el-tag>
|
||||
</template>
|
||||
@@ -142,10 +143,11 @@
|
||||
border
|
||||
style="width: 100%"
|
||||
:max-height="tableHeight"
|
||||
class="ebom-table ebom-table-nowrap"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" fixed="left" />
|
||||
<el-table-column prop="bomCode" label="单据编码" width="140" fixed="left">
|
||||
<el-table-column prop="bomCode" label="单据编码" width="140" fixed="left" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-link type="primary" @click="handleView(row)">{{ row.bomCode }}</el-link>
|
||||
</template>
|
||||
@@ -155,7 +157,7 @@
|
||||
{{ row.bomDate || row.createTime?.slice(0, 10) || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="单据状态" width="80" align="center">
|
||||
<el-table-column prop="status" label="单据状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="getStatusType(row.status)" size="small">{{ getStatusLabel(row.status) }}</el-tag>
|
||||
</template>
|
||||
@@ -165,8 +167,8 @@
|
||||
{{ getBizTypeLabel(row.businessType) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="deptName" label="业务部门" width="100" align="center" />
|
||||
<el-table-column prop="operatorName" label="业务人员" width="100" align="center">
|
||||
<el-table-column prop="deptName" label="业务部门" width="100" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="operatorName" label="业务人员" width="100" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ row.operatorName || row.createBy || '-' }}
|
||||
</template>
|
||||
@@ -658,4 +660,14 @@ onMounted(() => {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* 表格表头和内容不换行,过长截断显示 */
|
||||
.ebom-table-nowrap :deep(.el-table__header th),
|
||||
.ebom-table-nowrap :deep(.el-table__body td) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ebom-table-nowrap :deep(.el-table__body td .cell) {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
|
||||
171
mom-frontend-vue2/src/components/print/LocationLabelPrint.vue
Normal file
171
mom-frontend-vue2/src/components/print/LocationLabelPrint.vue
Normal file
@@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:visible="visible"
|
||||
@close="handleClose"
|
||||
fullscreen
|
||||
destroy-on-close
|
||||
:show-close="false"
|
||||
class="location-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="locations.length === 0" class="label-empty">暂无库区数据</div>
|
||||
<div v-else class="label-grid">
|
||||
<div
|
||||
v-for="(loc, idx) in locations"
|
||||
:key="loc.locationId || idx"
|
||||
class="label-card"
|
||||
>
|
||||
<div class="label-qr">
|
||||
<QrCode :value="loc.locationCode || String(loc.locationId || '')" :size="72" />
|
||||
</div>
|
||||
<div class="label-name">{{ loc.locationName || '-' }}</div>
|
||||
<div class="label-code">{{ loc.locationCode || '-' }}</div>
|
||||
<div class="label-info">
|
||||
<span v-if="loc.area != null">面积:{{ loc.area }} ㎡</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import QrCode from './QrCode.vue'
|
||||
|
||||
export default {
|
||||
name: 'LocationLabelPrint',
|
||||
components: { QrCode },
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
locations: {
|
||||
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(4, 1fr);
|
||||
gap: 16px;
|
||||
max-width: 1200px;
|
||||
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>
|
||||
@@ -48,6 +48,17 @@
|
||||
v-hasPermi="['mes:wm:location:remove']"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
icon="el-icon-printer"
|
||||
size="mini"
|
||||
:disabled="multiple"
|
||||
@click="handleBatchPrint"
|
||||
v-hasPermi="['mes:wm:location:print']"
|
||||
>批量打印</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
@@ -107,6 +118,13 @@
|
||||
@click="handleHiPrint(scope.row)"
|
||||
v-hasPermi="['mes:wm:location:print']"
|
||||
>标签打印</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-document"
|
||||
@click="handleSimplePrint(scope.row)"
|
||||
v-hasPermi="['mes:wm:location:print']"
|
||||
>简易打印</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -240,6 +258,12 @@
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 简易标签打印(不依赖MinIO) -->
|
||||
<LocationLabelPrint
|
||||
:visible.sync="printDialogVisible"
|
||||
:locations="printLocations"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -247,6 +271,7 @@
|
||||
import { listLocation, getLocation, delLocation, addLocation, updateLocation, changeFrozenState, setProductMixing, setBatchMixing } from "@/api/mes/wm/location";
|
||||
import {genCode} from "@/api/system/autocode/rule"
|
||||
import BarcodeImg from "@/components/barcodeImg/index.vue"
|
||||
import LocationLabelPrint from "@/components/print/LocationLabelPrint.vue"
|
||||
import { getBarcodeUrl } from '@/api/mes/wm/barcode';
|
||||
import {print} from "../../../../utils/print"
|
||||
import {getByTemplateType} from "@/api/print/template";
|
||||
@@ -255,7 +280,7 @@ export default {
|
||||
name: "Location",
|
||||
dicts: ['sys_yes_no'],
|
||||
mixins: [hiprintMixin],
|
||||
components: { BarcodeImg } ,
|
||||
components: { BarcodeImg, LocationLabelPrint },
|
||||
data() {
|
||||
return {
|
||||
//自动生成编码
|
||||
@@ -266,6 +291,7 @@ export default {
|
||||
loading: true,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
selectedRows: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
@@ -280,6 +306,9 @@ export default {
|
||||
title: "",
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 简易打印弹窗
|
||||
printDialogVisible: false,
|
||||
printLocations: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
@@ -314,6 +343,16 @@ export default {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
// 简易打印(不依赖MinIO,纯前端二维码+window.print)
|
||||
handleSimplePrint(row) {
|
||||
this.printLocations = [row]
|
||||
this.printDialogVisible = true
|
||||
},
|
||||
// 批量打印
|
||||
handleBatchPrint() {
|
||||
this.printLocations = this.selectedRows || this.locationList.filter(l => this.ids.includes(l.locationId))
|
||||
this.printDialogVisible = true
|
||||
},
|
||||
// 使用HiPrint打印
|
||||
async handleHiPrint(row) {
|
||||
let printData = row
|
||||
@@ -393,6 +432,7 @@ export default {
|
||||
// 多选框选中数据
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map(item => item.locationId)
|
||||
this.selectedRows = selection
|
||||
this.single = selection.length!==1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user