Files
klp-oa/klp-ui/src/views/wms/coil/docorrent.vue
砂糖 fa198181ee feat(wms): 新增报表透视表组件并优化操作状态显示
refactor: 替换select为tag显示操作状态
feat: 添加今天按钮到时间范围选择器
fix: 移除调试用的console.log
style: 调整按钮间距样式
feat: 新增厂家材质透视表和宽度厚度统计表
refactor: 优化导出功能去除orderBy参数
feat: 添加表面处理等查询条件
feat: 在coilTable中添加settings插槽
feat: 使用下拉菜单整合报表操作按钮
2026-05-05 14:52:24 +08:00

812 lines
27 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="app-container">
<!-- 搜索栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="80px">
<el-form-item label="钢卷号" prop="currentCoilNo">
<el-input v-model="queryParams.currentCoilNo" placeholder="请输入钢卷号" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="操作类型" prop="actionType">
<el-select v-model="queryParams.actionType" placeholder="请选择操作类型" clearable filterable>
<el-option v-for="item in dict.type.action_type" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- <el-form-item label="只看待操作" prop="actionStatus">
<el-switch @change="switchActionStatus" v-model="queryParams.actionSwitch" />
</el-form-item> -->
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple"
@click="handleDelete">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-refresh" size="mini" @click="handleRefresh"
:disabled="buttonLoading" v-loading="buttonLoading">刷新</el-button>
</el-col>
<el-col :span="3">
<el-checkbox v-model="rubbish" label="1" @change="getList">查看被删除操作</el-checkbox>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 数据表格 -->
<el-table v-loading="loading" :data="actionList" @selection-change="handleSelectionChange"
:row-class-name="tableRowClassName">
<el-table-column type="selection" width="55" align="center" />
<!-- <el-table-column label="序号" type="index" width="60" align="center" /> -->
<el-table-column label="钢卷号" align="center" prop="currentCoilNo">
<template slot-scope="scope">
<coil-no :coil-no="scope.row.currentCoilNo"></coil-no>
</template>
</el-table-column>
<el-table-column label="操作类型" align="center" prop="actionType" width="160">
<template slot-scope="scope">
<el-select @change="handleStatusChange(scope.row)" v-model="scope.row.actionType" placeholder="请选择操作类型" clearable filterable>
<el-option label="入库/收货操作" :value="401" />
<el-option label="发货操作" :value="402" />
<el-option label="移库操作" :value="403" />
<el-option label="通过库区编辑钢卷" :value="404" />
<el-option label="钢卷打包" :value="405" />
<el-option v-for="item in dict.type.action_type" :key="item.value" :label="item.label" :value="parseInt(item.value)" />
</el-select>
<!-- <span v-if="scope.row.actionType === 401">入库/收货操作</span>
<span v-else-if="scope.row.actionType === 402">发货操作</span>
<span v-else-if="scope.row.actionType === 403">移库操作</span>
<span v-else-if="scope.row.actionType === 404">通过库区编辑钢卷</span>
<span v-else-if="scope.row.actionType === 405">钢卷打包</span>
<dict-tag v-else :options='dict.type.action_type' :value="scope.row.actionType"></dict-tag> -->
</template>
</el-table-column>
<el-table-column label="操作状态" align="center" prop="actionStatus" width="120">
<template slot-scope="scope">
<!-- <el-select v-model="scope.row.actionStatus" placeholder="请选择操作状态" @change="handleStatusChange(scope.row)">
<el-option label="待处理" :value="0" />
<el-option label="处理中" :value="1" />
<el-option label="已完成" :value="2" />
<el-option label="已取消" :value="3" />
</el-select> -->
<el-tag v-if="scope.row.actionStatus === 0" type="info" size="mini">待处理</el-tag>
<el-tag v-else-if="scope.row.actionStatus === 1" type="warning" size="mini">处理中</el-tag>
<el-tag v-else-if="scope.row.actionStatus === 2" type="success" size="mini">已完成</el-tag>
<el-tag v-else-if="scope.row.actionStatus === 3" type="danger" size="mini">已取消</el-tag>
</template>
</el-table-column>
<el-table-column label="优先级" align="center" prop="priority" width="90">
<template slot-scope="scope">
<el-tag v-if="scope.row.priority === 0" type="info" size="mini">普通</el-tag>
<el-tag v-else-if="scope.row.priority === 1" type="warning" size="mini">重要</el-tag>
<el-tag v-else-if="scope.row.priority === 2" type="danger" size="mini">紧急</el-tag>
</template>
</el-table-column>
<el-table-column label="来源" align="center" prop="sourceType" width="80">
<template slot-scope="scope">
<el-tag v-if="scope.row.sourceType === 'scan'" type="success" size="mini">
<i class="el-icon-mobile"></i> 扫码
</el-tag>
<el-tag v-else type="info" size="mini">
<i class="el-icon-edit-outline"></i> 手动
</el-tag>
</template>
</el-table-column>
<el-table-column label="新增时间" align="center" prop="createTime" width="155" :show-overflow-tooltip="true">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作人" align="center" prop="updateBy" width="100" />
<el-table-column label="创建人" align="center" prop="createByName" width="140">
<template slot-scope="scope">
<el-select @change="handleProcessTimeChange(scope.row)" v-model="scope.row.createBy" placeholder="请选择创建人" filterable>
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userName" />
</el-select>
</template>
</el-table-column>
<el-table-column label="完成时间" align="center" prop="completeTime" width="220" :show-overflow-tooltip="true">
<template slot-scope="scope">
<el-date-picker @change="handleProcessTimeChange(scope.row)" value-format="yyyy-MM-dd HH:mm:ss"
style="width: 200px" v-model="scope.row.completeTime" type="datetime" placeholder="选择完成时间" />
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
<el-table-column label="操作" align="center" width="200" class-name="small-padding fixed-width" fixed="right">
<template slot-scope="scope">
<!-- 待处理状态显示操作按钮 -->
<!-- <template v-if="scope.row.actionStatus === 0">
<el-button size="mini" type="primary" icon="el-icon-edit" @click="handleProcess(scope.row)"
v-loading="buttonLoading" :disabled="buttonLoading">操作</el-button>
<el-button size="mini" type="danger" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button>
</template> -->
<!-- 处理中状态显示继续按钮 -->
<!-- <template v-else-if="scope.row.actionStatus === 1">
<el-button size="mini" type="warning" icon="el-icon-edit" @click="handleProcess(scope.row)"
:disabled="buttonLoading" v-loading="buttonLoading">继续</el-button>
<el-button size="mini" type="info" icon="el-icon-close" @click="handleCancel(scope.row)">取消</el-button>
</template> -->
<!-- 已完成或已取消状态显示删除按钮 -->
<template v-if="scope.row.actionStatus === 2 || scope.row.actionStatus === 3">
<el-button v-if="scope.row.delFlag == 0" size="mini" type="danger" icon="el-icon-delete"
@click="handleDelete(scope.row)">删除</el-button>
<el-button v-if="scope.row.delFlag == 2" size="mini" type="success" icon="el-icon-refresh"
@click="handleRestore(scope.row)">还原</el-button>
</template>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
@pagination="getList" />
<!-- 添加或修改对话框 -->
<el-dialog :title="title" :visible.sync="open" width="1200px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-form-item label="钢卷" prop="coilId">
<coil-selector v-model="form.coilId" :use-trigger="true" @select="handleCoilSelect" />
</el-form-item>
<el-form-item label="操作类型" prop="actionType">
<div class="action-type-cards">
<!-- 分条操作区 -->
<div class="card-section" v-if="splitTypes.length > 0">
<div class="section-title">分条操作</div>
<div class="action-cards-row">
<div v-for="item in splitTypes" :key="item.value" class="action-card split-card"
:class="{ 'active': form.actionType == item.value }" @click="form.actionType = parseInt(item.value)">
<div class="card-icon">
<i :class="getActionIcon(item.value)"></i>
</div>
<div class="card-content">
<div class="card-title">{{ item.label }}</div>
<div class="card-desc">{{ item.remark || '钢卷分条操作' }}</div>
</div>
</div>
</div>
</div>
<!-- 合卷操作区 -->
<div class="card-section" v-if="mergeTypes.length > 0">
<div class="section-title">合卷操作</div>
<div class="action-cards-row">
<div v-for="item in mergeTypes" :key="item.value" class="action-card"
:class="{ 'active': form.actionType == item.value }" @click="form.actionType = parseInt(item.value)">
<div class="card-icon">
<i :class="getActionIcon(item.value)"></i>
</div>
<div class="card-content">
<div class="card-title">{{ item.label }}</div>
<div class="card-desc">{{ item.remark || '钢卷操作' }}</div>
</div>
</div>
</div>
</div>
<!-- 其他操作区 -->
<div class="card-section" v-if="otherTypes.length > 0">
<div class="section-title">其他操作</div>
<div class="action-cards-row">
<div v-for="item in otherTypes" :key="item.value" class="action-card"
:class="{ 'active': form.actionType == item.value }" @click="form.actionType = parseInt(item.value)">
<div class="card-icon">
<i :class="getActionIcon(item.value)"></i>
</div>
<div class="card-content">
<div class="card-title">{{ item.label }}</div>
<div class="card-desc">{{ item.remark || '钢卷操作' }}</div>
</div>
</div>
</div>
</div>
</div>
</el-form-item>
<el-form-item label="优先级" prop="priority">
<el-select v-model="form.priority" placeholder="请选择优先级">
<el-option label="普通" :value="0" />
<el-option label="重要" :value="1" />
<el-option label="紧急" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm" :disabled="buttonLoading" v-loading="buttonLoading">
</el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
listPendingAction,
getPendingAction,
delPendingAction,
addPendingAction,
updatePendingAction,
startProcess,
cancelAction,
restorePendingAction,
} from '@/api/wms/pendingAction';
import { listUser } from '@/api/system/user';
import CoilSelector from '@/components/CoilSelector';
import CoilNo from '@/components/KLPService/Renderer/CoilNo.vue';
export default {
name: 'CoilActflow',
dicts: ['action_type'],
components: {
CoilSelector,
CoilNo
},
data() {
return {
// 遮罩层
loading: true,
buttonLoading: false,
rubbish: false,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 待操作列表数据
actionList: [],
// 弹出层标题
title: '',
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 20,
currentCoilNo: null,
actionType: null,
actionStatus: null, // 默认查询待处理
priority: null
},
// 表单参数
form: {},
// 表单校验
rules: {
coilId: [
{ required: true, message: '请选择钢卷', trigger: 'blur' }
],
actionType: [
{ required: true, message: '请选择操作类型', trigger: 'change' }
]
},
// 钢卷选择器可见性
coilSelectorVisible: false,
// 用户列表
userList: [],
};
},
computed: {
// 分条操作列表100-199
splitTypes() {
if (!this.dict.type.action_type) return [];
return this.dict.type.action_type.filter(item => {
const value = parseInt(item.value);
return value >= 100 && value <= 199;
});
},
// 合卷操作列表200-299
mergeTypes() {
if (!this.dict.type.action_type) return [];
return this.dict.type.action_type.filter(item => {
const value = parseInt(item.value);
return value >= 200 && value <= 299;
});
},
// 其他操作列表200-299等
otherTypes() {
if (!this.dict.type.action_type) return [];
return this.dict.type.action_type.filter(item => {
const value = parseInt(item.value);
return value < 100 || value > 299;
});
}
},
created() {
this.getList();
// 设置定时刷新(可选,用于移动端扫码后自动刷新)
this.startAutoRefresh();
// 查询用户列表
this.getUserList();
},
beforeDestroy() {
// 清除定时器
if (this.refreshTimer) {
clearInterval(this.refreshTimer);
}
},
methods: {
/** 查询待操作列表 */
getList() {
this.loading = true;
this.buttonLoading = true;
const payload = {
...this.queryParams,
includeDeleted: this.rubbish ? 2 : 0
}
listPendingAction(payload).then(response => {
console.log('response.rows', response.rows);
this.actionList = response.rows;
this.total = response.total;
this.buttonLoading = false;
this.loading = false;
});
},
/** 查询用户列表 */
getUserList() {
listUser({ pageSize: 9999 }).then(response => {
this.userList = response.rows;
});
},
switchActionStatus(value) {
console.log(value)
if (!value) {
this.queryParams.actionStatus = null;
} else {
this.queryParams.actionStatus = '-1';
}
this.getList()
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
actionId: null,
coilId: null,
currentCoilNo: null,
actionType: null,
actionStatus: null,
priority: 0,
sourceType: 'manual',
remark: null
};
this.resetForm('form');
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm('queryForm');
this.handleQuery();
},
handleStatusChange(row) {
// console.log(row)
updatePendingAction(row).then(response => {
this.$message.success('操作状态更新成功');
this.getList();
});
},
/** 还原按钮操作 */
handleRestore(row) {
const actionId = row.actionId;
this.$modal.confirm('是否确认还原该待操作记录?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
restorePendingAction(actionId).then(response => {
this.$message.success('还原成功');
this.getList();
});
});
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.actionId);
this.single = selection.length !== 1;
this.multiple = !selection.length;
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = '添加待操作';
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const actionId = row.actionId || this.ids;
getPendingAction(actionId).then(response => {
this.form = response.data;
this.open = true;
this.title = '修改待操作';
});
},
/** 提交按钮 */
submitForm() {
this.$refs['form'].validate(valid => {
if (valid) {
this.buttonLoading = true;
if (this.form.actionId != null) {
updatePendingAction(this.form).then(response => {
this.$message.success('修改成功');
this.open = false;
this.getList();
this.buttonLoading = false;
});
} else {
addPendingAction(this.form).then(response => {
this.$message.success('新增成功');
this.open = false;
this.getList();
this.buttonLoading = false;
});
}
}
});
},
/** 完成时间改变时触发 */
handleProcessTimeChange(row) {
console.log('完成时间改变:', row);
updatePendingAction(row).then(response => {
this.$message.success('更新成功');
});
},
/** 删除按钮操作 */
handleDelete(row) {
const actionIds = row.actionId || this.ids;
this.$confirm('是否确认删除该待操作记录?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
return delPendingAction(actionIds);
}).then(() => {
this.getList();
this.$message.success('删除成功');
}).catch(() => { });
},
/** 处理操作 - 跳转到对应页面 */
handleProcess(row) {
console.log('=== 开始处理操作 ===');
console.log('待操作记录:', row);
console.log('操作类型:', row.actionType);
console.log('钢卷ID:', row.coilId);
this.buttonLoading = true;
this.$forceUpdate();
const actionType = parseInt(row.actionType);
// 特殊处理:发货和移库操作不需要跳转
if (actionType === 4 || actionType === 5 || actionType === 401 || actionType === 402) {
this.$message.info(actionType === 4 ? '发货操作已在移动端完成' : '移库操作已在移动端完成');
this.buttonLoading = false;
return;
}
// 根据操作类型跳转到不同页面
let path = '';
// 分条操作100-199
if (actionType >= 100 && actionType <= 199) {
path = '/wms/split';
}
// 合卷操作200-299
else if (actionType == 200) {
path = '/wms/merge';
}
else if (actionType < 100) {
path = '/wms/typing';
}
// 其他操作类型
else {
this.$message.error('特殊操作请到专门的页面进行处理');
this.buttonLoading = false;
return;
}
if (!path) {
this.$message.error('未知的操作类型: ' + row.actionType);
this.buttonLoading = false;
return;
}
// 更新状态为处理中
console.log('调用startProcessactionId:', row.actionId);
startProcess(row.actionId).then(response => {
console.log('开始处理响应:', response);
if (response.code !== 200) {
this.$message.error(response.msg || '更新状态失败');
return;
}
// 跳转并传递参数
console.log('准备跳转到:', path, '参数:', { coilId: row.coilId, actionId: row.actionId });
this.$router.push({
path: path,
query: {
coilId: row.coilId,
actionId: row.actionId
}
});
this.buttonLoading = false;
}).catch(error => {
console.error('更新状态失败:', error);
this.$message.error('更新状态失败: ' + (error.message || error));
}).finally(() => {
this.buttonLoading = false;
});
},
/** 取消操作 */
handleCancel(row) {
this.$confirm('是否确认取消该操作?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
return cancelAction(row.actionId);
}).then(() => {
this.$message.success('操作已取消');
this.getList();
}).catch(() => { });
},
/** 刷新列表 */
handleRefresh() {
this.getList();
this.$message.success('刷新成功');
},
/** 自动刷新 */
startAutoRefresh() {
// 每30秒自动刷新一次用于移动端扫码后自动更新列表
this.refreshTimer = setInterval(() => {
// 只在查看待处理状态时自动刷新
this.getList();
}, 30000);
},
/** 表格行样式 */
tableRowClassName({ row }) {
if (row.priority === 2) {
return 'urgent-row';
} else if (row.priority === 1) {
return 'important-row';
}
return '';
},
/** 显示钢卷选择器 */
showCoilSelector() {
this.coilSelectorVisible = true;
},
/** 钢卷选择回调 */
handleCoilSelect(coil) {
this.form.coilId = coil.coilId;
this.form.currentCoilNo = coil.currentCoilNo;
},
/** 获取状态文本 */
getStatusText(status) {
const statusMap = {
0: '待处理',
1: '处理中',
2: '已完成',
3: '已取消'
};
return statusMap[status] || '未知';
},
/** 根据操作类型获取图标 */
getActionIcon(actionType) {
const value = parseInt(actionType);
const iconMap = {
1: 'el-icon-connection', // 合卷
2: 'el-icon-s-operation', // 分条
3: 'el-icon-edit', // 更新
4: 'el-icon-truck', // 发货
5: 'el-icon-s-grid', // 移库
101: 'el-icon-scissors', // 纵剪分条
102: 'el-icon-s-operation', // 横切分条
103: 'el-icon-s-unfold' // 开卷分条
};
return iconMap[value] || 'el-icon-s-operation';
}
}
};
</script>
<style scoped lang="scss">
.app-container {
::v-deep .urgent-row {
background: #fef0f0 !important;
}
::v-deep .important-row {
background: #fdf6ec !important;
}
// 优化按钮文字颜色
// 实心按钮:白色文字(在深色背景上清晰可见)
::v-deep .el-button--primary.el-button--mini:not(.is-plain) {
color: #fff;
}
::v-deep .el-button--danger.el-button--mini:not(.is-plain) {
color: #fff;
}
::v-deep .el-button--warning.el-button--mini:not(.is-plain) {
color: #fff;
}
::v-deep .el-button--info.el-button--mini:not(.is-plain) {
color: #fff;
}
::v-deep .el-button--success.el-button--mini:not(.is-plain) {
color: #fff;
}
// plain按钮同色系深色文字在浅色背景上
::v-deep .el-button--primary.el-button--mini.is-plain {
color: #409eff;
}
::v-deep .el-button--danger.el-button--mini.is-plain {
color: #f56c6c;
}
::v-deep .el-button--warning.el-button--mini.is-plain {
color: #e6a23c;
}
}
/* 操作类型卡片样式 */
.action-type-cards {
width: 100%;
.card-section {
margin-bottom: 24px;
&:last-child {
margin-bottom: 0;
}
}
.section-title {
font-size: 14px;
font-weight: 600;
color: #303133;
margin-bottom: 12px;
padding-left: 8px;
border-left: 3px solid #409eff;
}
// 分条操作区域的特殊样式
.card-section:first-child .section-title {
border-left-color: #e6a23c;
}
.action-cards-row {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 12px;
}
.action-card {
min-width: 0;
padding: 16px;
border: 2px solid #dcdfe6;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
background: #fff;
display: flex;
align-items: center;
gap: 12px;
&:hover {
border-color: #409eff;
box-shadow: 0 2px 12px 0 rgba(64, 158, 255, 0.15);
transform: translateY(-2px);
}
&.active {
border-color: #409eff;
background: linear-gradient(135deg, #e3f2fd 0%, #f0f7ff 100%);
box-shadow: 0 2px 12px 0 rgba(64, 158, 255, 0.3);
.card-icon {
background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%);
color: #fff;
}
.card-title {
color: #409eff;
font-weight: 600;
}
}
&.split-card {
&.active {
background: linear-gradient(135deg, #fff3e0 0%, #fff8f0 100%);
border-color: #e6a23c;
.card-icon {
background: linear-gradient(135deg, #e6a23c 0%, #f0ad4e 100%);
}
.card-title {
color: #e6a23c;
}
}
&:hover {
border-color: #e6a23c;
box-shadow: 0 2px 12px 0 rgba(230, 162, 60, 0.15);
}
}
}
.card-icon {
width: 48px;
height: 48px;
border-radius: 8px;
background: #f5f7fa;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
color: #909399;
transition: all 0.3s ease;
flex-shrink: 0;
}
.card-content {
flex: 1;
}
.card-title {
font-size: 16px;
font-weight: 500;
color: #303133;
margin-bottom: 4px;
transition: all 0.3s ease;
}
.card-desc {
font-size: 13px;
color: #909399;
line-height: 1.4;
}
}
</style>