Files
klp-oa/klp-ui/src/components/KLPService/RawMaterialSelect/index.vue
砂糖 96b49e71f4 feat(KLPService选择组件): 新增默认查询参数支持并优化钢卷录入页面
1. 为ProductSelect和RawMaterialSelect组件新增defaultQueryParams属性,支持传入默认查询参数合并到请求参数中
2. 优化钢卷录入页面:默认选中成品类型,根据解析的钢卷数据自动设置物料选择器的查询参数
3. 移除页面冗余空行和注释代码
2026-05-21 13:38:35 +08:00

548 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="raw-material-selector">
<!-- 触发器按钮 -->
<div class="el-input" v-if="!readonly" type="text" @click="dialogVisible = true">
<div class="el-input__inner">
<el-icon class="el-icon-search"></el-icon>
{{ buttonText }}
</div>
</div>
<span v-else class="readonly-text">{{ displayText }}</span>
<!-- 选择对话框 -->
<el-dialog title="选择原材料" :visible.sync="dialogVisible" width="1200px" :close-on-click-modal="false"
@close="handleClose" append-to-body>
<!-- 搜索区域 -->
<el-form :inline="true" :model="queryParams" class="search-form" size="small">
<el-form-item label="名称">
<MemoInput storageKey="productName" v-model="queryParams.rawMaterialName" placeholder="请输入原材料名称" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="规格">
<MemoInput storageKey="coilSpec" v-model="queryParams.specification" placeholder="请输入规格"
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="材质">
<MemoInput storageKey="material" v-model="queryParams.material" placeholder="请输入材质"
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="厂家">
<MemoInput storageKey="manufacturer" v-model="queryParams.manufacturer" placeholder="请输入厂家"
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="表面处理">
<MemoInput storageKey="surfaceTreatmentDesc" v-model="queryParams.surfaceTreatmentDesc" placeholder="请输入表面处理"
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="镀层质量">
<MemoInput storageKey="zincLayer" v-model="queryParams.zincLayer" placeholder="请输入镀层质量"
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
<el-button type="primary" plain icon="el-icon-plus" @click="handleAdd">新增</el-button>
</el-form-item>
</el-form>
<h3>最近选择</h3>
<el-table v-loading="recentLoading" title="最近选择" :data="recentlySelectedList" @row-click="handleRowClick">
<el-table-column label="编号" prop="rawMaterialCode" />
<el-table-column label="名称" prop="rawMaterialName" />
<el-table-column label="规格" prop="specification" />
<el-table-column label="材质" prop="material" />
<el-table-column label="厂家" prop="manufacturer" />
<el-table-column label="表面处理" prop="surfaceTreatmentDesc" />
<el-table-column label="镀层质量" prop="zincLayer" />
</el-table>
<!-- 卡片布局 -->
<div v-loading="loading" class="card-container">
<div v-for="(item, index) in rawMaterialList" :key="index + item.rawMaterialId" class="material-card"
:class="{ 'selected-card': isSelected(item.rawMaterialId) }" @click="handleRowClick(item)">
<!-- 卡片内容包含所有原表格字段 -->
<div class="card-content">
<div class="card-title">
<span class="name">{{ item.rawMaterialName }}</span>
<span class="code">[{{ item.specification || '-' }}]</span>
</div>
<div class="card-info">
<div class="info-item">材质<span>{{ item.material || '-' }}</span></div>
<div class="info-item">厂家<span>{{ item.manufacturer || '-' }}</span></div>
<div class="info-item">表面处理<span>{{ item.surfaceTreatmentDesc || '-' }}</span></div>
<div class="info-item">镀层质量<span>{{ item.zincLayer || '-' }}</span></div>
</div>
</div>
</div>
<!-- 空数据提示 -->
<div class="empty-tip" v-if="rawMaterialList.length === 0 && !loading">
暂无匹配的原材料数据,
<span @click="handleAdd" style="cursor: pointer; color: #409eff;">点击快速新增原材料</span>
</div>
</div>
<!-- 分页 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
@pagination="getList" />
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
</div>
</el-dialog>
<!-- 添加或修改原材料对话框 -->
<el-dialog :title="title" :visible.sync="open" width="400px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
<!-- <el-form-item label="原材料编号" prop="rawMaterialCode">
<el-input v-model="form.rawMaterialCode" placeholder="请输入原材料编号" />
</el-form-item> -->
<el-form-item label="原材料名称" prop="rawMaterialName">
<MemoInput storageKey="productName" v-model="form.rawMaterialName" placeholder="请输入原材料名称" />
</el-form-item>
<el-form-item label="规格" prop="specification">
<MemoInput storageKey="coilSpec" v-model="form.specification" placeholder="请输入规格" />
</el-form-item>
<el-form-item label="材质" prop="material">
<MemoInput storageKey="material" v-model="form.material" placeholder="请输入材质" />
</el-form-item>
<el-form-item label="厂家" prop="manufacturer">
<MemoInput storageKey="manufacturer" v-model="form.manufacturer" placeholder="请输入厂家" />
</el-form-item>
<el-form-item label="表面处理" prop="surfaceTreatmentDesc">
<MemoInput storageKey="surfaceTreatmentDesc" v-model="form.surfaceTreatmentDesc" placeholder="请输入表面处理" />
</el-form-item>
<el-form-item label="镀层质量" prop="zincLayer">
<MemoInput storageKey="zincLayer" v-model="form.zincLayer" placeholder="请输入镀层质量" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listRawMaterial, getRawMaterial, updateRawMaterial, addRawMaterial } from '@/api/wms/rawMaterial';
import MemoInput from '@/components/MemoInput/index.vue'
export default {
name: 'RawMaterialSelector',
components: {
MemoInput
},
props: {
// 绑定值原材料ID 单选字符串格式
value: {
type: [String, Number],
default: ''
},
// 是否只读
readonly: {
type: Boolean,
default: false
},
// 过滤条件
filters: {
type: Object,
default: () => ({})
},
defaultQueryParams: {
type: Object,
default: () => ({})
}
},
data() {
return {
dialogVisible: false,
loading: false,
recentLoading: false,
rawMaterialList: [],
recentlySelectedList: [],
total: 0,
queryParams: {
pageNum: 1,
pageSize: 10,
rawMaterialCode: undefined,
rawMaterialName: undefined,
specification: undefined,
material: undefined,
manufacturer: undefined,
surfaceTreatmentDesc: undefined,
zincLayer: undefined
},
// ✅ 单选核心变量选中的ID和单条数据对象
selectedId: '',
selectedRow: {},
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 表单参数
form: {},
// 表单校验
rules: {
// rawMaterialCode: [
// { required: true, message: "原材料编号不能为空", trigger: "blur" }
// ],
rawMaterialName: [
{ required: true, message: "原材料名称不能为空", trigger: "blur" }
],
unit: [
{ required: true, message: "计量单位不能为空", trigger: "blur" }
],
specification: [
{ required: true, message: "规格不能为空", trigger: "blur" }
],
},
// 提交按钮加载状态
buttonLoading: false,
};
},
computed: {
// 选择按钮展示文本
buttonText() {
if (!this.selectedId) {
return '请选择原材料';
}
return `${this.selectedRow.rawMaterialName}[${this.selectedRow.specification || '-'}] (${this.selectedRow.material || '-'})`;
},
// 只读状态展示文本
displayText() {
return this.selectedRow.rawMaterialName || '未选择';
}
},
watch: {
// 监听父组件传值 实现值回显 ✅修复原回显BUG
value: {
immediate: true,
async handler(val) {
this.selectedId = val || '';
// 有选中ID则获取详情赋值
if (this.selectedId) {
const res = await getRawMaterial(this.selectedId);
if (res.code === 200) this.selectedRow = res.data || {};
} else {
this.selectedRow = {};
}
}
},
// 弹窗打开时加载数据
dialogVisible(val) {
if (val) {
this.listRecentlySelected();
this.getList();
}
},
defaultQueryParams(val) {
this.queryParams = { ...this.queryParams, ...val };
}
},
methods: {
// 判断卡片是否选中
isSelected(rawMaterialId) {
return this.selectedId === rawMaterialId;
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
rawMaterialId: undefined,
rawMaterialCode: undefined,
rawMaterialName: undefined,
steelGrade: undefined,
targetColdGrade: undefined,
baseMaterialId: undefined,
surfaceTreatmentId: undefined,
thickness: undefined,
thicknessDeviation: undefined,
width: undefined,
targetColdWidth: undefined,
targetColdThickness: undefined,
crown: undefined,
coilWeight: undefined,
surfaceQuality: undefined,
hardnessHv5: undefined,
hardnessDiff: undefined,
compositionMn: undefined,
compositionP: undefined,
grainSize: undefined,
headTailCutFlag: undefined,
inspectionResult: undefined,
isEnabled: undefined,
delFlag: undefined,
remark: undefined,
createTime: undefined,
createBy: undefined,
updateTime: undefined,
updateBy: undefined,
specification: undefined,
unit: '卷',
material: undefined,
manufacturer: undefined,
surfaceTreatmentDesc: undefined,
zincLayer: undefined,
};
this.resetForm("form");
},
/** 新增按钮操作 */
handleAdd() {
// this.reset();
this.form = { ...this.queryParams };
this.open = true;
this.title = "添加原材料";
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
this.buttonLoading = true;
if (this.form.rawMaterialId != null) {
updateRawMaterial({
...this.form,
}).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
this.$store.dispatch('category/getRawMaterialMap');
}).finally(() => {
this.buttonLoading = false;
});
} else {
addRawMaterial({
...this.form,
unit: '卷',
rawMaterialCode: this.form.material || '' + (this.form.zincLayer || '') + new Date().getTime(),
}).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
this.$store.dispatch('category/getRawMaterialMap');
}).finally(() => {
this.buttonLoading = false;
});
}
}
});
},
// 获取最近选择的原材料数据
async listRecentlySelected() {
try {
this.recentLoading = true;
const ids = localStorage.getItem('recentlySelectedRawMaterialIds') || '';
const idsArray = ids.split(',').filter(id => id);
if (idsArray.length === 0) return;
const list = await Promise.all(idsArray.map(async id => {
const response = await getRawMaterial(id);
return response.data || {};
}));
this.recentlySelectedList = list || [];
} catch (error) {
console.error('获取最近选择的原材料列表失败', error);
this.$message.error('获取最近选择的原材料列表失败');
} finally {
this.recentLoading = false;
}
},
// 获取原材料列表 ✅修复原数据重复BUG
async getList() {
try {
this.loading = true;
const params = { ...this.queryParams, ...this.filters };
const response = await listRawMaterial(params);
if (response.code === 200) {
this.rawMaterialList = response.rows || [];
this.total = response.total || 0;
// 选中的项不在列表中则追加进去,保证选中高亮
if (this.selectedId && !this.rawMaterialList.some(item => item.rawMaterialId === this.selectedId)) {
const res = await getRawMaterial(this.selectedId);
res.code === 200 && res.data && this.rawMaterialList.push(res.data);
}
}
} catch (error) {
console.error('获取原材料列表失败', error);
this.$message.error('获取原材料列表失败');
} finally {
this.loading = false;
}
},
// 搜索
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
// 重置搜索条件
resetQuery() {
this.queryParams = {
pageNum: 1,
pageSize: 10,
rawMaterialCode: undefined,
rawMaterialName: undefined,
specification: undefined,
material: undefined,
manufacturer: undefined,
surfaceTreatmentDesc: undefined,
zincLayer: undefined
};
this.getList();
},
// 点击行/卡片选中 核心单选事件
handleRowClick(row) {
// 缓存最近选择数据最多5条
const index = this.recentlySelectedList.findIndex(item => item.rawMaterialId === row.rawMaterialId);
if (index > -1) this.recentlySelectedList.splice(index, 1);
this.recentlySelectedList.unshift(row);
this.recentlySelectedList.length > 5 && this.recentlySelectedList.pop();
localStorage.setItem('recentlySelectedRawMaterialIds', this.recentlySelectedList.map(item => item.rawMaterialId).join(','));
// 赋值选中状态
this.selectedId = row.rawMaterialId;
this.selectedRow = row;
// 向父组件传值
this.$emit('input', this.selectedId);
this.$emit('change', this.selectedId, this.selectedRow);
// 关闭弹窗
this.dialogVisible = false;
},
// 弹窗关闭时恢复选中状态,防止误操作
handleClose() {
this.selectedId = this.value || '';
this.dialogVisible = false;
}
},
// mounted() {
// this.listRecentlySelected();
// this.getList();
// }
};
</script>
<style scoped lang="scss">
.raw-material-selector {
display: inline-block;
}
.search-form {
margin-bottom: 20px;
flex-wrap: wrap;
}
.readonly-text {
color: #606266;
line-height: 1;
padding: 8px 0;
display: inline-block;
}
::v-deep .el-dialog__body {
padding: 20px;
overflow-x: auto;
}
::v-deep .el-form-item {
margin-bottom: 15px;
margin-right: 15px;
}
/* 卡片容器样式 */
.card-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
gap: 20px;
margin-bottom: 20px;
min-height: 400px;
padding: 10px;
}
/* 卡片样式 */
.material-card {
border: 1px solid #e6e6e6;
border-radius: 0px;
padding: 12px 16px;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
background: #fff;
&:hover {
border-color: #409eff;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08);
}
}
/* 选中卡片样式 */
.selected-card {
border-color: #409eff;
background-color: #f0f7ff;
}
/* 卡片内容样式 */
.card-content {
width: 100%;
}
.card-title {
display: flex;
align-items: center;
margin-bottom: 12px;
font-weight: 500;
}
.name {
font-size: 16px;
color: #1989fa;
margin-right: 8px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.code {
font-size: 14px;
color: #909399;
}
.card-info {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
font-size: 14px;
color: #606266;
}
.info-item {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.info-item span {
color: #303133;
}
/* 空数据提示 */
.empty-tip {
grid-column: 1 / -1;
display: flex;
align-items: center;
justify-content: center;
height: 400px;
color: #909399;
font-size: 16px;
}
</style>