refactor(wms/mes): 统一多场景查询逻辑,移除关键字混搜
1. 移除所有多字段关键字混搜,改为各筛选字段独立查询 2. 调整excel导出相关逻辑,优化文件名处理与导入模板 3. 修复物料导入的类型解析与token获取逻辑 4. 更新依赖与前端页面文案、筛选参数
This commit is contained in:
@@ -32,9 +32,9 @@ export const helpInbound = {
|
||||
},
|
||||
{
|
||||
title: '入库记录(页签3)',
|
||||
overview: '分页展示全部入库单,结构件与精密件同表(用"类型"列区分)。支持按入库单号、物料编码、类型、关键字、入库时间筛选;可按当前条件导出 xlsx。点行「明细」在右侧抽屉查看该单的批次号/SN 行(懒加载+分页,避免精密件上万条 SN 撑爆前端)。',
|
||||
overview: '分页展示全部入库单,结构件与精密件同表(用"类型"列区分)。支持按入库单号、物料编码、类型、物料名称、入库时间筛选(各字段独立,无关键字混搜);可按当前条件导出 xlsx。点行「明细」在右侧抽屉查看该单的批次号/SN 行(懒加载+分页,避免精密件上万条 SN 撑爆前端)。',
|
||||
fields: [
|
||||
{ name: '查询条件', source: '本页签顶部筛选区。', purpose: '缩小列表范围', fill: '可组合:入库单号(模糊)/物料编码(模糊)/类型(全部·结构件·精密件)/关键字(物料名称或单号)/入库时间区间' },
|
||||
{ name: '查询条件', source: '本页签顶部筛选区。', purpose: '缩小列表范围', fill: '可组合:入库单号(模糊)/物料编码(模糊)/类型(全部·结构件·精密件)/物料名称(模糊)/入库时间区间' },
|
||||
{ name: '明细', source: '行末「明细」按钮 → 右侧抽屉。', purpose: '查看该入库单的批次号/SN/数量明细', fill: '抽屉内分页展示,不回列表里嵌套子表' },
|
||||
{ name: '导出', source: '按钮。后端 /inbound/export 按当前条件返回全部(不分页)。', purpose: '把筛选结果一次性导出为 xlsx', fill: '点【导出】下载"入库管理.xlsx"' }
|
||||
]
|
||||
@@ -141,7 +141,7 @@ export const helpLedger = {
|
||||
fields: [
|
||||
{ name: '工单号', source: '筛选条件。数据来自 MES 工单。', purpose: '只看某个工单的领料台账', fill: '输入工单号(可空=查全部工单)' },
|
||||
{ name: '物料编码', source: '筛选条件。', purpose: '按物料编码精确过滤', fill: '输入物料编码(可空)' },
|
||||
{ name: '物料名称', source: '筛选条件。', purpose: '按物料名称模糊过滤', fill: '输入物料名称关键字(可空)' },
|
||||
{ name: '物料名称', source: '筛选条件。', purpose: '按物料名称模糊过滤', fill: '输入物料名称(可空)' },
|
||||
{ name: '状态', source: '筛选条件。可选项:待领料 / 部分领料 / 领料完结。', purpose: '按领料进度过滤', fill: '待领料=还没出过库;部分领料=已出库但没领完;领料完结=缺口为 0(前两种均由"进行中"按已出库数实时派生)' },
|
||||
{ name: 'ID', source: '台账记录主键(系统生成,只读)。', purpose: '唯一标识一行台账,便于定位与对接', fill: '系统自动生成,无需填写' },
|
||||
{ name: '需求总量 / 已出库 / 缺口', source: '系统计算。需求总量来自 BOM;已出库随出库自动累加;缺口 = 需求总量 − 已出库。', purpose: '一眼看清某物料还差多少未领', fill: '自动;缺口为 0 即领料完结' },
|
||||
|
||||
@@ -6,6 +6,7 @@ import request from '../utils/request'
|
||||
import { fmtTime } from '../utils/format'
|
||||
import { can } from '../utils/perm'
|
||||
import { exportXlsxFetch } from '../utils/export'
|
||||
import { getToken } from '../utils/auth'
|
||||
import PageHelp from '../components/PageHelp.vue'
|
||||
import { helpZone, helpMaterial } from '../help'
|
||||
|
||||
@@ -237,16 +238,18 @@ async function onMaterialFile(e) {
|
||||
try {
|
||||
const fd = new FormData()
|
||||
fd.append('file', file)
|
||||
const token = JSON.parse(localStorage.getItem('token') || '""')
|
||||
// 注意:token 是纯文本存储,不能用 JSON.parse(会对 JWT 抛 SyntaxError 误报"导入异常")
|
||||
const res = await fetch('/api/material/import', {
|
||||
method: 'POST',
|
||||
headers: { Authorization: 'Bearer ' + token },
|
||||
headers: { Authorization: 'Bearer ' + getToken() },
|
||||
body: fd
|
||||
})
|
||||
const body = await res.json().catch(() => ({}))
|
||||
const body = await res.json().catch(() => null)
|
||||
const d = body?.data || {}
|
||||
if (!res.ok || body?.code !== 0) {
|
||||
ElMessage.error('导入失败:' + (body?.message || '未知错误'))
|
||||
// 业务/权限错误:优先展示后端中文 message;解析不出 body 时按 HTTP 状态给中文提示
|
||||
const httpText = res.status === 401 ? '登录已过期,请重新登录' : `服务器错误(${res.status})`
|
||||
ElMessage.error('导入失败:' + (body?.message || httpText))
|
||||
return
|
||||
}
|
||||
if (d.errors && d.errors.length) {
|
||||
@@ -258,7 +261,7 @@ async function onMaterialFile(e) {
|
||||
ElMessage.success(`导入成功 ${d.success || 0} 条`)
|
||||
await loadMaterials()
|
||||
} catch (err) {
|
||||
ElMessage.error('导入异常:' + (err?.message || err))
|
||||
ElMessage.error('导入失败:网络异常或登录已过期,请刷新页面重试')
|
||||
} finally {
|
||||
matImporting.value = false
|
||||
}
|
||||
@@ -373,7 +376,7 @@ async function onMaterialFile(e) {
|
||||
<el-button type="success" @click="openMatCreate">新增物料</el-button>
|
||||
<el-button v-if="can('material:import')" type="warning" :loading="matImporting" @click="pickMaterialFile">导入</el-button>
|
||||
<el-button v-if="can('material:export')" :loading="matExporting" @click="exportMaterials">导出</el-button>
|
||||
<el-tooltip v-if="can('material:import')" content="Excel 模板(首行表头):物料编码 | 名称 | 简称 | 规格 | 单位 | 类型(1结构件/2精密件) | 说明">
|
||||
<el-tooltip v-if="can('material:import')" content="Excel 模板(首行表头):物料编码 | 名称 | 简称 | 规格 | 单位 | 类型(结构件/精密件) | 说明">
|
||||
<el-link type="info" :underline="false" style="margin-left:4px">导入说明</el-link>
|
||||
</el-tooltip>
|
||||
<input ref="matFileRef" type="file" accept=".xlsx,.xls" style="display:none" @change="onMaterialFile" />
|
||||
|
||||
@@ -168,7 +168,7 @@ async function importExcel(mode, which) {
|
||||
|
||||
/* ===================== Tab3 入库记录(统一主表 + 抽屉明细,不分页内嵌子表) ===================== */
|
||||
// 查询条件
|
||||
const filters = reactive({ inboundNo: '', materialCode: '', manageMode: '', keyword: '' })
|
||||
const filters = reactive({ inboundNo: '', materialCode: '', manageMode: '', materialName: '' })
|
||||
const dateRange = ref([]) // [开始日期, 结束日期] YYYY-MM-DD
|
||||
const loadingRec = ref(false)
|
||||
const list = ref([])
|
||||
@@ -184,7 +184,7 @@ async function loadRecords() {
|
||||
inboundNo: filters.inboundNo.trim(),
|
||||
materialCode: filters.materialCode.trim(),
|
||||
manageMode: filters.manageMode,
|
||||
keyword: filters.keyword.trim(),
|
||||
materialName: filters.materialName.trim(),
|
||||
startDate: dateRange.value?.[0] || '',
|
||||
endDate: dateRange.value?.[1] || '',
|
||||
page: page.current,
|
||||
@@ -206,7 +206,7 @@ function resetFilters() {
|
||||
filters.inboundNo = ''
|
||||
filters.materialCode = ''
|
||||
filters.manageMode = ''
|
||||
filters.keyword = ''
|
||||
filters.materialName = ''
|
||||
dateRange.value = []
|
||||
search()
|
||||
}
|
||||
@@ -218,7 +218,7 @@ async function exportCsv() {
|
||||
inboundNo: filters.inboundNo.trim(),
|
||||
materialCode: filters.materialCode.trim(),
|
||||
manageMode: filters.manageMode,
|
||||
keyword: filters.keyword.trim(),
|
||||
materialName: filters.materialName.trim(),
|
||||
startDate: dateRange.value?.[0] || '',
|
||||
endDate: dateRange.value?.[1] || ''
|
||||
}, '入库管理.xlsx')
|
||||
@@ -410,8 +410,8 @@ onMounted(() => {
|
||||
<el-option label="精密件" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="关键字">
|
||||
<el-input v-model="filters.keyword" placeholder="物料名称/单号" clearable style="width:180px" @keyup.enter="search" />
|
||||
<el-form-item label="物料名称">
|
||||
<el-input v-model="filters.materialName" placeholder="物料名称(模糊)" clearable style="width:160px" @keyup.enter="search" />
|
||||
</el-form-item>
|
||||
<el-form-item label="入库时间">
|
||||
<el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD"
|
||||
|
||||
@@ -42,12 +42,15 @@ export async function exportXlsxFetch(path, params, fallbackName = '导出.xlsx'
|
||||
const body = await res.json().catch(() => ({}))
|
||||
throw new Error(body.message || '导出失败')
|
||||
}
|
||||
// 从 Content-Disposition 解析文件名
|
||||
// 从 Content-Disposition 解析文件名:优先 filename*=UTF-8''(后端放中文原名),兜底 filename(ASCII 名)
|
||||
let filename = fallbackName
|
||||
const cd = res.headers.get('Content-Disposition') || ''
|
||||
const m = cd.match(/filename="?([^";]+)"?/)
|
||||
let m = cd.match(/filename\*=UTF-8''([^;]+)/i)
|
||||
if (m) {
|
||||
filename = decodeURIComponent(m[1])
|
||||
filename = decodeURIComponent(m[1]) || filename
|
||||
} else {
|
||||
m = cd.match(/filename="?([^";]+)"?/)
|
||||
if (m) filename = decodeURIComponent(m[1])
|
||||
}
|
||||
const blob = await res.blob()
|
||||
const url = URL.createObjectURL(blob)
|
||||
|
||||
Reference in New Issue
Block a user