Compare commits
33 Commits
9bba1a4dad
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b93d636c8a | ||
| 0e1599809d | |||
| 3953399d90 | |||
|
|
04c98c664b | ||
|
|
9fab0ade4d | ||
| cafa0a9a6c | |||
|
|
3afed26d7f | ||
|
|
a23a4645dc | ||
|
|
0902be0135 | ||
|
|
ac55c9a4d1 | ||
|
|
2f47047ae6 | ||
| 8e3a26448a | |||
| eed35addb5 | |||
| 3c4e60bc49 | |||
|
|
166afcb959 | ||
|
|
dd94606e86 | ||
|
|
5bae1f405b | ||
|
|
32a2210a5f | ||
|
|
a5b36e2f4c | ||
| 0325df38ec | |||
| 67c742acee | |||
|
|
325041ada6 | ||
|
|
983bb0a172 | ||
|
|
3916546c50 | ||
|
|
1c74f99766 | ||
| 8f535018e5 | |||
|
|
cd491678d9 | ||
|
|
94e67c73b6 | ||
|
|
1cfd157c88 | ||
| babf87ffd0 | |||
|
|
8ff6390613 | ||
| 8c58a93e67 | |||
| f65a4439ae |
@@ -6,7 +6,8 @@
|
||||
export default {
|
||||
onLaunch: function() {
|
||||
this.initApp()
|
||||
updateManager.checkUpdate();
|
||||
updateManager.checkUpdate();
|
||||
plus.navigator.closeSplashscreen()
|
||||
},
|
||||
methods: {
|
||||
// 初始化应用
|
||||
@@ -17,14 +18,7 @@
|
||||
//#ifdef H5
|
||||
this.checkLogin()
|
||||
//#endif
|
||||
// uni.hideTabBar()
|
||||
},
|
||||
// mounted() {
|
||||
// uni.hideTabBar()
|
||||
// },
|
||||
// onShow() {
|
||||
// uni.hideTabBar()
|
||||
// },
|
||||
initConfig() {
|
||||
this.globalData.config = config
|
||||
},
|
||||
|
||||
18
apps/hand-factory/api/business/dashboard.js
Normal file
18
apps/hand-factory/api/business/dashboard.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import zinc1Request from '@/utils/zinc1Request'
|
||||
|
||||
// 获取当前生产中的计划信息
|
||||
export function getCurrentPlan() {
|
||||
return zinc1Request({
|
||||
url: '/api/business/dashboard/currentPlan',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取当前生产卷的关键工艺参数
|
||||
export function getCurrentProcess() {
|
||||
return zinc1Request({
|
||||
url: '/api/business/dashboard/currentProcess',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
20
apps/hand-factory/api/business/report.js
Normal file
20
apps/hand-factory/api/business/report.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import zinc1Request from '@/utils/zinc1Request'
|
||||
|
||||
// 生产实绩汇总
|
||||
export function getReportSummary(params) {
|
||||
return zinc1Request({
|
||||
url: '/api/report/summary',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 生产实绩明细
|
||||
export function getReportDetails(params) {
|
||||
return zinc1Request({
|
||||
url: '/api/report/details',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
11
apps/hand-factory/api/business/stoppage.js
Normal file
11
apps/hand-factory/api/business/stoppage.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import zinc1Request from '@/utils/zinc1Request'
|
||||
|
||||
// 停机记录列表
|
||||
export function listStoppage(data) {
|
||||
return zinc1Request({
|
||||
url: '/api/stoppage/list',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import request from '@/utils/request'
|
||||
import errorCode from '@/utils/errorCode'
|
||||
import { toast, tansParams } from '@/utils/common'
|
||||
|
||||
// 登录方法
|
||||
export function login(username, password, code, uuid) {
|
||||
@@ -18,6 +20,50 @@ export function login(username, password, code, uuid) {
|
||||
})
|
||||
}
|
||||
|
||||
// Zinc1系统登录方法(直接使用uni.request,因为登录接口不需要token,完全静默处理)
|
||||
export function loginZinc1(username, password, code, uuid) {
|
||||
const data = {
|
||||
username,
|
||||
password,
|
||||
code,
|
||||
uuid
|
||||
}
|
||||
|
||||
const baseUrl = 'http://140.143.206.120:10082/prod-api'
|
||||
const timeout = 10000
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
method: 'post',
|
||||
timeout: timeout,
|
||||
url: baseUrl + '/login',
|
||||
data: data,
|
||||
header: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
dataType: 'json'
|
||||
}).then(response => {
|
||||
let [error, res] = response
|
||||
if (error) {
|
||||
// 静默失败,不显示任何提示
|
||||
reject('Zinc1系统连接异常')
|
||||
return
|
||||
}
|
||||
const code = res.data.code || 200
|
||||
if (code === 200 && res.data && res.data.token) {
|
||||
// 只有成功时才resolve
|
||||
resolve(res.data)
|
||||
} else {
|
||||
// 其他情况静默失败
|
||||
reject('Zinc1登录失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
// 静默失败,不显示任何提示
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 注册方法
|
||||
export function register(data) {
|
||||
return request({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import request from '@/utils/request'
|
||||
import zinc1Request from '@/utils/zinc1Request'
|
||||
|
||||
export function listDeviceEnumAll() {
|
||||
return request({
|
||||
return zinc1Request({
|
||||
url: '/api/deviceEnum/all',
|
||||
method: 'get'
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import request from '@/utils/request'
|
||||
import zinc1Request from '@/utils/zinc1Request'
|
||||
|
||||
export function getDeviceFieldMetaAll() {
|
||||
return request({
|
||||
return zinc1Request({
|
||||
url: '/api/deviceFieldMeta/all',
|
||||
method: 'get'
|
||||
})
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import request from '@/utils/request'
|
||||
import zinc1Request from '@/utils/zinc1Request'
|
||||
|
||||
// 获取最新N条设备快照
|
||||
export function listDeviceSnapshotLatest(params) {
|
||||
return request({
|
||||
return zinc1Request({
|
||||
url: '/api/deviceSnapshot/latest',
|
||||
method: 'get',
|
||||
params
|
||||
@@ -11,7 +11,7 @@ export function listDeviceSnapshotLatest(params) {
|
||||
|
||||
// 按时间范围查询设备快照
|
||||
export function listDeviceSnapshotRange(params) {
|
||||
return request({
|
||||
return zinc1Request({
|
||||
url: '/api/deviceSnapshot/range',
|
||||
method: 'get',
|
||||
params
|
||||
|
||||
73
apps/hand-factory/api/system/config.js
Normal file
73
apps/hand-factory/api/system/config.js
Normal file
@@ -0,0 +1,73 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询参数列表
|
||||
export function listConfig(query) {
|
||||
return request({
|
||||
url: '/system/config/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询参数详细
|
||||
export function getConfig(configId) {
|
||||
return request({
|
||||
url: '/system/config/' + configId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据参数键名查询参数值
|
||||
export function getConfigKey(configKey) {
|
||||
return request({
|
||||
url: '/system/config/configKey/' + configKey,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增参数配置
|
||||
export function addConfig(data) {
|
||||
return request({
|
||||
url: '/system/config',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改参数配置
|
||||
export function updateConfig(data) {
|
||||
return request({
|
||||
url: '/system/config',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改参数配置
|
||||
export function updateConfigByKey(key, value) {
|
||||
return request({
|
||||
url: '/system/config/updateByKey',
|
||||
method: 'put',
|
||||
data: {
|
||||
configKey: key,
|
||||
configValue: value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 删除参数配置
|
||||
export function delConfig(configId) {
|
||||
return request({
|
||||
url: '/system/config/' + configId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 刷新参数缓存
|
||||
export function refreshCache() {
|
||||
return request({
|
||||
url: '/system/config/refreshCache',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -42,3 +42,26 @@ export function delActualWarehouse(actualWarehouseId) {
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取两级的树结构
|
||||
export function treeActualWarehouseTwoLevel(query) {
|
||||
return request({
|
||||
url: '/wms/actualWarehouse/levelTwo',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制释放库位
|
||||
*/
|
||||
export function forceReleaseLocation(actualWarehouseId) {
|
||||
if (!actualWarehouseId) {
|
||||
throw new Error('actualWarehouseId is required');
|
||||
}
|
||||
return request({
|
||||
url: '/wms/actualWarehouse/release/' + actualWarehouseId,
|
||||
method: 'put',
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -75,3 +75,11 @@ export function exportCoil(coilId) {
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 撤回钢卷发货
|
||||
export function cancelExportCoil(coilId) {
|
||||
return request({
|
||||
url: '/wms/materialCoil/withdrawExportCoil/' + coilId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
52
apps/hand-factory/api/wms/deliveryWaybill.js
Normal file
52
apps/hand-factory/api/wms/deliveryWaybill.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询发货单主列表
|
||||
export function listDeliveryWaybill(query) {
|
||||
return request({
|
||||
url: '/wms/deliveryWaybill/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询发货单主详细
|
||||
export function getDeliveryWaybill(waybillId) {
|
||||
return request({
|
||||
url: '/wms/deliveryWaybill/' + waybillId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增发货单主
|
||||
export function addDeliveryWaybill(data) {
|
||||
return request({
|
||||
url: '/wms/deliveryWaybill',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改发货单主
|
||||
export function updateDeliveryWaybill(data) {
|
||||
return request({
|
||||
url: '/wms/deliveryWaybill',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除发货单主
|
||||
export function delDeliveryWaybill(waybillId) {
|
||||
return request({
|
||||
url: '/wms/deliveryWaybill/' + waybillId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export function updateDeliveryWaybillStatus(data) {
|
||||
return request({
|
||||
url: '/wms/deliveryWaybill/status',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
62
apps/hand-factory/api/wms/deliveryWaybillDetail.js
Normal file
62
apps/hand-factory/api/wms/deliveryWaybillDetail.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询发货单明细列表
|
||||
export function listDeliveryWaybillDetail(query) {
|
||||
return request({
|
||||
url: '/wms/deliveryWaybillDetail/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询发货单明细详细
|
||||
export function getDeliveryWaybillDetail(detailId) {
|
||||
return request({
|
||||
url: '/wms/deliveryWaybillDetail/' + detailId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增发货单明细
|
||||
export function addDeliveryWaybillDetail(data) {
|
||||
return request({
|
||||
url: '/wms/deliveryWaybillDetail',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改发货单明细
|
||||
export function updateDeliveryWaybillDetail(data) {
|
||||
return request({
|
||||
url: '/wms/deliveryWaybillDetail',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除发货单明细
|
||||
export function delDeliveryWaybillDetail(detailId) {
|
||||
return request({
|
||||
url: '/wms/deliveryWaybillDetail/' + detailId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 批量新增发货单明细
|
||||
export function batchAddDeliveryWaybillDetail(data) {
|
||||
return request({
|
||||
url: '/wms/deliveryWaybillDetail/batch',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询已绑定发货的钢卷列表
|
||||
export function listBoundCoil(query) {
|
||||
return request({
|
||||
url: '/wms/deliveryWaybillDetail/boundCoilList',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
72
apps/hand-factory/api/wms/mealReport.js
Normal file
72
apps/hand-factory/api/wms/mealReport.js
Normal file
@@ -0,0 +1,72 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询部门报餐主列表
|
||||
export function listMealReport(query) {
|
||||
return request({
|
||||
url: '/wms/mealReport/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询部门报餐主详细
|
||||
export function getMealReport(reportId) {
|
||||
return request({
|
||||
url: '/wms/mealReport/' + reportId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增部门报餐主
|
||||
export function addMealReport(data) {
|
||||
return request({
|
||||
url: '/wms/mealReport',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改部门报餐主
|
||||
export function updateMealReport(data) {
|
||||
return request({
|
||||
url: '/wms/mealReport',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除部门报餐主
|
||||
export function delMealReport(reportId) {
|
||||
return request({
|
||||
url: '/wms/mealReport/' + reportId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 报表相关接口
|
||||
// 按餐别统计
|
||||
export function getMealTypeCount(query) {
|
||||
return request({
|
||||
url: '/wms/mealReport/report/mealType',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 按部门统计
|
||||
export function getDeptCount(query) {
|
||||
return request({
|
||||
url: '/wms/mealReport/report/dept',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 按日期统计
|
||||
export function getDateCount(query) {
|
||||
return request({
|
||||
url: '/wms/mealReport/report/date',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,23 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
function parseDate(date) {
|
||||
// 修复1:参数名和内部变量名冲突,改用tempDate
|
||||
// 修复2:如果传入的date为空/无效,默认使用当前时间
|
||||
const tempDate = date ? new Date(date) : new Date();
|
||||
|
||||
// 获取年、月、日、时、分、秒(补零处理,确保是两位数)
|
||||
const year = tempDate.getFullYear();
|
||||
// 月份从0开始,所以要+1,不足两位补0
|
||||
const month = String(tempDate.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(tempDate.getDate()).padStart(2, '0');
|
||||
const hours = String(tempDate.getHours()).padStart(2, '0');
|
||||
const minutes = String(tempDate.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(tempDate.getSeconds()).padStart(2, '0');
|
||||
|
||||
// 格式化为YYYY-mm-dd HH:mm:ss并返回
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
// 查询钢卷待操作列表
|
||||
export function listPendingAction(query) {
|
||||
return request({
|
||||
@@ -19,19 +37,33 @@ export function getPendingAction(actionId) {
|
||||
|
||||
// 新增钢卷待操作
|
||||
export function addPendingAction(data) {
|
||||
const payload = { ...data }
|
||||
if (payload.processTime) {
|
||||
payload.processTime = parseDate(payload.processTime)
|
||||
}
|
||||
if (payload.completeTime) {
|
||||
payload.completeTime = parseDate(payload.completeTime)
|
||||
}
|
||||
return request({
|
||||
url: '/wms/coilPendingAction',
|
||||
method: 'post',
|
||||
data: data
|
||||
data: payload
|
||||
})
|
||||
}
|
||||
|
||||
// 修改钢卷待操作
|
||||
export function updatePendingAction(data) {
|
||||
const payload = { ...data }
|
||||
if (payload.processTime) {
|
||||
payload.processTime = parseDate(payload.processTime)
|
||||
}
|
||||
if (payload.completeTime) {
|
||||
payload.completeTime = parseDate(payload.completeTime)
|
||||
}
|
||||
return request({
|
||||
url: '/wms/coilPendingAction',
|
||||
method: 'put',
|
||||
data: data
|
||||
data: payload
|
||||
})
|
||||
}
|
||||
|
||||
@@ -75,3 +107,12 @@ export function cancelAction(actionId) {
|
||||
})
|
||||
}
|
||||
|
||||
// 导出钢卷待操作
|
||||
export function exportPendingAction(query) {
|
||||
return request({
|
||||
url: '/wms/coilPendingAction/export',
|
||||
method: 'post',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,402 @@
|
||||
<template>
|
||||
<view class="klp-actual-warehouse-picker">
|
||||
<!-- 输入框触发器 - 全部修复:阻止冒泡+固定右侧按钮+文字颜色差异化 -->
|
||||
<view class="picker-input" @click="openPopup">
|
||||
<!-- 文本区域:未选择=浅灰色(占位色) 选中=深灰色(内容色) -->
|
||||
<span class="picker-text" :class="{selected: selectedNode.actualWarehouseCode}">
|
||||
{{ selectedNode.actualWarehouseCode || '请选择真实库区' }}
|
||||
</span>
|
||||
<!-- ✅ 终极防冒泡:原生view包裹图标 + stop.prevent 双重阻断 + 绝对定位固定右侧 -->
|
||||
<view class="clear-btn-wrap" v-show="selectedNode.actualWarehouseCode" @click.stop.prevent="handleClear">
|
||||
<uni-icons type="clear" size="24" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- uni-popup 原生组件 纯属性使用 无任何样式 -->
|
||||
<uni-popup ref="popup" type="bottom" background-color="#fff" :mask-click="true" @mask-click="closePopup">
|
||||
<!-- ✅ 弹窗内部根容器 承载所有样式 固定高度核心 -->
|
||||
<view class="popup-inner-container">
|
||||
<!-- 弹窗头部标题 -->
|
||||
<view class="popup-header">选择实际库区</view>
|
||||
|
||||
<!-- 第一层横向滚动tab -->
|
||||
<scroll-view scroll-x class="tab-scroll-view" scroll-with-animation>
|
||||
<view class="tab-item-wrap">
|
||||
<view class="tab-item" :class="{active: activeFirstId === item.actualWarehouseId}"
|
||||
@click="handleFirstTabClick(item)" v-for="item in treeData" :key="item.actualWarehouseId">
|
||||
{{ item.actualWarehouseName }}
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 第二层横向滚动tab,点选第一层tab后才出现 -->
|
||||
<scroll-view v-if="activeFirstId" scroll-x class="tab-scroll-view tab-second" scroll-with-animation>
|
||||
<view class="tab-item-wrap">
|
||||
<view class="tab-item" :class="{active: activeSecondId === item.actualWarehouseId}"
|
||||
@click="handleSecondTabClick(item)" v-for="item in secondLevelList" :key="item.actualWarehouseId">
|
||||
{{ item.actualWarehouseName }}
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- ✅✅✅ 核心修改:筛选框抽离出来 独立节点 固定置顶 永不滚动 -->
|
||||
<view class="search-box">
|
||||
<uni-icons type="search" size="24" color="#999"></uni-icons>
|
||||
<input type="text" v-model="searchKeyword" placeholder="输入编码搜索仓库" class="search-input"
|
||||
placeholder-class="search-placeholder" />
|
||||
<uni-icons v-if="searchKeyword" type="clear" size="24" color="#999" class="clear-icon"
|
||||
@click="searchKeyword = ''"></uni-icons>
|
||||
</view>
|
||||
|
||||
<!-- ✅✅✅ 仅这个区域滚动:纯数据列表区 -->
|
||||
<view class="select-scroll-content">
|
||||
<!-- 加载中状态 -->
|
||||
<view class="loading" v-if="loading">加载中...</view>
|
||||
|
||||
<!-- 情况1:只选中第一层 → 展示筛选后的二级数据 -->
|
||||
<view v-else-if="activeFirstId && !activeSecondId" class="select-list">
|
||||
<view class="select-item" @click="handleSelectSecondItem(item)" v-for="item in filterSecondList"
|
||||
:key="item.actualWarehouseId">
|
||||
{{ item.name }}
|
||||
<text class="code">{{ item.actualWarehouseCode }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 情况2:选中第二层 → 展示筛选后的三级数据 -->
|
||||
<view v-else-if="activeSecondId" class="select-list">
|
||||
<view class="select-item" @click="handleSelectThirdItem(item)" v-for="item in filterThirdList"
|
||||
:key="item.actualWarehouseId">
|
||||
{{ item.name }}
|
||||
<text class="code">{{ item.actualWarehouseCode }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空数据展示 -->
|
||||
<view class="empty" v-else-if="showEmpty">暂无仓库数据</view>
|
||||
<!-- 筛选后无数据的兜底提示 -->
|
||||
<view class="empty"
|
||||
v-else-if="searchKeyword && (filterSecondList.length === 0 && filterThirdList.length === 0)">暂无匹配的仓库编码
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
treeActualWarehouseTwoLevel,
|
||||
listActualWarehouse
|
||||
} from '@/api/wms/actualWarehouse.js'
|
||||
|
||||
export default {
|
||||
name: "klp-actual-warehouse-picker",
|
||||
props: {
|
||||
showEmpty: {
|
||||
default: false,
|
||||
type: Boolean
|
||||
},
|
||||
value: {
|
||||
default: undefined,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
treeData: [],
|
||||
secondLevelList: [],
|
||||
thirdLevelList: [],
|
||||
selectedNode: {},
|
||||
activeFirstId: '',
|
||||
activeSecondId: '',
|
||||
loading: false,
|
||||
searchKeyword: '' // 筛选关键词
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 完美双向绑定
|
||||
innerValue: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('input', val);
|
||||
this.$emit('change', this.selectedNode);
|
||||
}
|
||||
},
|
||||
// ✅ 精准匹配:只做 actualWarehouseCode 仓库编码的模糊筛选(纯编码匹配,无名称)
|
||||
filterSecondList() {
|
||||
if (!this.searchKeyword) return this.secondLevelList
|
||||
const keyword = this.searchKeyword.trim().toLowerCase()
|
||||
return this.secondLevelList.filter(item => {
|
||||
const code = item.actualWarehouseCode?.toLowerCase() || ''
|
||||
return code.includes(keyword)
|
||||
})
|
||||
},
|
||||
// ✅ 精准匹配:只做 actualWarehouseCode 仓库编码的模糊筛选(纯编码匹配,无名称)
|
||||
filterThirdList() {
|
||||
if (!this.searchKeyword) return this.thirdLevelList
|
||||
const keyword = this.searchKeyword.trim().toLowerCase()
|
||||
return this.thirdLevelList.filter(item => {
|
||||
const code = item.actualWarehouseCode?.toLowerCase() || ''
|
||||
return code.includes(keyword)
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getTwoLevelData()
|
||||
},
|
||||
methods: {
|
||||
async getTwoLevelData() {
|
||||
try {
|
||||
this.loading = true
|
||||
const res = await treeActualWarehouseTwoLevel()
|
||||
this.treeData = res.data || []
|
||||
} catch (err) {
|
||||
uni.showToast({
|
||||
title: '加载仓库数据失败',
|
||||
icon: 'none'
|
||||
})
|
||||
console.error('仓库数据加载异常:', err)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
// ✅ 新增:清空选中的仓库数据 + 重置所有状态
|
||||
handleClear() {
|
||||
// 1. 清空选中的仓库节点数据
|
||||
this.selectedNode = {}
|
||||
// 2. 双向绑定同步清空,父组件v-model能拿到空值
|
||||
this.innerValue = undefined
|
||||
// 3. 重置一级/二级选中的tab id
|
||||
this.activeFirstId = ''
|
||||
this.activeSecondId = ''
|
||||
// 4. 清空二级、三级仓库列表数据
|
||||
this.secondLevelList = []
|
||||
this.thirdLevelList = []
|
||||
// 5. 清空搜索关键词
|
||||
this.searchKeyword = ''
|
||||
// 可选:清空成功的轻提示,和你原有提示风格一致
|
||||
uni.showToast({
|
||||
title: '已清空选择',
|
||||
icon: 'none',
|
||||
duration: 1200
|
||||
})
|
||||
},
|
||||
openPopup() {
|
||||
this.$refs.popup.open()
|
||||
},
|
||||
closePopup() {
|
||||
this.$refs.popup.close()
|
||||
this.searchKeyword = ''
|
||||
},
|
||||
handleFirstTabClick(item) {
|
||||
this.activeFirstId = item.actualWarehouseId
|
||||
this.activeSecondId = ''
|
||||
this.thirdLevelList = []
|
||||
this.searchKeyword = ''
|
||||
this.secondLevelList = item.children || []
|
||||
},
|
||||
async handleSecondTabClick(item) {
|
||||
this.activeSecondId = item.actualWarehouseId
|
||||
this.thirdLevelList = []
|
||||
this.searchKeyword = ''
|
||||
try {
|
||||
this.loading = true
|
||||
const res = await listActualWarehouse({
|
||||
parentId: item.actualWarehouseId
|
||||
})
|
||||
this.thirdLevelList = res.data || []
|
||||
} catch (err) {
|
||||
uni.showToast({
|
||||
title: '加载库区数据失败',
|
||||
icon: 'none'
|
||||
})
|
||||
console.error('三级仓库加载异常:', err)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
handleSelectSecondItem(item) {
|
||||
this.selectedNode = item
|
||||
this.innerValue = item.actualWarehouseId
|
||||
this.closePopup()
|
||||
},
|
||||
handleSelectThirdItem(item) {
|
||||
this.selectedNode = item
|
||||
this.innerValue = item.actualWarehouseId
|
||||
this.closePopup()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.klp-actual-warehouse-picker {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// 输入框触发器样式 - 全部优化:父容器相对定位 + 文本颜色区分 + 按钮绝对定位固定右侧
|
||||
.picker-input {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
padding: 0 30rpx;
|
||||
// ✅ 给右侧清除按钮预留空间,防止文本和按钮重叠
|
||||
padding-right: 70rpx;
|
||||
border: 1px solid #e5e5e5;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 12rpx;
|
||||
font-size: 28rpx;
|
||||
box-sizing: border-box;
|
||||
// ✅ 关键:父容器相对定位,为子元素绝对定位做铺垫
|
||||
position: relative;
|
||||
|
||||
// ✅ 文本颜色差异化核心样式
|
||||
.picker-text {
|
||||
// 未选择仓库 - 占位文本颜色(浅灰色,placeholder色)
|
||||
color: #999;
|
||||
&.selected {
|
||||
// 选中仓库后 - 内容文本颜色(深灰色,主色)
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 清除按钮:绝对定位 死死固定在输入框最右侧 永不偏移
|
||||
.clear-btn-wrap {
|
||||
position: absolute;
|
||||
right: 20rpx; // 距离右侧固定间距
|
||||
top: 50%;
|
||||
transform: translateY(-50%); // 垂直居中完美对齐
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
// 按钮点击按压反馈,和你原有风格一致
|
||||
&:active {
|
||||
background-color: #e5e5e5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 弹窗内部根容器 - 固定高度核心 样式100%生效
|
||||
.popup-inner-container {
|
||||
width: 100%;
|
||||
height: 80vh; // 弹窗固定高度,适配所有手机
|
||||
// max-height: 900rpx; // 大屏高度上限,防止过高
|
||||
overflow: hidden; // 禁止整体滚动
|
||||
display: flex;
|
||||
flex-direction: column; // 垂直布局核心
|
||||
}
|
||||
|
||||
// 弹窗头部标题 - 固定不滚动
|
||||
.popup-header {
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
flex-shrink: 0; // 固定高度 不被压缩
|
||||
}
|
||||
|
||||
// 横向滚动tab容器 - 固定不滚动
|
||||
.tab-scroll-view {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
flex-shrink: 0; // 固定高度 不被压缩
|
||||
|
||||
.tab-item-wrap {
|
||||
display: inline-block;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
display: inline-block;
|
||||
padding: 12rpx 30rpx;
|
||||
margin: 0 10rpx;
|
||||
font-size: 28rpx;
|
||||
border-radius: 20rpx;
|
||||
color: #666;
|
||||
|
||||
&.active {
|
||||
background: #007aff;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 二级tab区分隔线
|
||||
.tab-second {
|
||||
border-top: 1px solid #f5f5f5;
|
||||
}
|
||||
|
||||
// ✅✅✅ 筛选框样式 - 固定置顶 永不滚动 核心样式
|
||||
.search-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16rpx 30rpx;
|
||||
margin: 10rpx 20rpx 0;
|
||||
background: #f8f8f8;
|
||||
border-radius: 30rpx;
|
||||
flex-shrink: 0; // 关键:固定高度 不参与滚动 不被压缩
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
height: 50rpx;
|
||||
line-height: 50rpx;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
margin: 0 10rpx;
|
||||
}
|
||||
|
||||
.search-placeholder {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.clear-icon {
|
||||
padding: 0 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// ✅✅✅ 唯一滚动区域:纯数据列表区
|
||||
.select-scroll-content {
|
||||
flex: 1; // 占满弹窗剩余所有高度
|
||||
overflow-y: auto; // 仅此处滚动 核心!
|
||||
padding: 10rpx 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
.select-list {
|
||||
.select-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
font-size: 40rpx;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
|
||||
&:active {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.code {
|
||||
color: #000;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loading,
|
||||
.empty {
|
||||
text-align: center;
|
||||
padding: 50rpx 0;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -37,10 +37,10 @@ export default {
|
||||
// 产线列表(原Tab列表改造,可外部传入,此处保留默认值)
|
||||
lineList: [
|
||||
{ name: "科伦普重工-酸轧机组", key: "acidity" },
|
||||
{ name: "科伦普重工-彩涂机组", key: "paint" },
|
||||
{ name: "科伦普重工-镀锌线一组", key: "zinc1" },
|
||||
{ name: "科伦普重工-镀锌线二组", key: "zinc2" },
|
||||
{ name: "科伦普重工-镀锌线三组", key: "zinc3" },
|
||||
// { name: "科伦普重工-彩涂机组", key: "paint" },
|
||||
{ name: "科伦普重工-镀锌机组", key: "zinc1" },
|
||||
// { name: "科伦普重工-镀锌线二组", key: "zinc2" },
|
||||
// { name: "科伦普重工-镀锌线三组", key: "zinc3" },
|
||||
],
|
||||
// 当前选中的产线索引(关联picker)
|
||||
currentLineIndex: 0,
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
<text class="picker-text" :class="{ 'picker-placeholder': !itemType }">
|
||||
{{ itemType === 'product' ? '成品' : itemType === 'raw_material' ? '原料' : '请选择物品类型' }}
|
||||
</text>
|
||||
<text class="picker-arrow" v-if="!disabled">▼</text>
|
||||
<!-- ✅ 互斥显示:选中类型=清除按钮 未选中=下拉箭头 -->
|
||||
<!-- <text class="picker-clear" v-if="itemType && !disabled" @click.stop.prevent="handleClearItemType">✕</text> -->
|
||||
<text class="picker-arrow">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -18,7 +20,9 @@
|
||||
<text class="picker-text" :class="{ 'picker-placeholder': !selectedName }">
|
||||
{{ loadingProducts ? '加载中...' : selectedName || '请选择产品' }}
|
||||
</text>
|
||||
<text class="picker-arrow" v-if="!disabled && !loadingProducts">▼</text>
|
||||
<!-- ✅ 互斥显示:选中产品=清除按钮 未选中/加载中=下拉箭头 -->
|
||||
<text class="picker-clear" v-if="selectedName && !disabled && !loadingProducts" @click.stop.prevent="handleClearProduct">✕</text>
|
||||
<text class="picker-arrow" v-else-if="!disabled && !loadingProducts">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -29,7 +33,9 @@
|
||||
<text class="picker-text" :class="{ 'picker-placeholder': !selectedName }">
|
||||
{{ loadingRawMaterials ? '加载中...' : selectedName || '请选择原材料' }}
|
||||
</text>
|
||||
<text class="picker-arrow" v-if="!disabled && !loadingRawMaterials">▼</text>
|
||||
<!-- ✅ 互斥显示:选中原料=清除按钮 未选中/加载中=下拉箭头 -->
|
||||
<text class="picker-clear" v-if="selectedName && !disabled && !loadingRawMaterials" @click.stop.prevent="handleClearRawMaterial">✕</text>
|
||||
<text class="picker-arrow" v-else-if="!disabled && !loadingRawMaterials">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -259,7 +265,6 @@
|
||||
const keyword = this.rawMaterialSearchKeyword.trim().toLowerCase();
|
||||
this.filteredRawMaterials = keyword
|
||||
? this.rawMaterials.filter(m => {
|
||||
console.log(m.specification)
|
||||
// 只基于原材料名称过滤,且确保是原材料数据
|
||||
return (m.rawMaterialName && m.rawMaterialName.toLowerCase().includes(keyword))
|
||||
|| m?.specification?.toLowerCase()?.includes(keyword)
|
||||
@@ -324,13 +329,36 @@
|
||||
},
|
||||
closeRawMaterialPicker() {
|
||||
this.$refs.rawMaterialPopup.close();
|
||||
},
|
||||
|
||||
// ✅ 新增核心清除方法1:清空物品类型 + 所有关联数据(完全重置)
|
||||
handleClearItemType() {
|
||||
this.$emit('update:itemType', '');
|
||||
this.$emit('update:itemId', undefined);
|
||||
this.$emit('update:materialType', undefined);
|
||||
this.selectedName = '';
|
||||
uni.showToast({ title: '已清空物品类型', icon: 'none' });
|
||||
},
|
||||
|
||||
// ✅ 新增核心清除方法2:仅清空选中的产品,保留物品类型为成品
|
||||
handleClearProduct() {
|
||||
this.$emit('update:itemId', undefined);
|
||||
this.selectedName = '';
|
||||
uni.showToast({ title: '已清空产品选择', icon: 'none' });
|
||||
},
|
||||
|
||||
// ✅ 新增核心清除方法3:仅清空选中的原材料,保留物品类型为原料
|
||||
handleClearRawMaterial() {
|
||||
this.$emit('update:itemId', undefined);
|
||||
this.selectedName = '';
|
||||
uni.showToast({ title: '已清空原材料选择', icon: 'none' });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* 样式保持不变 */
|
||||
/* 样式保持不变 + 新增清除按钮样式 */
|
||||
.form-item-optional {
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
@@ -347,6 +375,7 @@
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
padding: 0 24rpx;
|
||||
padding-right: 60rpx; // ✅ 新增:给清除按钮/箭头预留空间,防止文本遮挡
|
||||
background: #f8f9fa;
|
||||
border: 2rpx solid #e8e8e8;
|
||||
border-radius: 12rpx;
|
||||
@@ -382,6 +411,23 @@
|
||||
color: #999;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
// ✅ 新增:清除按钮样式,与箭头风格统一,点击有按压反馈
|
||||
.picker-clear {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 8rpx;
|
||||
border-radius: 50%;
|
||||
&:active {
|
||||
background-color: #e8e8e8;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.warehouse-popup {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- 选择器触发按钮 -->
|
||||
<!-- 选择器触发按钮 - 核心互斥:未选中=箭头 / 选中=清除按钮,二选一 -->
|
||||
<view
|
||||
class="picker-input"
|
||||
@click="handleOpen"
|
||||
@@ -9,15 +9,21 @@
|
||||
<text class="picker-text" :class="{ 'picker-placeholder': !selectedName }">
|
||||
{{ selectedName || placeholder }}
|
||||
</text>
|
||||
<text class="picker-arrow" v-if="!disabled">▼</text>
|
||||
<!-- ✅ 互斥核心:选中库区 → 清除按钮 | 未选中 → 下拉箭头,永不共存 -->
|
||||
<text
|
||||
class="picker-clear"
|
||||
v-if="selectedName && !disabled"
|
||||
@click.stop.prevent="handleClear"
|
||||
>✕</text>
|
||||
<text class="picker-arrow" v-else-if="!disabled">▼</text>
|
||||
</view>
|
||||
|
||||
<!-- 弹窗内容 -->
|
||||
<!-- 弹窗内容 - 固定逻辑库区标题,无任何真实库区相关内容 -->
|
||||
<uni-popup ref="popup" type="bottom" @close="handlePopupClose">
|
||||
<view class="warehouse-popup">
|
||||
<!-- 弹窗头部 -->
|
||||
<!-- 弹窗头部 - 固定标题:选择逻辑库区 -->
|
||||
<view class="popup-header">
|
||||
<text class="popup-title">{{ title || (wareType === 'virtual' ? '选择逻辑库区' : '选择真实库区') }}</text>
|
||||
<text class="popup-title">选择逻辑库区</text>
|
||||
<text class="popup-close" @click="handleClose">✕</text>
|
||||
</view>
|
||||
|
||||
@@ -42,11 +48,11 @@
|
||||
<view
|
||||
class="warehouse-item"
|
||||
v-for="item in filteredList"
|
||||
:key="getItemId(item)"
|
||||
:key="item.warehouseId"
|
||||
@click="handleSelect(item)"
|
||||
>
|
||||
<text class="warehouse-name">{{ getItemName(item) }}</text>
|
||||
<text class="warehouse-check" v-if="getItemId(item) === selectedId">✓</text>
|
||||
<text class="warehouse-name">{{ item.warehouseName }}</text>
|
||||
<text class="warehouse-check" v-if="item.warehouseId === selectedId">✓</text>
|
||||
</view>
|
||||
<view class="empty-tip" v-if="filteredList.length === 0">
|
||||
<text>未找到匹配的库区</text>
|
||||
@@ -65,13 +71,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// ✅ 只保留逻辑库区接口,彻底删除真实库区接口引入
|
||||
import { listWarehouse } from '@/api/wms/warehouse.js'
|
||||
import { listActualWarehouse } from '@/api/wms/actualWarehouse.js'
|
||||
|
||||
export default {
|
||||
name: 'WarehousePicker',
|
||||
props: {
|
||||
// 已选中的库区ID(用于回显)
|
||||
// 已选中的逻辑库区ID(用于回显)
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
@@ -86,25 +92,17 @@ export default {
|
||||
type: String,
|
||||
default: '请选择库区'
|
||||
},
|
||||
// 弹窗标题
|
||||
// 弹窗标题(保留该属性,如需自定义可传,默认固定为选择逻辑库区)
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 库区类型:virtual-逻辑库区,actual-真实库区
|
||||
wareType: {
|
||||
type: String,
|
||||
default: 'virtual',
|
||||
validator: (value) => {
|
||||
return ['virtual', 'actual'].includes(value)
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 弹窗显示状态
|
||||
showPopup: false,
|
||||
// 所有库区列表
|
||||
// 所有逻辑库区列表
|
||||
dataList: [],
|
||||
// 过滤后的库区列表
|
||||
filteredList: [],
|
||||
@@ -137,31 +135,17 @@ export default {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
if (val && this.dataList.length) {
|
||||
const matched = this.dataList.find(item => this.getItemId(item) === val)
|
||||
this.selectedName = matched ? this.getItemName(matched) : ''
|
||||
const matched = this.dataList.find(item => item.warehouseId === val)
|
||||
this.selectedName = matched ? matched.warehouseName : ''
|
||||
}
|
||||
}
|
||||
},
|
||||
// 监听库区类型变化,重新加载数据
|
||||
wareType: {
|
||||
immediate: true,
|
||||
handler() {
|
||||
this.loadWarehouses()
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadWarehouses()
|
||||
},
|
||||
methods: {
|
||||
// 根据类型获取项目ID
|
||||
getItemId(item) {
|
||||
return this.wareType === 'virtual' ? item.warehouseId : item.actualWarehouseId
|
||||
},
|
||||
|
||||
// 根据类型获取项目名称
|
||||
getItemName(item) {
|
||||
return this.wareType === 'virtual' ? item.warehouseName : item.actualWarehouseName
|
||||
},
|
||||
|
||||
// 加载库区列表
|
||||
// 加载逻辑库区列表 ✅ 精简后:只保留逻辑库区接口请求,无任何类型判断
|
||||
async loadWarehouses() {
|
||||
if (this.loading) return
|
||||
|
||||
@@ -170,23 +154,18 @@ export default {
|
||||
this.errorMsg = ''
|
||||
|
||||
try {
|
||||
// 根据类型选择不同的API
|
||||
const res = this.wareType === 'virtual'
|
||||
? await listWarehouse({ pageNum: 1, pageSize: 1000 })
|
||||
: await listActualWarehouse({ pageNum: 1, pageSize: 1000 })
|
||||
|
||||
const res = await listWarehouse({ pageNum: 1, pageSize: 1000 })
|
||||
if (res.code === 200) {
|
||||
this.dataList = res[this.wareType === 'virtual' ? 'data' : 'rows'] || []
|
||||
this.dataList = res.data || []
|
||||
this.filteredList = [...this.dataList]
|
||||
// 初始化选中项名称
|
||||
this.updateSelectedName()
|
||||
} else {
|
||||
throw new Error(res.msg || `${this.wareType === 'virtual' ? '逻辑' : '真实'}库区数据加载失败`)
|
||||
throw new Error(res.msg || '逻辑库区数据加载失败')
|
||||
}
|
||||
} catch (err) {
|
||||
this.error = true
|
||||
this.errorMsg = err.message
|
||||
console.error(`${this.wareType === 'virtual' ? '逻辑' : '真实'}库区加载失败:`, err)
|
||||
console.error('逻辑库区加载失败:', err)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
@@ -196,29 +175,33 @@ export default {
|
||||
updateSelectedName() {
|
||||
if (!this.selectedId) return
|
||||
|
||||
const matched = this.dataList.find(
|
||||
item => this.getItemId(item) === this.selectedId
|
||||
)
|
||||
this.selectedName = matched ? this.getItemName(matched) : ''
|
||||
const matched = this.dataList.find(item => item.warehouseId === this.selectedId)
|
||||
this.selectedName = matched ? matched.warehouseName : ''
|
||||
},
|
||||
|
||||
// 搜索过滤
|
||||
// 搜索过滤:只匹配逻辑库区名称
|
||||
handleSearch() {
|
||||
const keyword = this.searchKeyword.trim().toLowerCase()
|
||||
if (!keyword) {
|
||||
this.filteredList = [...this.dataList]
|
||||
} else {
|
||||
this.filteredList = this.dataList.filter(item =>
|
||||
this.getItemName(item).toLowerCase().includes(keyword)
|
||||
item.warehouseName.toLowerCase().includes(keyword)
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
// 选择库区
|
||||
// 选择逻辑库区:保留禁用库区过滤逻辑
|
||||
handleSelect(item) {
|
||||
this.selectedId = this.getItemId(item)
|
||||
this.selectedName = this.getItemName(item)
|
||||
// 通知父组件选中的完整信息
|
||||
if (item.isEnabled == 0) {
|
||||
uni.showToast({
|
||||
title: '该库区已禁用',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.selectedId = item.warehouseId
|
||||
this.selectedName = item.warehouseName
|
||||
this.$emit('change', item)
|
||||
this.handleClose()
|
||||
},
|
||||
@@ -226,7 +209,6 @@ export default {
|
||||
// 打开弹窗
|
||||
handleOpen() {
|
||||
if (this.disabled) return
|
||||
// 每次打开前重置搜索
|
||||
this.searchKeyword = ''
|
||||
this.filteredList = [...this.dataList]
|
||||
this.showPopup = true
|
||||
@@ -242,17 +224,25 @@ export default {
|
||||
// 弹窗关闭回调
|
||||
handlePopupClose() {
|
||||
this.showPopup = false
|
||||
},
|
||||
|
||||
// ✅ 清空选中的库区数据:清空ID+名称,双向绑定同步,触发空回调
|
||||
handleClear() {
|
||||
this.selectedId = '';
|
||||
this.selectedName = '';
|
||||
this.$emit('change', {});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* 保持原有样式不变 */
|
||||
/* 保持原有样式不变,样式适配互斥显示,无需任何修改 */
|
||||
.picker-input {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
padding: 0 24rpx;
|
||||
padding-right: 60rpx; // 预留按钮/箭头空间,防止文本遮挡
|
||||
background: #f8f9fa;
|
||||
border: 2rpx solid #e8e8e8;
|
||||
border-radius: 12rpx;
|
||||
@@ -283,11 +273,29 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
// 下拉箭头样式
|
||||
.picker-arrow {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
// 清除按钮样式 (和箭头位置一致,样式协调)
|
||||
.picker-clear {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 8rpx;
|
||||
border-radius: 50%;
|
||||
&:active {
|
||||
background-color: #e8e8e8;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.warehouse-popup {
|
||||
@@ -417,7 +425,7 @@ export default {
|
||||
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
padding: 60rpx 0;
|
||||
padding: 60rpx 0;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
@@ -404,7 +404,7 @@ export default {
|
||||
gridColor: "#e4e7ed",
|
||||
showTitle: true,
|
||||
fontSize: 10,
|
||||
data: [{ min: 0, title: "温度(°C)" }]
|
||||
data: [{ min: 0, title: "m/min" }]
|
||||
},
|
||||
extra: {
|
||||
line: {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,13 +2,14 @@
|
||||
module.exports = {
|
||||
// baseUrl: 'http://192.168.31.116:8080',
|
||||
baseUrl: 'http://140.143.206.120:8080',
|
||||
wsUrl: 'ws://140.143.206.120:18081',
|
||||
// baseUrl: 'http://localhost:8080',
|
||||
// 应用信息
|
||||
appInfo: {
|
||||
// 应用名称
|
||||
name: "ruoyi-app",
|
||||
// 应用版本
|
||||
version: "1.3.17",
|
||||
version: "1.3.30",
|
||||
// 应用logo
|
||||
logo: "/static/logo.jpg",
|
||||
// 官方网站
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name" : "科伦普",
|
||||
"appid" : "__UNI__E781B49",
|
||||
"description" : "",
|
||||
"versionName" : "1.3.17",
|
||||
"versionName" : "1.3.28",
|
||||
"versionCode" : 1,
|
||||
"transformPx" : false,
|
||||
"app-plus" : {
|
||||
|
||||
@@ -82,59 +82,25 @@
|
||||
"style": {
|
||||
"navigationBarTitleText": "钢卷收货"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/search/search",
|
||||
"style": {
|
||||
"navigationBarTitleText": "钢卷查询"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/meal/meal",
|
||||
"style": {
|
||||
"navigationBarTitleText": "报餐情况"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/fahuo/fahuo",
|
||||
"style": {
|
||||
"navigationBarTitleText": "发货"
|
||||
}
|
||||
}
|
||||
// {
|
||||
// "path": "pages/register",
|
||||
// "style": {
|
||||
// "navigationBarTitleText": "注册"
|
||||
// }
|
||||
// }, {
|
||||
// "path": "pages/work/index",
|
||||
// "style": {
|
||||
// "navigationBarTitleText": "工作台"
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "path": "pages/mine/avatar/index",
|
||||
// "style": {
|
||||
// "navigationBarTitleText": "修改头像"
|
||||
// }
|
||||
// }, {
|
||||
// "path": "pages/mine/info/edit",
|
||||
// "style": {
|
||||
// "navigationBarTitleText": "编辑资料"
|
||||
// }
|
||||
// }, {
|
||||
// "path": "pages/mine/pwd/index",
|
||||
// "style": {
|
||||
// "navigationBarTitleText": "修改密码"
|
||||
// }
|
||||
// }, {
|
||||
// "path": "pages/mine/setting/index",
|
||||
// "style": {
|
||||
// "navigationBarTitleText": "应用设置"
|
||||
// }
|
||||
// }, {
|
||||
// "path": "pages/mine/help/index",
|
||||
// "style": {
|
||||
// "navigationBarTitleText": "常见问题"
|
||||
// }
|
||||
// }, {
|
||||
// "path": "pages/mine/about/index",
|
||||
// "style": {
|
||||
// "navigationBarTitleText": "关于我们"
|
||||
// }
|
||||
// }, {
|
||||
// "path": "pages/common/webview/index",
|
||||
// "style": {
|
||||
// "navigationBarTitleText": "浏览网页"
|
||||
// }
|
||||
// }, {
|
||||
// "path": "pages/common/textview/index",
|
||||
// "style": {
|
||||
// "navigationBarTitleText": "浏览文本"
|
||||
// }
|
||||
// },
|
||||
],
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "black",
|
||||
@@ -150,23 +116,35 @@
|
||||
"selectedIconPath": "/static/images/tabbar/home_.png",
|
||||
"iconPath": "/static/images/tabbar/home.png"
|
||||
},
|
||||
// {
|
||||
// "text": "扫码",
|
||||
// "pagePath": "pages/code/code",
|
||||
// "selectedIconPath": "/static/images/tabbar/work_.png",
|
||||
// "iconPath": "/static/images/tabbar/work.png"
|
||||
// },
|
||||
{
|
||||
"text": "扫码",
|
||||
"pagePath": "pages/easycode/easycode",
|
||||
"selectedIconPath": "/static/images/tabbar/work_.png",
|
||||
"iconPath": "/static/images/tabbar/work.png"
|
||||
},
|
||||
// {
|
||||
// "text": "收货",
|
||||
// "pagePath": "pages/receive/receive",
|
||||
// "selectedIconPath": "/static/images/tabbar/receive_.png",
|
||||
// "iconPath": "/static/images/tabbar/receive.png"
|
||||
// },
|
||||
// {
|
||||
// "text": "查找",
|
||||
// "pagePath": "pages/search/search",
|
||||
// "selectedIconPath": "/static/images/tabbar/search_.png",
|
||||
// "iconPath": "/static/images/tabbar/search.png"
|
||||
// },
|
||||
{
|
||||
"text": "收货",
|
||||
"pagePath": "pages/receive/receive",
|
||||
"selectedIconPath": "/static/images/tabbar/receive_.png",
|
||||
"iconPath": "/static/images/tabbar/receive.png"
|
||||
"text": "报餐",
|
||||
"pagePath": "pages/meal/meal",
|
||||
"selectedIconPath": "/static/images/tabbar/meal_.png",
|
||||
"iconPath": "/static/images/tabbar/meal.png"
|
||||
},
|
||||
{
|
||||
"text": "发货",
|
||||
"pagePath": "pages/fahuo/fahuo",
|
||||
"selectedIconPath": "/static/images/tabbar/fahuo_.png",
|
||||
"iconPath": "/static/images/tabbar/fahuo.png"
|
||||
},
|
||||
{
|
||||
"text": "我的",
|
||||
@@ -184,4 +162,5 @@
|
||||
"k-(.*)": "@/components/klp-ui/k-$1/k-$1.vue"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,9 +36,11 @@
|
||||
</view>
|
||||
<view class="btn-grid quick-grid">
|
||||
<button class="type-btn ship-btn" @click="handleShip">发货</button>
|
||||
<button class="type-btn cancelship-btn" @click="handleCancelShip">撤回发货</button>
|
||||
<button class="type-btn move-btn" @click="handleTranfer">移库</button>
|
||||
<button class="type-btn see-btn" @click="handleSee">查看存储钢卷</button>
|
||||
<button class="type-btn packing-btn" @click="handlePack">打包</button>
|
||||
<button class="type-btn release-btn" @click="handleRelease">释放库位</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -108,6 +110,73 @@
|
||||
|
||||
<!-- <klp-scaner></klp-scaner> -->
|
||||
<qs-scanlistener ref='pda'></qs-scanlistener>
|
||||
|
||||
<uni-popup ref="shipPopup" type="bottom">
|
||||
<view style="background-color: white; padding: 20rpx;">
|
||||
<view class="info-card" v-if="coilDetail.coilId">
|
||||
<view class="info-grid">
|
||||
<view class="info-item">
|
||||
<text class="item-label">入场钢卷号</text>
|
||||
<text class="item-value">{{ coilDetail.enterCoilNo || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">当前钢卷号</text>
|
||||
<text class="item-value">{{ coilDetail.currentCoilNo || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">逻辑库区</text>
|
||||
<text class="item-value">{{ coilDetail.warehouseName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">实际库区</text>
|
||||
<text class="item-value">{{ coilDetail.actualWarehouseName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">重量</text>
|
||||
<text class="item-value">{{ coilDetail.netWeight || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">厂家</text>
|
||||
<text class="item-value">{{ (coilDetail.manufacturer) || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">材质</text>
|
||||
<text class="item-value">{{ (coilDetail.material) || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">规格</text>
|
||||
<text class="item-value">{{ (coilDetail.specification) || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">品名</text>
|
||||
<text class="item-value">{{ (coilDetail.itemName) || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<button
|
||||
v-if="currentAction == 'withdrawShip'"
|
||||
:disabled="buttonLoading"
|
||||
:loading="buttonLoading"
|
||||
@click="handleCancelShipSubmit">
|
||||
撤回发货
|
||||
</button>
|
||||
<button
|
||||
v-if="currentAction == 'ship'"
|
||||
:disabled="buttonLoading"
|
||||
:loading="buttonLoading"
|
||||
@click="handleShipSubmit">
|
||||
发货
|
||||
</button>
|
||||
<button
|
||||
v-if="currentAction == 'release'"
|
||||
:disabled="buttonLoading"
|
||||
:loading="buttonLoading"
|
||||
@click="handleReleaseSubmit">
|
||||
释放库位
|
||||
</button>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -123,13 +192,15 @@
|
||||
updateMaterialCoilSimple,
|
||||
listMaterialCoil,
|
||||
updateMaterialCoil,
|
||||
exportCoil
|
||||
exportCoil,
|
||||
cancelExportCoil
|
||||
} from '@/api/wms/coil.js'
|
||||
import {
|
||||
addPendingAction
|
||||
} from '@/api/wms/pendingAction.js'
|
||||
import {
|
||||
getActualWarehouse
|
||||
getActualWarehouse,
|
||||
forceReleaseLocation
|
||||
} from '@/api/wms/actualWarehouse.js'
|
||||
|
||||
export default {
|
||||
@@ -141,7 +212,9 @@
|
||||
form: {},
|
||||
targetWarehouse: null, // 存储选中的目标库区信息
|
||||
bomDialogShow: false, // BOM参数弹窗控制(原有功能补充),
|
||||
mode: 'pda' // pda或camera
|
||||
mode: 'pda', // pda或camera
|
||||
currentAction: '', // ship,withdrawShip,release
|
||||
buttonLoading: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -156,7 +229,7 @@
|
||||
otherTypes() {
|
||||
return this.types.filter(item => {
|
||||
const value = parseInt(item.dictValue);
|
||||
return value < 100 || value > 199;
|
||||
return value < 100 || (value > 199 && value < 400);
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -169,6 +242,54 @@
|
||||
})
|
||||
},
|
||||
|
||||
async handleCancelShip() {
|
||||
// 撤回发货
|
||||
this.currentAction = 'withdrawShip';
|
||||
const content = await this.getQRCodeContent()
|
||||
let coilId = content.current_coil_id && content.current_coil_id !== 'null' ? content
|
||||
.current_coil_id : null;
|
||||
if (!coilId) {
|
||||
uni.showToast({
|
||||
title: '二维码异常',
|
||||
icon: 'none'
|
||||
})
|
||||
return;
|
||||
}
|
||||
const coilRes = await getMaterialCoil(coilId);
|
||||
if (coilRes.data.status == 0) {
|
||||
uni.showToast({
|
||||
title: '钢卷还未发货',
|
||||
icon: 'error'
|
||||
})
|
||||
return;
|
||||
}
|
||||
this.coilDetail = coilRes.data;
|
||||
this.$refs.shipPopup.open('bottom');
|
||||
},
|
||||
|
||||
async handleCancelShipSubmit() {
|
||||
if (!this.coilDetail.coilId) {
|
||||
uni.showToast({
|
||||
title: '钢卷标识缺失',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
const res = await cancelExportCoil(this.coilDetail.coilId)
|
||||
console.log(res)
|
||||
if (res.code != 200) {
|
||||
uni.showToast({
|
||||
icon: 'error',
|
||||
title: res.message || '撤回失败请重试'
|
||||
})
|
||||
return;
|
||||
}
|
||||
uni.showToast({
|
||||
title: this.coilDetail.currentCoilNo + '发货已撤回',
|
||||
icon: 'none'
|
||||
})
|
||||
this.$refs.shipPopup.close()
|
||||
},
|
||||
|
||||
scan(mode = 'camera') {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (mode == 'camera') {
|
||||
@@ -234,6 +355,55 @@
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async handleRelease() {
|
||||
const actualWarehouseId = await this.scan();
|
||||
// 查询真实库区在此处的钢卷
|
||||
const res = await listMaterialCoil({
|
||||
actualWarehouseId,
|
||||
dataType: 1
|
||||
})
|
||||
if (res.total == 0) {
|
||||
uni.showToast({
|
||||
title: '该库区未发现钢卷',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
};
|
||||
if (res.rows.length == 1) {
|
||||
this.coilDetail = res.rows[0]
|
||||
this.currentAction = 'release';
|
||||
this.$refs.shipPopup.open('bottom');
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '数据异常',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async handleReleaseSubmit() {
|
||||
this.buttonLoading = true;
|
||||
try {
|
||||
const res = await forceReleaseLocation(this.coilDetail.actualWarehouseId);
|
||||
if (res.code == 200) {
|
||||
uni.showToast({
|
||||
title: '释放成功',
|
||||
icon: 'none'
|
||||
})
|
||||
this.$refs.shipPopup.close()
|
||||
} else {
|
||||
throw new Error()
|
||||
}
|
||||
} catch {
|
||||
uni.showToast({
|
||||
title: '库区释放失败',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
this.buttonLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// 扫码并创建待操作(原有方法不变)
|
||||
async handleScan(actionType) {
|
||||
@@ -380,7 +550,7 @@
|
||||
if (coilRes.code !== 200) {
|
||||
throw new Error(coilRes.msg || '查询钢卷信息失败');
|
||||
}
|
||||
|
||||
|
||||
if (!coilRes.data) {
|
||||
throw new Error('未找到钢卷信息');
|
||||
}
|
||||
@@ -393,57 +563,63 @@
|
||||
})
|
||||
return;
|
||||
}
|
||||
this.$refs.shipPopup.open('bottom');
|
||||
this.currentAction = 'ship';
|
||||
this.coilDetail = coilRes.data
|
||||
},
|
||||
|
||||
uni.showModal({
|
||||
cancelText: '取消',
|
||||
confirmText: '确认',
|
||||
title: '确定要将钢卷号为:' + coilRes.data.currentCoilNo + '发货吗?',
|
||||
showCancel: true,
|
||||
success: async () => {
|
||||
try {
|
||||
// 判断钢卷的质量状态,必须是A+, A,A-, B+其中之一
|
||||
if (!['A+', 'A', 'A-', 'B+'].includes(coilRes.data.qualityStatus)) {
|
||||
this.$message.warning('钢卷质量状态需在B+及以上');
|
||||
return;
|
||||
}
|
||||
// 1. 更新钢卷状态为已发货
|
||||
await exportCoil(coilRes.data.coilId);
|
||||
|
||||
// 2. 插入一条已完成的待操作记录
|
||||
await addPendingAction({
|
||||
coilId: coilRes.data.coilId,
|
||||
currentCoilNo: coilRes.data.currentCoilNo,
|
||||
actionType: 402, // 402=发货
|
||||
actionStatus: 2, // 直接标记为完成状态
|
||||
scanTime: new Date(),
|
||||
scanDevice: this.getDeviceInfo(),
|
||||
priority: 0, // 0=普通
|
||||
sourceType: 'scan',
|
||||
warehouseId: coilRes.data.warehouseId,
|
||||
processTime: new Date(),
|
||||
completeTime: new Date()
|
||||
});
|
||||
|
||||
uni.showToast({
|
||||
title: '发货成功',
|
||||
icon: 'none'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('发货失败:', error);
|
||||
uni.showToast({
|
||||
title: error?.message || '发货失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
},
|
||||
fail() {
|
||||
async handleShipSubmit() {
|
||||
try {
|
||||
// 判断钢卷的质量状态,必须是A+, A,A-, B+,B,B-其中之一
|
||||
if (!['A+', 'A', 'A-', 'B+', 'B', 'B-'].includes(this.coilDetail.qualityStatus)
|
||||
&& !(this.coilDetail.qualityStatus == null
|
||||
|| this.coilDetail.qualityStatus == undefined
|
||||
|| this.coilDetail.qualityStatus == '')
|
||||
) {
|
||||
uni.showToast({
|
||||
title: '已取消发货',
|
||||
title: '只能发货B-以上品质的钢卷',
|
||||
icon: 'none'
|
||||
})
|
||||
});
|
||||
return;
|
||||
}
|
||||
});
|
||||
uni.hideLoading();
|
||||
// 1. 更新钢卷状态为已发货
|
||||
const res = await exportCoil(this.coilDetail.coilId);
|
||||
|
||||
// 2. 插入一条已完成的待操作记录
|
||||
await addPendingAction({
|
||||
coilId: this.coilDetail.coilId,
|
||||
currentCoilNo: this.coilDetail.currentCoilNo,
|
||||
actionType: 402, // 402=发货
|
||||
actionStatus: 2, // 直接标记为完成状态
|
||||
scanTime: new Date(),
|
||||
scanDevice: this.getDeviceInfo(),
|
||||
priority: 0, // 0=普通
|
||||
sourceType: 'scan',
|
||||
warehouseId: this.coilDetail.warehouseId,
|
||||
processTime: new Date(),
|
||||
completeTime: new Date()
|
||||
});
|
||||
|
||||
if (res.code != 200) {
|
||||
uni.showToast({
|
||||
icon: 'error',
|
||||
title: res.message || '发货失败请重试'
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
uni.showToast({
|
||||
title: '发货成功',
|
||||
icon: 'success'
|
||||
});
|
||||
this.$refs.shipPopup.close()
|
||||
} catch (error) {
|
||||
console.error('发货失败:', error);
|
||||
uni.showToast({
|
||||
title: error?.message || '发货失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 移库操作:第一步 - 扫描钢卷,打开移库弹窗(原有方法优化)
|
||||
@@ -671,6 +847,7 @@
|
||||
},
|
||||
mounted() {
|
||||
getDicts('action_type').then(res => {
|
||||
console.log(res.data)
|
||||
this.types = res.data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,22 @@
|
||||
<template>
|
||||
<view class="typing-container">
|
||||
<view class="info-grid">
|
||||
<view class="info-item">
|
||||
<text class="item-label">品名</text>
|
||||
<text class="item-value">{{ coilDetail.itemName }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">规格</text>
|
||||
<text class="item-value">{{ coilDetail.itemSpecification }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">材质</text>
|
||||
<text class="item-value">{{ coilDetail.itemMaterial }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">厂家</text>
|
||||
<text class="item-value">{{ coilDetail.itemManufacturer }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">入场钢卷号</text>
|
||||
<text class="item-value">{{ coilDetail.enterCoilNo || '-' }}</text>
|
||||
@@ -93,7 +109,8 @@
|
||||
data() {
|
||||
return {
|
||||
form: {},
|
||||
coilDetail: {},
|
||||
coilDetail: {
|
||||
},
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
@@ -107,8 +124,14 @@
|
||||
fetchCoil(coilId) {
|
||||
getMaterialCoil(coilId).then(res => {
|
||||
this.form = res.data;
|
||||
this.coilDetail = res.data;
|
||||
console.log(this.form, '钢卷信息')
|
||||
this.coilDetail = {
|
||||
...res.data,
|
||||
itemName: res.data.itemName,
|
||||
itemSpecification: res.data.specification,
|
||||
itemManufacturer: res.data.manufacturer,
|
||||
itemMaterial: res.data.material
|
||||
};
|
||||
console.log('钢卷信息', this.form, )
|
||||
})
|
||||
},
|
||||
// 获取设备信息(原有方法不变)
|
||||
|
||||
792
apps/hand-factory/pages/fahuo/fahuo.vue
Normal file
792
apps/hand-factory/pages/fahuo/fahuo.vue
Normal file
@@ -0,0 +1,792 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 筛选栏 -->
|
||||
<view class="filter-bar">
|
||||
<!-- 左侧:发货计划选择 -->
|
||||
<view class="plan-select" @click="openPlanPopup">
|
||||
<uni-icons type="shop" size="20" color="#666"></uni-icons>
|
||||
<text class="plan-text">{{ currentPlan.planNo || '选择发货单据' }}</text>
|
||||
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 发货计划选择悬浮窗 -->
|
||||
<uni-popup ref="planPopup" type="bottom" :mask-click="true" height="70%">
|
||||
<view class="popup-container">
|
||||
<!-- 弹窗头部 -->
|
||||
<view class="popup-header">
|
||||
<text class="popup-title">选择发货单据</text>
|
||||
<uni-icons type="close" size="20" @click="closePlanPopup"></uni-icons>
|
||||
</view>
|
||||
|
||||
<!-- 计划筛选输入框 -->
|
||||
<view class="plan-search">
|
||||
<input v-model="planKeyword" placeholder="请输入发货单号筛选" clearable class="plan-search-input"
|
||||
@confirm="fetchDeliveryPlan"></input>
|
||||
<uni-icons type="search" size="18" color="#666" @click="fetchDeliveryPlan(true)"></uni-icons>
|
||||
</view>
|
||||
|
||||
<!-- 发货计划列表 -->
|
||||
<scroll-view class="detail-scroll" scroll-y>
|
||||
<view class="form-card">
|
||||
<uni-list v-if="planList.length">
|
||||
<uni-list-item v-for="(item, index) in planList" :key="index"
|
||||
:title="item.waybillName + '-' + item.consigneeUnit"
|
||||
:note="item.licensePlate + '(' + item.principal + ')'"
|
||||
clickable
|
||||
@click="selectPlan(item)"></uni-list-item>
|
||||
</uni-list>
|
||||
<view class="empty-tip" v-else>暂无发货计划数据</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<uni-load-more :status="planHasMore ? 'more' : 'noMore'" @clickLoadMore="fetchDeliveryPlan(false)"
|
||||
v-if="planList.length"></uni-load-more>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
|
||||
<uni-popup ref='viewPopup' type="bottom" :mask-click="true" height="70%">
|
||||
<view class="popup-container">
|
||||
<!-- 弹窗头部:标题+关闭按钮 -->
|
||||
<view class="popup-header">
|
||||
<text class="popup-title">钢卷详情信息</text>
|
||||
<uni-icons type="close" size="20" @click="closeViewPopup"></uni-icons>
|
||||
</view>
|
||||
|
||||
<!-- 详情内容区域(滚动布局,适配多内容) -->
|
||||
<scroll-view class="detail-scroll" scroll-y>
|
||||
<view class="form-card" v-if="form.coilId">
|
||||
<!-- 基础信息网格 -->
|
||||
<view class="info-grid">
|
||||
<view class="info-item">
|
||||
<text class="item-label">入场钢卷号</text>
|
||||
<text class="item-value">{{ form.enterCoilNo || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">钢卷号</text>
|
||||
<text class="item-value">{{ form.currentCoilNo || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">班组</text>
|
||||
<text class="item-value">{{ form.team || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">数据类型</text>
|
||||
<text
|
||||
class="item-value">{{ form.dataType === 0 ? '默认数据' : form.dataType === 10 ? '待发货数据' : '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item full-width">
|
||||
<text class="item-label">逻辑库位</text>
|
||||
<text class="item-value">{{ form.warehouseName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item full-width">
|
||||
<text class="item-label">实际库位</text>
|
||||
<text class="item-value">{{ form.actualWarehouseName || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 物料信息 -->
|
||||
<view class="card-title" style="margin-top: 30rpx;">
|
||||
<text class="title-dot"></text>
|
||||
<text class="title-text">物料信息</text>
|
||||
</view>
|
||||
<view class="info-grid">
|
||||
<view class="info-item">
|
||||
<text class="item-label">物品名称</text>
|
||||
<text class="item-value">{{ form.itemName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">规格</text>
|
||||
<text class="item-value">{{ form.specification || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">材质</text>
|
||||
<text class="item-value">{{ form.material || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">厂家</text>
|
||||
<text class="item-value">{{ form.manufacturer || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">表面处理</text>
|
||||
<text class="item-value">{{ form.surfaceTreatmentDesc || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">镀层质量</text>
|
||||
<text class="item-value">{{ form.zincLayer || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 重量信息 -->
|
||||
<view class="card-title" style="margin-top: 30rpx;">
|
||||
<text class="title-dot"></text>
|
||||
<text class="title-text">数据信息</text>
|
||||
</view>
|
||||
<view class="info-grid">
|
||||
<view class="info-item">
|
||||
<text class="item-label">毛重(吨)</text>
|
||||
<text class="item-value">{{ form.grossWeight || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">净重(吨)</text>
|
||||
<text class="item-value">{{ form.netWeight || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">长度(m)</text>
|
||||
<text class="item-value">{{ form.length || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作信息 -->
|
||||
<view class="card-title" style="margin-top: 30rpx;">
|
||||
<text class="title-dot"></text>
|
||||
<text class="title-text">操作信息</text>
|
||||
</view>
|
||||
<view class="info-grid">
|
||||
<view class="info-item">
|
||||
<text class="item-label">操作人</text>
|
||||
<text class="item-value">{{ operatorName }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">更新时间</text>
|
||||
<text class="item-value">{{ currentAction.updateTime || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item full-width">
|
||||
<text class="item-label">操作状态</text>
|
||||
<text
|
||||
class="item-value">{{ currentAction.actionStatus === 0 ? '未开始' : currentAction.actionStatus === 2 ? '已完成' : '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空数据提示 -->
|
||||
<view class="empty-tip" v-else>
|
||||
暂无钢卷详情数据
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
|
||||
<!-- 主列表区域 -->
|
||||
<view class="main-list">
|
||||
<scroll-view class="list-scroll" scroll-y>
|
||||
<uni-list v-if="list.length">
|
||||
<uni-list-item v-for="(item, index) in list" :key="index" @click=""
|
||||
:title="`钢卷号:${item.coilNo || '-'}`" :note="`操作时间:${item.updateTime || '-'}`">
|
||||
<template v-slot:footer>
|
||||
<button style="margin-right: 10rpx;" size="mini" type="primary" plain="true" @click="openViewPopup(item)">查看</button>
|
||||
</template>
|
||||
</uni-list-item>
|
||||
</uni-list>
|
||||
<view class="empty-tip" v-else>暂无待操作数据</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<uni-load-more :status="hasMore ? 'more' : 'noMore'" @clickLoadMore="fetchList(false)"
|
||||
v-if="list.length"></uni-load-more>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
listDeliveryWaybill
|
||||
} from '@/api/wms/deliveryWaybill.js'
|
||||
import {
|
||||
listDeliveryPlan
|
||||
} from '@/api/wms/deliveryPlan.js';
|
||||
import {
|
||||
listDeliveryWaybillDetail
|
||||
} from '@/api/wms/deliveryWaybillDetail.js'
|
||||
import {
|
||||
updateMaterialCoilSimple,
|
||||
getMaterialCoil
|
||||
} from '@/api/wms/coil.js'
|
||||
|
||||
// 入库操作编码
|
||||
const ACTION_TYPE = 401;
|
||||
// 发货计划类型编码
|
||||
const RECEIVE_PLAN_TYPE = 2;
|
||||
// 未开始操作状态
|
||||
const ACTION_STATUS = 0;
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 主列表数据
|
||||
list: [],
|
||||
currentPlan: {}, // 当前选中的发货计划
|
||||
currentCoilNo: undefined, // 当前输入的钢卷号
|
||||
hasMore: true, // 是否有更多主列表数据
|
||||
pageNum: 1, // 主列表页码
|
||||
pageSize: 10, // 每页条数
|
||||
refreshing: false, // 下拉刷新状态
|
||||
|
||||
// 发货计划弹窗相关
|
||||
planKeyword: '', // 计划筛选关键词
|
||||
planList: [], // 发货计划列表
|
||||
planHasMore: true, // 计划列表是否有更多
|
||||
planPageNum: 1, // 计划列表页码
|
||||
popupShow: false, // 弹窗显示状态
|
||||
|
||||
form: {},
|
||||
loading: false,
|
||||
currentAction: {},
|
||||
loadingDetail: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 获取当前操作者昵称
|
||||
operatorName() {
|
||||
return this.$store.state.user.nickName || this.$store.state.user.name || '未知'
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
// 页面显示时初始化加载数据
|
||||
this.fetchList(true);
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 获取待操作列表数据
|
||||
* @param {Boolean} isRefresh 是否刷新(重置页码)
|
||||
*/
|
||||
async fetchList(isRefresh = false) {
|
||||
try {
|
||||
// 刷新时重置页码和加载状态
|
||||
if (isRefresh) {
|
||||
this.pageNum = 1;
|
||||
this.refreshing = true;
|
||||
}
|
||||
|
||||
// 构造请求参数
|
||||
const params = {
|
||||
pageNum: this.pageNum,
|
||||
waybillId: this.currentPlan.waybillId,
|
||||
pageSize: this.pageSize
|
||||
};
|
||||
|
||||
// 请求接口
|
||||
const res = await listDeliveryWaybillDetail(params);
|
||||
if (res.code === 200) {
|
||||
const list = res.rows || [];
|
||||
// 刷新时替换数据,加载更多时追加数据
|
||||
this.list = isRefresh ? list : [...this.list, ...list];
|
||||
// 判断是否有更多数据
|
||||
this.hasMore = this.pageNum * this.pageSize < res.total;
|
||||
}
|
||||
|
||||
console.log(this.list, '需要渲染的数据')
|
||||
} catch (err) {
|
||||
console.error('获取待操作列表失败:', err);
|
||||
uni.showToast({
|
||||
title: '数据加载失败',
|
||||
icon: 'none'
|
||||
});
|
||||
} finally {
|
||||
// 结束下拉刷新状态
|
||||
this.refreshing = false;
|
||||
// 加载更多时页码+1
|
||||
if (!isRefresh) this.pageNum++;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取发货计划列表
|
||||
* @param {Boolean} isRefresh 是否刷新(重置页码)
|
||||
*/
|
||||
async fetchDeliveryPlan(isRefresh = false) {
|
||||
try {
|
||||
if (isRefresh) {
|
||||
this.planPageNum = 1;
|
||||
}
|
||||
|
||||
// 构造请求参数
|
||||
const params = {
|
||||
waybillName: this.planKeyword || '', // 计划编号筛选
|
||||
pageNum: this.planPageNum,
|
||||
pageSize: this.pageSize,
|
||||
};
|
||||
|
||||
const res = await listDeliveryWaybill(params);
|
||||
if (res.code === 200) {
|
||||
const list = res.rows || [];
|
||||
|
||||
this.planList = isRefresh ? list : [...this.planList, ...list];
|
||||
this.planHasMore = this.planPageNum * this.pageSize < res.total;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('获取发货单据失败:', err);
|
||||
uni.showToast({
|
||||
title: '计划加载失败',
|
||||
icon: 'none'
|
||||
});
|
||||
} finally {
|
||||
if (!isRefresh) this.planPageNum++;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开发货计划弹窗
|
||||
*/
|
||||
openPlanPopup() {
|
||||
this.$refs.planPopup.open();
|
||||
// 打开弹窗时加载计划数据
|
||||
this.fetchDeliveryPlan(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭发货计划弹窗
|
||||
*/
|
||||
closePlanPopup() {
|
||||
this.$refs.planPopup.close();
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开发货弹窗
|
||||
*/
|
||||
openReceivePopup(row) {
|
||||
this.$refs.receivePopup.open('bottom')
|
||||
// this.loadingDetail = false;
|
||||
uni.showLoading({
|
||||
title: '正在加载发货详情'
|
||||
})
|
||||
getMaterialCoil(row.coilId).then(res => {
|
||||
this.form = res.data;
|
||||
this.currentAction = row;
|
||||
// this.loadingDetail = true
|
||||
uni.hideLoading()
|
||||
})
|
||||
},
|
||||
|
||||
openViewPopup(row) {
|
||||
this.$refs.viewPopup.open('bottom')
|
||||
// this.loadingDetail = false;
|
||||
uni.showLoading({
|
||||
title: '正在加载发货详情'
|
||||
})
|
||||
getMaterialCoil(row.coilId).then(res => {
|
||||
console.log(res.data)
|
||||
this.form = res.data;
|
||||
this.currentAction = row;
|
||||
uni.hideLoading()
|
||||
// this.loadingDetail = true
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭钢卷详情弹窗
|
||||
*/
|
||||
closeViewPopup() {
|
||||
this.$refs.viewPopup.close();
|
||||
// 可选:清空表单临时数据(根据业务需求决定是否保留)
|
||||
this.form = {};
|
||||
this.currentAction = {};
|
||||
},
|
||||
|
||||
/**
|
||||
* 确认发货
|
||||
*/
|
||||
confirmReceive(row) {
|
||||
const currentAction = this.currentAction;
|
||||
const form = this.form;
|
||||
const that = this;
|
||||
uni.showModal({
|
||||
title: '确定要发货吗?',
|
||||
success() {
|
||||
// console.log(currentAction, form)
|
||||
that.loading = true;
|
||||
Promise.all([
|
||||
updatePendingAction({
|
||||
...currentAction,
|
||||
actionStatus: 2
|
||||
}),
|
||||
updateMaterialCoilSimple({
|
||||
...form,
|
||||
dataType: 1
|
||||
})
|
||||
]).then(_ => {
|
||||
uni.showToast({
|
||||
title: '钢卷已发货'
|
||||
});
|
||||
that.fetchList(true);
|
||||
that.loading = false;
|
||||
that.$refs.receivePopup.close()
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 选择发货计划
|
||||
* @param {Object} plan 选中的计划对象
|
||||
*/
|
||||
selectPlan(plan) {
|
||||
this.currentPlan = plan;
|
||||
this.closePlanPopup();
|
||||
// 选择计划后重新加载主列表
|
||||
this.fetchList(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* 下拉刷新触发(修正后事件可正常绑定)
|
||||
*/
|
||||
onPullDownRefresh() {
|
||||
this.fetchList(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* 页面上拉加载(可选:补充页面级上拉加载)
|
||||
*/
|
||||
onReachBottom() {
|
||||
if (this.hasMore) {
|
||||
this.fetchList(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
// 补充页面级上拉加载钩子(可选,增强体验)
|
||||
onReachBottom() {
|
||||
this.onReachBottom();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 筛选栏样式 */
|
||||
.filter-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10rpx 20rpx;
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.plan-select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
padding: 10rpx 15rpx;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 6rpx;
|
||||
margin-right: 20rpx;
|
||||
min-width: 200rpx;
|
||||
}
|
||||
|
||||
.plan-text {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.coil-filter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 6rpx;
|
||||
padding: 0 15rpx;
|
||||
}
|
||||
|
||||
.coil-input {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
/* 弹窗样式 */
|
||||
.popup-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.popup-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.popup-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.plan-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
padding: 15rpx 20rpx;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.plan-search-input {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
// .plan-list {
|
||||
// flex: 1;
|
||||
// overflow-y: auto;
|
||||
// padding: 10rpx;
|
||||
// }
|
||||
|
||||
/* 列表样式 */
|
||||
.main-list {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.list-scroll {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.uni-list {
|
||||
background-color: #fff;
|
||||
margin: 10rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.uni-list-item {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
/* 空数据提示 */
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
padding: 50rpx 0;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 加载更多样式 */
|
||||
.uni-load-more {
|
||||
margin: 20rpx 0;
|
||||
}
|
||||
|
||||
.form-card {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
overflow: scroll;
|
||||
height: 70vh;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 25rpx;
|
||||
padding-bottom: 20rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
.title-dot {
|
||||
width: 8rpx;
|
||||
height: 28rpx;
|
||||
background: #007aff;
|
||||
border-radius: 4rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
flex: 1;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
font-size: 22rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 12rpx;
|
||||
margin-right: 10rpx;
|
||||
|
||||
&.status-1 {
|
||||
background: #d1f2eb;
|
||||
color: #0c6957;
|
||||
}
|
||||
|
||||
&.status-0 {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
}
|
||||
}
|
||||
|
||||
.more-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
background: #f0f7ff;
|
||||
border-radius: 20rpx;
|
||||
border: 1rpx solid #007aff;
|
||||
|
||||
.icon-more {
|
||||
font-size: 24rpx;
|
||||
color: #007aff;
|
||||
}
|
||||
|
||||
.more-text {
|
||||
font-size: 24rpx;
|
||||
color: #007aff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 信息网格 */
|
||||
.info-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20rpx;
|
||||
|
||||
.info-item {
|
||||
flex: 1;
|
||||
min-width: 45%;
|
||||
background: #f8f9fa;
|
||||
padding: 20rpx;
|
||||
border-radius: 12rpx;
|
||||
|
||||
&.full-width {
|
||||
flex: 0 0 100%;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 表单项 */
|
||||
.form-item {
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
&:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 15rpx;
|
||||
font-weight: 500;
|
||||
|
||||
&::before {
|
||||
content: '*';
|
||||
color: #ff4d4f;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.form-label-optional {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 15rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
padding: 0 24rpx;
|
||||
background: #f8f9fa;
|
||||
border: 2rpx solid #e8e8e8;
|
||||
border-radius: 12rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
box-sizing: border-box;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:focus {
|
||||
background: #fff;
|
||||
border-color: #007aff;
|
||||
}
|
||||
|
||||
&.form-input-disabled {
|
||||
background: #f5f5f5;
|
||||
color: #999;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 操作者信息 */
|
||||
.operator-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20rpx 0;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.operator-label {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.operator-name {
|
||||
font-size: 26rpx;
|
||||
color: #007aff;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
/* 操作按钮 */
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-top: 30rpx;
|
||||
|
||||
.btn {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
border-radius: 12rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #f5f5f5;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #007aff 0%, #0051d5 100%);
|
||||
color: #fff;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 122, 255, 0.3);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,11 +1,17 @@
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
hasJumped: false // 防止重复跳转的标记
|
||||
tabVisible: {
|
||||
admin: [true, true, false, true, true],
|
||||
canteen: [false, false, true, false, true],
|
||||
worker: [false, true, false, true, true]
|
||||
},
|
||||
hasJumped: false, // 防止重复跳转的标记
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
@@ -25,10 +31,13 @@ export default {
|
||||
const roles = res.data.roles;
|
||||
|
||||
if (roles.includes('admin')) {
|
||||
uni.setTabBarItem({
|
||||
index: 0,
|
||||
visible: true
|
||||
});
|
||||
for (let i = 0; i < this.tabVisible.admin.length; i++) {
|
||||
const v = this.tabVisible.admin[i]
|
||||
uni.setTabBarItem({
|
||||
index: i,
|
||||
visible: v
|
||||
});
|
||||
}
|
||||
// 管理员角色跳转
|
||||
uni.switchTab({
|
||||
url: '/pages/line/line',
|
||||
@@ -44,14 +53,40 @@ export default {
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (roles.includes('canteen')) {
|
||||
for (let i = 0; i < this.tabVisible.canteen.length; i++) {
|
||||
const v = this.tabVisible.canteen[i]
|
||||
uni.setTabBarItem({
|
||||
index: i,
|
||||
visible: v
|
||||
});
|
||||
}
|
||||
// 食堂角色跳转
|
||||
uni.switchTab({
|
||||
url: '/pages/meal/meal',
|
||||
success: () => {
|
||||
this.hasJumped = true; // 标记已跳转
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('管理员页面跳转失败:', err);
|
||||
uni.showToast({
|
||||
title: '跳转产线页面失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (roles.includes('worker')) {
|
||||
// 工人角色跳转
|
||||
// 设置tab内容
|
||||
// 设置产线和复杂扫码不可见
|
||||
uni.setTabBarItem({
|
||||
index: 0,
|
||||
visible: false
|
||||
});
|
||||
for (let i = 0; i < this.tabVisible.worker.length; i++) {
|
||||
const v = this.tabVisible.worker[i]
|
||||
uni.setTabBarItem({
|
||||
index: i,
|
||||
visible: v
|
||||
});
|
||||
}
|
||||
uni.switchTab({
|
||||
url: '/pages/easycode/easycode',
|
||||
success: () => {
|
||||
@@ -67,16 +102,13 @@ export default {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 处理未定义角色(默认角色)
|
||||
uni.showToast({
|
||||
title: '检测到未知角色,将跳转至默认页面',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
uni.setTabBarItem({
|
||||
index: 0,
|
||||
visible: false
|
||||
});
|
||||
for (let i = 0; i < this.tabVisible.worker.length; i++) {
|
||||
const v = this.tabVisible.worker[i]
|
||||
uni.setTabBarItem({
|
||||
index: i,
|
||||
visible: v
|
||||
});
|
||||
}
|
||||
// 延迟跳转,确保提示被用户看到
|
||||
setTimeout(() => {
|
||||
uni.switchTab({
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
|
||||
<view class="content-wrapper">
|
||||
<Acidity v-if="active == 0"/>
|
||||
<Paint v-else-if="active == 1"/>
|
||||
<Zinc1 v-else-if="active == 2"></Zinc1>
|
||||
<Zinc2 v-else-if="active == 3"></Zinc2>
|
||||
<Zinc3 v-else-if="active == 4"></Zinc3>
|
||||
<!-- <Paint v-else-if="active == 1"/> -->
|
||||
<Zinc1 v-else-if="active == 1"></Zinc1>
|
||||
<!-- <Zinc2 v-else-if="active == 3"></Zinc2> -->
|
||||
<!-- <Zinc3 v-else-if="active == 4"></Zinc3> -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -24,15 +24,6 @@
|
||||
<view class="action-btn">
|
||||
<button @click="handleLogin" class="login-btn cu-btn block bg-blue lg round">登录</button>
|
||||
</view>
|
||||
<!-- <view class="reg text-center" v-if="register">
|
||||
<text class="text-grey1">没有账号?</text>
|
||||
<text @click="handleUserRegister" class="text-blue">立即注册</text>
|
||||
</view> -->
|
||||
<!-- <view class="xieyi text-center">
|
||||
<text class="text-grey1">登录即代表同意</text>
|
||||
<text @click="handleUserAgrement" class="text-blue">《用户协议》</text>
|
||||
<text @click="handlePrivacy" class="text-blue">《隐私协议》</text>
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
524
apps/hand-factory/pages/meal/meal.vue
Normal file
524
apps/hand-factory/pages/meal/meal.vue
Normal file
@@ -0,0 +1,524 @@
|
||||
<template>
|
||||
<view class="meal-report-container">
|
||||
<!-- 日期和时间选择区域 -->
|
||||
<view class="date-time-wrap">
|
||||
<view class="picker-item">
|
||||
<text class="picker-label">餐别:</text>
|
||||
<uni-data-select v-model="queryParams.mealType" :localdata="range" @change="getList"></uni-data-select>
|
||||
</view>
|
||||
<!-- 报餐日期选择 -->
|
||||
<view class="picker-item">
|
||||
<text class="picker-label">报餐日期:</text>
|
||||
<uni-datetime-picker v-model="queryParams.reportDate" type="date" placeholder="选择日期" @change="onDateConfirm"
|
||||
class="picker-input"></uni-datetime-picker>
|
||||
</view>
|
||||
|
||||
<!-- 自定义截止时间选择 -->
|
||||
<view class="picker-item">
|
||||
<text class="picker-label">截止时间:</text>
|
||||
<view class="custom-time-picker" @click="openTimePopup">
|
||||
<text class="time-text">{{ formattedDeadlineTime }}</text>
|
||||
<uni-icons type="arrowdown" size="14" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 报餐统计卡片 -->
|
||||
<view class="statistics-card">
|
||||
<view class="card-title">报餐人数统计</view>
|
||||
<view class="stats-grid">
|
||||
<!-- 第一行 -->
|
||||
<view class="stats-item">
|
||||
<text class="item-label">堂食人数</text>
|
||||
<text class="item-value">{{ validDineIn + invalidDineIn }}</text>
|
||||
</view>
|
||||
<view class="stats-item">
|
||||
<text class="item-label">打包人数</text>
|
||||
<text class="item-value">{{ validTakeout + invalidTakeout }}</text>
|
||||
</view>
|
||||
<view class="stats-item">
|
||||
<text class="item-label">总人数</text>
|
||||
<text class="item-value">{{ validTotal + invalidTotal }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 第二行 -->
|
||||
<view class="stats-item">
|
||||
<text class="item-label">有效堂食人数</text>
|
||||
<text class="item-value">{{ validDineIn }}</text>
|
||||
</view>
|
||||
<view class="stats-item">
|
||||
<text class="item-label">有效打包人数</text>
|
||||
<text class="item-value">{{ validTakeout }}</text>
|
||||
</view>
|
||||
<view class="stats-item">
|
||||
<text class="item-label">有效总人数</text>
|
||||
<text class="item-value">{{ validTotal }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 第三行 -->
|
||||
<view class="stats-item">
|
||||
<text class="item-label">无效堂食人数</text>
|
||||
<text class="item-value">{{ invalidDineIn }}</text>
|
||||
</view>
|
||||
<view class="stats-item">
|
||||
<text class="item-label">无效打包人数</text>
|
||||
<text class="item-value">{{ invalidTakeout }}</text>
|
||||
</view>
|
||||
<view class="stats-item">
|
||||
<text class="item-label">无效总人数</text>
|
||||
<text class="item-value">{{ invalidTotal }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 自定义时分秒选择弹窗 -->
|
||||
<uni-popup ref="timePopup" type="bottom" border-radius="10px 10px 0 0">
|
||||
<view class="time-popup-content">
|
||||
<!-- 弹窗标题 -->
|
||||
<view class="popup-header">
|
||||
<text class="popup-title">选择截止时间</text>
|
||||
<view class="popup-btns">
|
||||
<button class="cancel-btn" @click="closeTimePopup">取消</button>
|
||||
<button class="confirm-btn" @click="confirmTime">确认</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 时分秒选择区域 -->
|
||||
<view class="time-select-wrap">
|
||||
<!-- 小时选择 -->
|
||||
<view class="time-unit">
|
||||
<text class="unit-label">时</text>
|
||||
<uni-number-box v-model="timeSelect.hour" :min="0" :max="23" :step="1"
|
||||
@change="handleTimeChange"></uni-number-box>
|
||||
</view>
|
||||
|
||||
<!-- 分钟选择 -->
|
||||
<view class="time-unit">
|
||||
<text class="unit-label">分</text>
|
||||
<uni-number-box v-model="timeSelect.minute" :min="0" :max="59" :step="1"
|
||||
@change="handleTimeChange"></uni-number-box>
|
||||
</view>
|
||||
|
||||
<!-- 秒选择 -->
|
||||
<view class="time-unit">
|
||||
<text class="unit-label">秒</text>
|
||||
<uni-number-box v-model="timeSelect.second" :min="0" :max="59" :step="1"
|
||||
@change="handleTimeChange"></uni-number-box>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
listMealReport
|
||||
} from "@/api/wms/mealReport";
|
||||
import {
|
||||
getDicts
|
||||
} from '@/api/system/dict/data.js'
|
||||
import {
|
||||
getConfigKey
|
||||
} from '@/api/system/config.js'
|
||||
|
||||
export default {
|
||||
name: 'MealReportStatistics',
|
||||
data() {
|
||||
return {
|
||||
queryParams: {
|
||||
mealType: '',
|
||||
reportDate: '',
|
||||
pageSize: 9999,
|
||||
pageNum: 1
|
||||
},
|
||||
deadlineDate: '', // 截止日期
|
||||
deadlineTime: '12:00:00', // 截止时间(原始值)
|
||||
// 时分秒选择器临时变量
|
||||
timeSelect: {
|
||||
hour: 12,
|
||||
minute: 0,
|
||||
second: 0
|
||||
},
|
||||
list: [],
|
||||
loading: false,
|
||||
// 统计数据
|
||||
validDineIn: 0,
|
||||
validTakeout: 0,
|
||||
validTotal: 0,
|
||||
invalidDineIn: 0,
|
||||
invalidTakeout: 0,
|
||||
invalidTotal: 0,
|
||||
range: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 格式化截止时间显示(补零)
|
||||
formattedDeadlineTime() {
|
||||
const [hour, minute, second] = this.deadlineTime.split(':');
|
||||
return `${hour.padStart(2, '0')}:${minute.padStart(2, '0')}:${second.padStart(2, '0')}`;
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
// 初始化今日日期
|
||||
this.initTodayDate();
|
||||
// 初始化截止日期为今日
|
||||
this.deadlineDate = this.queryParams.reportDate;
|
||||
// 获取餐别字典数据
|
||||
this.getRangeData();
|
||||
// 加载报餐数据
|
||||
this.getList();
|
||||
this.getDeadlineConfig();
|
||||
},
|
||||
methods: {
|
||||
/** 初始化今日日期为yyyy-MM-dd格式 */
|
||||
initTodayDate() {
|
||||
const today = new Date();
|
||||
const year = today.getFullYear();
|
||||
const month = String(today.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(today.getDate()).padStart(2, '0');
|
||||
this.queryParams.reportDate = `${year}-${month}-${day}`;
|
||||
},
|
||||
|
||||
getDeadlineConfig() {
|
||||
getConfigKey('hrm.meal.deadline').then(response => {
|
||||
this.queryParams.deadlineTime = response.msg || '16:00:00'
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取餐别字典数据 */
|
||||
getRangeData() {
|
||||
getDicts('hrm_meal_type').then(res => {
|
||||
this.range = res.data.map(item => ({
|
||||
text: item.dictLabel,
|
||||
value: item.dictValue
|
||||
}));
|
||||
});
|
||||
},
|
||||
|
||||
/** 查询部门报餐列表 */
|
||||
getList() {
|
||||
this.loading = true;
|
||||
listMealReport(this.queryParams).then(response => {
|
||||
this.list = response.rows || [];
|
||||
this.loading = false;
|
||||
this.calcTableSum();
|
||||
}).catch(error => {
|
||||
console.error('获取报餐数据失败:', error);
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
|
||||
/** 核心逻辑:区分有效/无效报餐统计 */
|
||||
calcTableSum() {
|
||||
let validDine = 0,
|
||||
validTake = 0,
|
||||
validAll = 0;
|
||||
let invalidDine = 0,
|
||||
invalidTake = 0,
|
||||
invalidAll = 0;
|
||||
|
||||
this.list.forEach(item => {
|
||||
// 处理空值,转为数字
|
||||
const dine = item.dineInPeople ? Number(item.dineInPeople) : 0;
|
||||
const take = item.takeoutPeople ? Number(item.takeoutPeople) : 0;
|
||||
const total = item.totalPeople ? Number(item.totalPeople) : 0;
|
||||
|
||||
// 判断当前报餐是否有效
|
||||
if (this.isValidMealReport(item.createTime)) {
|
||||
validDine += dine;
|
||||
validTake += take;
|
||||
validAll += total;
|
||||
} else {
|
||||
invalidDine += dine;
|
||||
invalidTake += take;
|
||||
invalidAll += total;
|
||||
}
|
||||
});
|
||||
|
||||
// 赋值到统计变量
|
||||
this.validDineIn = validDine;
|
||||
this.validTakeout = validTake;
|
||||
this.validTotal = validAll;
|
||||
this.invalidDineIn = invalidDine;
|
||||
this.invalidTakeout = invalidTake;
|
||||
this.invalidTotal = invalidAll;
|
||||
},
|
||||
|
||||
/** 判断报餐是否有效:创建时间在截止时间之前则有效 */
|
||||
isValidMealReport(createTime) {
|
||||
if (!createTime) return false;
|
||||
|
||||
// 拼接完整的截止时间字符串
|
||||
const deadlineDateTime = `${this.deadlineDate || this.queryParams.reportDate} ${this.deadlineTime}`;
|
||||
// 比较时间
|
||||
const createTimeObj = new Date(createTime);
|
||||
const deadlineTimeObj = new Date(deadlineDateTime);
|
||||
|
||||
return createTimeObj <= deadlineTimeObj;
|
||||
},
|
||||
|
||||
/** 报餐日期选择确认 */
|
||||
onDateConfirm(e) {
|
||||
this.queryParams.reportDate = e;
|
||||
// 如果截止日期未选择,同步为报餐日期
|
||||
if (!this.deadlineDate) {
|
||||
this.deadlineDate = e;
|
||||
}
|
||||
this.getList();
|
||||
},
|
||||
|
||||
/** 截止日期选择确认 */
|
||||
onDeadlineDateConfirm(e) {
|
||||
this.deadlineDate = e;
|
||||
this.calcTableSum();
|
||||
},
|
||||
|
||||
// ===== 自定义时间选择器逻辑 =====
|
||||
/** 打开时间选择弹窗 */
|
||||
openTimePopup() {
|
||||
// 解析当前时间到选择器
|
||||
const [hour, minute, second] = this.deadlineTime.split(':').map(Number);
|
||||
this.timeSelect = {
|
||||
hour,
|
||||
minute,
|
||||
second
|
||||
};
|
||||
// 打开弹窗
|
||||
this.$refs.timePopup.open('center');
|
||||
},
|
||||
|
||||
/** 关闭时间选择弹窗 */
|
||||
closeTimePopup() {
|
||||
this.$refs.timePopup.close();
|
||||
},
|
||||
|
||||
/** 处理时分秒数值变化 */
|
||||
handleTimeChange() {
|
||||
// 确保数值在合法范围内
|
||||
this.timeSelect.hour = Math.max(0, Math.min(23, this.timeSelect.hour));
|
||||
this.timeSelect.minute = Math.max(0, Math.min(59, this.timeSelect.minute));
|
||||
this.timeSelect.second = Math.max(0, Math.min(59, this.timeSelect.second));
|
||||
},
|
||||
|
||||
/** 确认选择的时间 */
|
||||
confirmTime() {
|
||||
// 格式化时分秒(补零)
|
||||
const hour = String(this.timeSelect.hour).padStart(2, '0');
|
||||
const minute = String(this.timeSelect.minute).padStart(2, '0');
|
||||
const second = String(this.timeSelect.second).padStart(2, '0');
|
||||
// 更新截止时间
|
||||
this.deadlineTime = `${hour}:${minute}:${second}`;
|
||||
// 重新计算统计数据
|
||||
this.calcTableSum();
|
||||
// 关闭弹窗
|
||||
this.closeTimePopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 容器样式 */
|
||||
.meal-report-container {
|
||||
padding: 15px;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 日期时间选择区域 */
|
||||
.date-time-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
background-color: #fff;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.picker-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.picker-label {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.picker-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 自定义时间选择器样式 */
|
||||
.custom-time-picker {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.time-text {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 统计卡片样式 */
|
||||
.statistics-card {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
padding: 12px 15px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
/* 网格布局:3列 */
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
border: 1px solid #eee;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
/* 统计项样式 */
|
||||
.stats-item {
|
||||
padding: 12px 8px;
|
||||
text-align: center;
|
||||
border-right: 1px solid #eee;
|
||||
border-bottom: 1px solid #eee;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 去掉最后一列右边框 */
|
||||
.stats-grid .stats-item:nth-child(3n) {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
/* 去掉最后一行下边框 */
|
||||
.stats-grid .stats-item:last-child,
|
||||
.stats-grid .stats-item:nth-last-child(2),
|
||||
.stats-grid .stats-item:nth-last-child(3) {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* 标签和值样式 */
|
||||
.item-label {
|
||||
display: block;
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
display: block;
|
||||
color: #333;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 时间弹窗样式 */
|
||||
.time-popup-content {
|
||||
background-color: #fff;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.popup-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 15px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.popup-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.popup-btns {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.cancel-btn,
|
||||
.confirm-btn {
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
background-color: #f5f5f5;
|
||||
color: #666;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
background-color: #007aff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* 时分秒选择区域 */
|
||||
.time-select-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.time-unit {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.unit-label {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.colon {
|
||||
font-size: 20px;
|
||||
color: #333;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
/* 适配小屏手机 */
|
||||
@media (max-width: 375px) {
|
||||
.stats-item {
|
||||
padding: 10px 5px;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.time-select-wrap {
|
||||
gap: 5px;
|
||||
padding: 15px 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -25,57 +25,20 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- <view class="content-section">
|
||||
<view class="mine-actions grid col-4 text-center">
|
||||
<view class="action-item" @click="handleJiaoLiuQun">
|
||||
<view class="iconfont icon-friendfill text-pink icon"></view>
|
||||
<text class="text">交流群</text>
|
||||
</view>
|
||||
<view class="action-item" @click="handleBuilding">
|
||||
<view class="iconfont icon-service text-blue icon"></view>
|
||||
<text class="text">在线客服</text>
|
||||
</view>
|
||||
<view class="action-item" @click="handleBuilding">
|
||||
<view class="iconfont icon-community text-mauve icon"></view>
|
||||
<text class="text">反馈社区</text>
|
||||
</view>
|
||||
<view class="action-item" @click="handleBuilding">
|
||||
<view class="iconfont icon-dianzan text-green icon"></view>
|
||||
<text class="text">点赞我们</text>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<view class="menu-list">
|
||||
<view class="list-cell list-cell-arrow" @click="handleToSearch">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-setting menu-icon"></view>
|
||||
<view>钢卷查找</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="list-cell list-cell-arrow" @click="handleLogout">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-setting menu-icon"></view>
|
||||
<view>退出登录</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="list-cell list-cell-arrow" @click="handleToEditInfo">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-user menu-icon"></view>
|
||||
<view>编辑资料</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow" @click="handleHelp">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-help menu-icon"></view>
|
||||
<view>常见问题</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow" @click="handleAbout">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-aixin menu-icon"></view>
|
||||
<view>关于我们</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-cell list-cell-arrow" @click="handleToSetting">
|
||||
<view class="menu-item-box">
|
||||
<view class="iconfont icon-setting menu-icon"></view>
|
||||
<view>应用设置</view>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
</view>
|
||||
@@ -98,8 +61,8 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleToInfo() {
|
||||
this.$tab.navigateTo('/pages/mine/info/index')
|
||||
handleToSearch() {
|
||||
this.$tab.navigateTo('/pages/search/search')
|
||||
},
|
||||
handleLogout() {
|
||||
this.$modal.confirm('确定注销并退出系统吗?').then(() => {
|
||||
@@ -108,30 +71,7 @@
|
||||
})
|
||||
})
|
||||
},
|
||||
handleToEditInfo() {
|
||||
this.$tab.navigateTo('/pages/mine/info/edit')
|
||||
},
|
||||
handleToSetting() {
|
||||
this.$tab.navigateTo('/pages/mine/setting/index')
|
||||
},
|
||||
handleToLogin() {
|
||||
this.$tab.reLaunch('/pages/login')
|
||||
},
|
||||
handleToAvatar() {
|
||||
this.$tab.navigateTo('/pages/mine/avatar/index')
|
||||
},
|
||||
handleHelp() {
|
||||
this.$tab.navigateTo('/pages/mine/help/index')
|
||||
},
|
||||
handleAbout() {
|
||||
this.$tab.navigateTo('/pages/mine/about/index')
|
||||
},
|
||||
handleJiaoLiuQun() {
|
||||
this.$modal.showToast('QQ群:①133713780(满)、②146013835(满)、③189091635')
|
||||
},
|
||||
handleBuilding() {
|
||||
this.$modal.showToast('模块建设中~')
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -166,33 +166,29 @@
|
||||
<text class="title-text">物料信息</text>
|
||||
</view>
|
||||
<view class="info-grid">
|
||||
<!-- <view class="info-item">
|
||||
<text class="item-label">物品类型</text>
|
||||
<text class="item-value">{{ form.itemType || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">物品ID</text>
|
||||
<text class="item-value">{{ form.itemId || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item full-width">
|
||||
<text class="item-label">物料名称</text>
|
||||
<text class="item-value">{{ form.itemName || '-' }}</text>
|
||||
</view> -->
|
||||
<view class="info-item">
|
||||
<text class="item-label">物品名称</text>
|
||||
<text class="item-value">{{ form.rawMaterial.rawMaterialName || form.product.productName || '-' }}</text>
|
||||
<text class="item-value">{{ form.itemName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">规格</text>
|
||||
<text class="item-value">{{ form.rawMaterial.specification || form.product.specification || '-' }}</text>
|
||||
<text class="item-value">{{ form.specification || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">材质</text>
|
||||
<text class="item-value">{{ form.rawMaterial.material || form.product.material || '-' }}</text>
|
||||
<text class="item-value">{{ form.material || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">厂家</text>
|
||||
<text class="item-value">{{ form.rawMaterial.manufacturer || form.product.manufacturer || '-' }}</text>
|
||||
<text class="item-value">{{ form.manufacturer || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">表面处理</text>
|
||||
<text class="item-value">{{ form.surfaceTreatmentDesc || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">镀层质量</text>
|
||||
<text class="item-value">{{ form.zincLayer || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
875
apps/hand-factory/pages/search/search.vue
Normal file
875
apps/hand-factory/pages/search/search.vue
Normal file
@@ -0,0 +1,875 @@
|
||||
<template>
|
||||
<view class="typing-container">
|
||||
<view class="form-card" v-show='currentView == "search"'>
|
||||
<view class="card-title">
|
||||
<text class="title-dot"></text>
|
||||
<text class="title-text">设置条件查询钢卷</text>
|
||||
</view>
|
||||
|
||||
<!-- 当前钢卷号 -->
|
||||
<view class="form-item form-item-optional">
|
||||
<text class="form-label">入场钢卷号</text>
|
||||
<input v-model="form.enterCoilNo" placeholder="请输入当前钢卷号" class="form-input"
|
||||
:disabled="coilDetail.dataType === 0" />
|
||||
</view>
|
||||
|
||||
<view class="form-item form-item-optional">
|
||||
<text class="form-label">当前钢卷号</text>
|
||||
<input v-model="form.currentCoilNo" placeholder="请输入当前钢卷号" class="form-input"
|
||||
:disabled="coilDetail.dataType === 0" :class="{ 'form-input-disabled': coilDetail.dataType === 0 }" />
|
||||
</view>
|
||||
|
||||
<!-- 班组 -->
|
||||
<view class="form-item form-item-optional">
|
||||
<text class="form-label">班组</text>
|
||||
<input v-model="form.team" placeholder="请输入班组名称" class="form-input" />
|
||||
</view>
|
||||
|
||||
<!-- 库区选择 -->
|
||||
<view class="form-item form-item-optional">
|
||||
<text class="form-label-optional">逻辑库区</text>
|
||||
<klp-warehouse-picker v-model="form.warehouseId" :disabled="coilDetail.dataType === 0" placeholder="请选择目标库区" />
|
||||
</view>
|
||||
|
||||
<view class="form-item form-item-optional">
|
||||
<text class="form-label-optional">真实库区</text>
|
||||
<klp-actual-warehouse-picker v-model="form.actualWarehouseId" :disabled="coilDetail.dataType === 0"
|
||||
placeholder="请选择目标库区" />
|
||||
</view>
|
||||
|
||||
<!-- 物品类型、产品、原材料选择(使用封装组件) -->
|
||||
<klp-material-picker
|
||||
:item-type.sync="form.itemType"
|
||||
:item-id.sync="form.itemId"
|
||||
:material-type.sync="form.materialType"
|
||||
:disabled="coilDetail.dataType === 0"
|
||||
:page-size="2000"
|
||||
/>
|
||||
|
||||
<view class="form-item form-item-optional">
|
||||
<text class="form-label-optional">品名</text>
|
||||
<input v-model="form.itemName" placeholder="请输入品名" class="form-input" />
|
||||
</view>
|
||||
|
||||
<view class="form-item form-item-optional">
|
||||
<text class="form-label-optional">规格</text>
|
||||
<input v-model="form.itemSpecification" placeholder="请输入规格" class="form-input" />
|
||||
</view>
|
||||
|
||||
<view class="form-item form-item-optional">
|
||||
<text class="form-label-optional">材质</text>
|
||||
<input v-model="form.itemMaterial" placeholder="请输入材质" class="form-input" />
|
||||
</view>
|
||||
|
||||
<view class="form-item form-item-optional">
|
||||
<text class="form-label-optional">厂家</text>
|
||||
<input v-model="form.itemManufacturer" placeholder="请输入厂家" class="form-input" />
|
||||
</view>
|
||||
|
||||
<!-- 毛重 -->
|
||||
<view class="form-item form-item-optional">
|
||||
<text class="form-label-optional">毛重 (吨)</text>
|
||||
<input v-model="form.grossWeight" type="digit" placeholder="请输入毛重" class="form-input"
|
||||
:disabled="coilDetail.dataType === 0" :class="{ 'form-input-disabled': coilDetail.dataType === 0 }" />
|
||||
</view>
|
||||
|
||||
<!-- 净重 -->
|
||||
<view class="form-item form-item-optional">
|
||||
<text class="form-label-optional">净重 (吨)</text>
|
||||
<input v-model="form.netWeight" type="digit" placeholder="请输入净重" class="form-input"
|
||||
:disabled="coilDetail.dataType === 0" :class="{ 'form-input-disabled': coilDetail.dataType === 0 }" />
|
||||
</view>
|
||||
|
||||
<!-- 长度 -->
|
||||
<view class="form-item form-item-optional">
|
||||
<text class="form-label-optional">长度 (米)</text>
|
||||
<input v-model="form.length" type="digit" placeholder="请输入长度" class="form-input"
|
||||
:disabled="coilDetail.dataType === 0" :class="{ 'form-input-disabled': coilDetail.dataType === 0 }" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-show='currentView == "list"' class="list-wrap">
|
||||
<!-- 列表中尽可能多的展示信息 -->
|
||||
<uni-list v-if="list.length > 0">
|
||||
<uni-list-item
|
||||
v-for="item in list"
|
||||
:key="item.coilId"
|
||||
:title="item.currentCoilNo"
|
||||
:note="`入场号:${item.enterCoilNo} | 净重:${item.netWeight}吨 | ${item.warehouseName} | ${item.materialType} | ${getStatusText(item.status)}`"
|
||||
clickable
|
||||
@click="showCoilDetail(item)"
|
||||
></uni-list-item>
|
||||
</uni-list>
|
||||
|
||||
<!-- 空列表兜底 -->
|
||||
<view class="empty-list" v-else>
|
||||
<text>暂无符合条件的钢卷数据</text>
|
||||
</view>
|
||||
|
||||
<!-- 分页控件 -->
|
||||
<view class="pagination" v-if="total > 0">
|
||||
<view class="pagination-info">
|
||||
<text>共 {{ total }} 条 / 共 {{ totalPages }} 页</text>
|
||||
<text>当前第 {{ pager.pageNum }} 页</text>
|
||||
</view>
|
||||
<view class="pagination-btns">
|
||||
<button
|
||||
class="page-btn"
|
||||
@click="prevPage"
|
||||
:disabled="pager.pageNum <= 1"
|
||||
>
|
||||
上一页
|
||||
</button>
|
||||
<button
|
||||
class="page-btn"
|
||||
@click="nextPage"
|
||||
:disabled="pager.pageNum >= totalPages"
|
||||
>
|
||||
下一页
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 钢卷详情弹窗 -->
|
||||
<uni-popup ref="shipPopup" type="bottom">
|
||||
<view style="background-color: white; padding: 20rpx;">
|
||||
<!-- 弹窗标题+关闭按钮 -->
|
||||
<view class="popup-header flex justify-between align-center mb-20">
|
||||
<text class="font-32 font-bold">钢卷详情</text>
|
||||
<text class="icon-close font-40" @click="closePopup"></text>
|
||||
</view>
|
||||
|
||||
<view class="info-card" v-if="coilDetail.coilId">
|
||||
<view class="info-grid">
|
||||
<view class="info-item">
|
||||
<text class="item-label">入场钢卷号</text>
|
||||
<text class="item-value">{{ coilDetail.enterCoilNo || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">当前钢卷号</text>
|
||||
<text class="item-value">{{ coilDetail.currentCoilNo || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">厂家卷号</text>
|
||||
<text class="item-value">{{ coilDetail.supplierCoilNo || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">状态</text>
|
||||
<text class="item-value">{{ getStatusText(coilDetail.status) }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">毛重 (吨)</text>
|
||||
<text class="item-value">{{ coilDetail.grossWeight || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">净重 (吨)</text>
|
||||
<text class="item-value">{{ coilDetail.netWeight || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">逻辑库区</text>
|
||||
<text class="item-value">{{ coilDetail.warehouseName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">真实库区</text>
|
||||
<text class="item-value">{{ coilDetail.actualWarehouseName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">班组</text>
|
||||
<text class="item-value">{{ coilDetail.team || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">物料类型</text>
|
||||
<text class="item-value">{{ coilDetail.materialType || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">品名</text>
|
||||
<text class="item-value">{{ coilDetail.itemName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">规格</text>
|
||||
<text class="item-value">{{ coilDetail.specification || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">材质</text>
|
||||
<text class="item-value">{{ coilDetail.material || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="item-label">厂家</text>
|
||||
<text class="item-value">{{ coilDetail.manufacturer || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
|
||||
<!-- ✅ 新增:查询悬浮按钮 - search视图显示 -->
|
||||
<view class="float-btn search-btn" v-show="currentView === 'search'" @click="searchCoilList">
|
||||
<text class="btn-text">查询</text>
|
||||
</view>
|
||||
|
||||
<!-- ✅ 新增:重新查找悬浮按钮 - list视图显示 -->
|
||||
<view class="float-btn back-btn" v-show="currentView === 'list'" @click="backToSearch">
|
||||
<text class="btn-text">重新查找</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getMaterialCoil, listMaterialCoil } from '@/api/wms/coil';
|
||||
import { addPendingAction } from '@/api/wms/pendingAction';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 补全表单所有绑定字段的初始值,解决双向绑定警告
|
||||
form: {
|
||||
enterCoilNo: '',
|
||||
currentCoilNo: '',
|
||||
team: '',
|
||||
warehouseId: '',
|
||||
actualWarehouseId: '',
|
||||
itemType: 'product',
|
||||
itemId: '',
|
||||
materialType: '',
|
||||
itemName: '',
|
||||
itemSpecification: '',
|
||||
itemMaterial: '',
|
||||
itemManufacturer: '',
|
||||
grossWeight: '',
|
||||
netWeight: '',
|
||||
length: '',
|
||||
},
|
||||
pager: {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
total: 0,
|
||||
coilDetail: {}, // 钢卷详情数据
|
||||
list: [], // 钢卷列表数据
|
||||
loading: false,
|
||||
currentView: 'search' // 视图切换:search=查询页,list=列表页
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 计算总页数
|
||||
totalPages() {
|
||||
if (this.total === 0) return 0;
|
||||
return Math.ceil(this.total / this.pager.pageSize);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// ✅ 新增:封装通用滚动到顶部方法,uniapp全端兼容
|
||||
scrollToPageTop() {
|
||||
uni.pageScrollTo({
|
||||
scrollTop: 0,
|
||||
duration: 300, // 顺滑滚动,毫秒值
|
||||
fail: () => {} // 容错处理,防止报错
|
||||
})
|
||||
},
|
||||
|
||||
// 获取设备信息(原有方法不变)
|
||||
getDeviceInfo() {
|
||||
try {
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
return `${systemInfo.platform} ${systemInfo.model}`;
|
||||
} catch (e) {
|
||||
return 'Unknown Device';
|
||||
}
|
||||
},
|
||||
|
||||
// 查询钢卷列表(原有方法优化+✅新增滚动到顶部)
|
||||
async searchCoilList() {
|
||||
// 重置页码为1
|
||||
this.pager.pageNum = 1;
|
||||
|
||||
uni.showLoading({
|
||||
title: '正在查询,请稍后'
|
||||
});
|
||||
try {
|
||||
const res = await listMaterialCoil({
|
||||
...this.form,
|
||||
...this.pager,
|
||||
selectType: this.form.itemType
|
||||
});
|
||||
this.list = res.rows || [];
|
||||
this.total = res.total || 0;
|
||||
console.log(this.list)
|
||||
this.currentView = 'list'
|
||||
// ✅ 切换视图后,滚动到页面顶部
|
||||
this.scrollToPageTop()
|
||||
} catch (err) {
|
||||
console.error('查询钢卷列表失败:', err);
|
||||
uni.showToast({
|
||||
title: '查询失败,检查网络后重试',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
} finally {
|
||||
uni.hideLoading()
|
||||
}
|
||||
},
|
||||
|
||||
// 上一页
|
||||
async prevPage() {
|
||||
if (this.pager.pageNum <= 1) return;
|
||||
this.pager.pageNum -= 1;
|
||||
await this.loadPageData();
|
||||
},
|
||||
|
||||
// 下一页
|
||||
async nextPage() {
|
||||
if (this.pager.pageNum >= this.totalPages) return;
|
||||
this.pager.pageNum += 1;
|
||||
await this.loadPageData();
|
||||
},
|
||||
|
||||
// 加载指定页码的数据
|
||||
async loadPageData() {
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
});
|
||||
try {
|
||||
const res = await listMaterialCoil({
|
||||
...this.form,
|
||||
...this.pager,
|
||||
selectType: this.form.itemType
|
||||
});
|
||||
this.list = res.rows || [];
|
||||
// 滚动到列表顶部
|
||||
this.scrollToPageTop();
|
||||
} catch (err) {
|
||||
console.error('加载分页数据失败:', err);
|
||||
uni.showToast({
|
||||
title: '加载失败,请重试',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
} finally {
|
||||
uni.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
// 回到查询页面(原有方法+✅新增滚动到顶部)
|
||||
backToSearch() {
|
||||
this.currentView = 'search';
|
||||
// 可选:重置表单数据,如需保留查询条件则注释此行
|
||||
// this.form = this.$options.data().form;
|
||||
// ✅ 切换视图后,滚动到页面顶部
|
||||
this.scrollToPageTop()
|
||||
},
|
||||
|
||||
// 点击列表项-展示钢卷详情弹窗
|
||||
showCoilDetail(item) {
|
||||
this.coilDetail = { ...item }; // 深拷贝防止原数据被修改
|
||||
this.$refs.shipPopup.open(); // 打开底部弹窗
|
||||
},
|
||||
|
||||
// 关闭弹窗
|
||||
closePopup() {
|
||||
this.$refs.shipPopup.close();
|
||||
},
|
||||
|
||||
// 格式化状态文本:0=在库 1=已出库
|
||||
getStatusText(status) {
|
||||
return status === 0 ? '在库' : status === 1 ? '已出库' : '未知';
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.typing-container {
|
||||
padding: 20rpx;
|
||||
padding-bottom: 30rpx;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
// 列表页样式补充
|
||||
.list-wrap {
|
||||
// ✅ 移除原按钮样式,无需保留
|
||||
.empty-list {
|
||||
text-align: center;
|
||||
padding: 100rpx 0;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
// 分页样式
|
||||
.pagination {
|
||||
margin-top: 40rpx;
|
||||
padding: 20rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
|
||||
.pagination-info {
|
||||
display: flex;
|
||||
gap: 30rpx;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
|
||||
text {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-btns {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
|
||||
.page-btn {
|
||||
width: 160rpx;
|
||||
height: 70rpx;
|
||||
line-height: 70rpx;
|
||||
background: #f0f7ff;
|
||||
border: 1rpx solid #007aff;
|
||||
border-radius: 8rpx;
|
||||
color: #007aff;
|
||||
font-size: 28rpx;
|
||||
|
||||
&:disabled {
|
||||
background: #f5f5f5;
|
||||
border-color: #ddd;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 核心新增:悬浮按钮样式(全局通用)
|
||||
.float-btn {
|
||||
position: fixed;
|
||||
right: 30rpx;
|
||||
bottom: 60rpx;
|
||||
z-index: 9999; // 置顶层级,永不遮挡
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.15);
|
||||
transition: all 0.2s ease;
|
||||
&:active {
|
||||
transform: scale(0.95); // 按压缩放动效
|
||||
opacity: 0.9;
|
||||
}
|
||||
.btn-text {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
// 查询悬浮按钮-主色调(渐变蓝,和原有按钮风格一致)
|
||||
.search-btn {
|
||||
background: linear-gradient(135deg, #007aff 0%, #0051d5 100%);
|
||||
}
|
||||
// 重新查找悬浮按钮-次要色调(灰色系,区分查询按钮)
|
||||
.back-btn {
|
||||
background: linear-gradient(135deg, #666 0%, #333 100%);
|
||||
}
|
||||
|
||||
// 弹窗样式补充
|
||||
.popup-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.mb-20 {margin-bottom: 20rpx;}
|
||||
.font-32 {font-size:32rpx;}
|
||||
.font-40 {font-size:40rpx;}
|
||||
.font-bold {font-weight: bold;}
|
||||
.icon-close {color:#999;}
|
||||
|
||||
/* 扫码区域 */
|
||||
.scan-section {
|
||||
padding: 80rpx 40rpx;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||
|
||||
.scan-btn-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 30rpx;
|
||||
|
||||
.scan-icon {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #007aff 0%, #0051d5 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 122, 255, 0.3);
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
.icon-camera {
|
||||
font-size: 70rpx;
|
||||
}
|
||||
|
||||
.scan-tip {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 卡片样式 */
|
||||
.info-card,
|
||||
.material-card,
|
||||
.form-card {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
/* 警告卡片 */
|
||||
.warning-card {
|
||||
background: #fff2f0;
|
||||
border: 2rpx solid #ffccc7;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
|
||||
.warning-icon {
|
||||
font-size: 48rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.warning-content {
|
||||
flex: 1;
|
||||
|
||||
.warning-title {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #cf1322;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.warning-desc {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #a8071a;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 25rpx;
|
||||
padding-bottom: 20rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
.title-dot {
|
||||
width: 8rpx;
|
||||
height: 28rpx;
|
||||
background: #007aff;
|
||||
border-radius: 4rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
flex: 1;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
font-size: 22rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 12rpx;
|
||||
margin-right: 10rpx;
|
||||
|
||||
&.status-1 {
|
||||
background: #d1f2eb;
|
||||
color: #0c6957;
|
||||
}
|
||||
|
||||
&.status-0 {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
}
|
||||
}
|
||||
|
||||
.more-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
background: #f0f7ff;
|
||||
border-radius: 20rpx;
|
||||
border: 1rpx solid #007aff;
|
||||
|
||||
.icon-more {
|
||||
font-size: 24rpx;
|
||||
color: #007aff;
|
||||
}
|
||||
|
||||
.more-text {
|
||||
font-size: 24rpx;
|
||||
color: #007aff;
|
||||
}
|
||||
}
|
||||
} // ✅ 修复:补全card-title的闭合大括号
|
||||
|
||||
/* 信息网格 */
|
||||
.info-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20rpx;
|
||||
|
||||
.info-item {
|
||||
flex: 1;
|
||||
min-width: 45%;
|
||||
background: #f8f9fa;
|
||||
padding: 20rpx;
|
||||
border-radius: 12rpx;
|
||||
|
||||
&.full-width {
|
||||
flex: 0 0 100%;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 表单项 */
|
||||
.form-item {
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
&:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 15rpx;
|
||||
font-weight: 500;
|
||||
|
||||
// &::before {
|
||||
// content: '*';
|
||||
// color: #ff4d4f;
|
||||
// margin-right: 6rpx;
|
||||
// }
|
||||
}
|
||||
|
||||
.form-label-optional {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 15rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
padding: 0 24rpx;
|
||||
background: #f8f9fa;
|
||||
border: 2rpx solid #e8e8e8;
|
||||
border-radius: 12rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
box-sizing: border-box;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:focus {
|
||||
background: #fff;
|
||||
border-color: #007aff;
|
||||
}
|
||||
|
||||
&.form-input-disabled {
|
||||
background: #f5f5f5;
|
||||
color: #999;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 操作者信息 */
|
||||
.operator-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20rpx 0;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.operator-label {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.operator-name {
|
||||
font-size: 26rpx;
|
||||
color: #007aff;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
/* 操作按钮 */
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-top: 30rpx;
|
||||
|
||||
.btn {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
border-radius: 12rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #f5f5f5;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #007aff 0%, #0051d5 100%);
|
||||
color: #fff;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 122, 255, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
/* BOM弹窗 */
|
||||
.bom-popup {
|
||||
background: #fff;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
max-height: 70vh;
|
||||
|
||||
.popup-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 30rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
.popup-title {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.popup-close {
|
||||
font-size: 40rpx;
|
||||
color: #999;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-body {
|
||||
padding: 30rpx;
|
||||
max-height: 500rpx;
|
||||
|
||||
.bom-item {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
padding: 24rpx;
|
||||
background: #f8f9fa;
|
||||
border-radius: 12rpx;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bom-index {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
background: #007aff;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.bom-content {
|
||||
flex: 1;
|
||||
|
||||
.bom-row {
|
||||
display: flex;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bom-label {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
min-width: 120rpx;
|
||||
}
|
||||
|
||||
.bom-value {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
padding: 60rpx 0;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
BIN
apps/hand-factory/static/images/tabbar/fahuo.png
Normal file
BIN
apps/hand-factory/static/images/tabbar/fahuo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
BIN
apps/hand-factory/static/images/tabbar/fahuo_.png
Normal file
BIN
apps/hand-factory/static/images/tabbar/fahuo_.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
BIN
apps/hand-factory/static/images/tabbar/meal.png
Normal file
BIN
apps/hand-factory/static/images/tabbar/meal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
BIN
apps/hand-factory/static/images/tabbar/meal_.png
Normal file
BIN
apps/hand-factory/static/images/tabbar/meal_.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.1 KiB |
BIN
apps/hand-factory/static/images/tabbar/search.png
Normal file
BIN
apps/hand-factory/static/images/tabbar/search.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
BIN
apps/hand-factory/static/images/tabbar/search_.png
Normal file
BIN
apps/hand-factory/static/images/tabbar/search_.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
@@ -7,13 +7,17 @@ import {
|
||||
} from "@/utils/validate"
|
||||
import {
|
||||
login,
|
||||
loginZinc1,
|
||||
logout,
|
||||
getInfo
|
||||
} from '@/api/login'
|
||||
import {
|
||||
getToken,
|
||||
setToken,
|
||||
removeToken
|
||||
removeToken,
|
||||
getZinc1Token,
|
||||
setZinc1Token,
|
||||
removeZinc1Token
|
||||
} from '@/utils/auth'
|
||||
import defAva from '@/static/images/avatar.png'
|
||||
|
||||
@@ -56,25 +60,41 @@ const user = {
|
||||
},
|
||||
|
||||
actions: {
|
||||
// 登录
|
||||
Login({
|
||||
commit
|
||||
}, userInfo) {
|
||||
const username = userInfo.username.trim()
|
||||
const password = userInfo.password
|
||||
const code = userInfo.code
|
||||
const uuid = userInfo.uuid
|
||||
return new Promise((resolve, reject) => {
|
||||
login(username, password, code, uuid).then(res => {
|
||||
console.log('token', res)
|
||||
setToken(res.data.token)
|
||||
commit('SET_TOKEN', res.data.token)
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
// 登录(主系统登录成功即算登录成功,Zinc1系统静默登录)
|
||||
Login({
|
||||
commit
|
||||
}, userInfo) {
|
||||
const username = userInfo.username.trim()
|
||||
const password = userInfo.password
|
||||
const code = userInfo.code
|
||||
const uuid = userInfo.uuid
|
||||
return new Promise((resolve, reject) => {
|
||||
// 先执行主系统登录
|
||||
login(username, password, code, uuid).then(mainRes => {
|
||||
// 主系统登录成功,立即保存token并resolve
|
||||
if (mainRes && mainRes.data && mainRes.data.token) {
|
||||
setToken(mainRes.data.token)
|
||||
commit('SET_TOKEN', mainRes.data.token)
|
||||
}
|
||||
|
||||
// 主系统登录成功后,静默执行Zinc1登录(不等待结果,不显示任何错误)
|
||||
loginZinc1(username, password, code, uuid).then(zinc1Res => {
|
||||
// Zinc1登录成功,静默保存token
|
||||
if (zinc1Res && zinc1Res.data && zinc1Res.data.token) {
|
||||
setZinc1Token(zinc1Res.data.token)
|
||||
}
|
||||
}).catch(() => {
|
||||
// Zinc1登录失败,完全静默处理,不做任何提示
|
||||
})
|
||||
|
||||
// 主系统登录成功即返回
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
// 只有主系统登录失败才reject
|
||||
reject(error)
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
// 获取用户信息
|
||||
GetInfo({
|
||||
@@ -106,24 +126,25 @@ const user = {
|
||||
})
|
||||
},
|
||||
|
||||
// 退出系统
|
||||
LogOut({
|
||||
commit,
|
||||
state
|
||||
}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
logout(state.token).then(() => {
|
||||
commit('SET_TOKEN', '')
|
||||
commit('SET_ROLES', [])
|
||||
commit('SET_PERMISSIONS', [])
|
||||
removeToken()
|
||||
storage.clean()
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
// 退出系统
|
||||
LogOut({
|
||||
commit,
|
||||
state
|
||||
}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
logout(state.token).then(() => {
|
||||
commit('SET_TOKEN', '')
|
||||
commit('SET_ROLES', [])
|
||||
commit('SET_PERMISSIONS', [])
|
||||
removeToken()
|
||||
removeZinc1Token() // 同时清除Zinc1 token
|
||||
storage.clean()
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"uni-load-more.contentdown": "上拉显示更多",
|
||||
"uni-load-more.contentdown": "点击加载更多",
|
||||
"uni-load-more.contentrefresh": "正在加载...",
|
||||
"uni-load-more.contentnomore": "没有更多数据了"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const TokenKey = 'App-Token'
|
||||
const Zinc1TokenKey = 'App-Zinc1-Token'
|
||||
|
||||
export function getToken() {
|
||||
return uni.getStorageSync(TokenKey)
|
||||
@@ -11,3 +12,16 @@ export function setToken(token) {
|
||||
export function removeToken() {
|
||||
return uni.removeStorageSync(TokenKey)
|
||||
}
|
||||
|
||||
// Zinc1系统的token管理
|
||||
export function getZinc1Token() {
|
||||
return uni.getStorageSync(Zinc1TokenKey)
|
||||
}
|
||||
|
||||
export function setZinc1Token(token) {
|
||||
return uni.setStorageSync(Zinc1TokenKey, token)
|
||||
}
|
||||
|
||||
export function removeZinc1Token() {
|
||||
return uni.removeStorageSync(Zinc1TokenKey)
|
||||
}
|
||||
|
||||
@@ -20,9 +20,7 @@ const request = config => {
|
||||
url = url.slice(0, -1)
|
||||
config.url = url
|
||||
}
|
||||
|
||||
console.log('请求参数[' + config.method + config.url + ']', config)
|
||||
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
method: config.method || 'get',
|
||||
|
||||
@@ -13,50 +13,142 @@ export function createMeasureSocket({
|
||||
} = {}) {
|
||||
let socket = null
|
||||
let manualClose = false
|
||||
// 保存回调函数的引用,允许清空
|
||||
let callbacks = {
|
||||
onOpen,
|
||||
onClose,
|
||||
onError,
|
||||
onMessage
|
||||
}
|
||||
|
||||
const wsBase = (config.wsUrl || config.baseUrl || '').replace(/^http/, 'ws')
|
||||
const url = `${wsBase}/websocket?type=${type}`
|
||||
console.log(url)
|
||||
|
||||
function connect() {
|
||||
manualClose = false
|
||||
socket = new WebSocket(url)
|
||||
|
||||
socket.onopen = () => {
|
||||
onOpen && onOpen()
|
||||
// 使用 uni.connectSocket 建立连接(部分运行环境可能不支持 WebSocket,会直接抛异常)
|
||||
try {
|
||||
socket = uni.connectSocket({
|
||||
url,
|
||||
success() {
|
||||
console.log('connectSocket 调用成功,等待 onOpen 回调')
|
||||
},
|
||||
fail(err) {
|
||||
console.error('connectSocket 调用失败:', err)
|
||||
callbacks.onError && callbacks.onError(err)
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('connectSocket 执行异常(可能环境不支持 WebSocket):', err)
|
||||
callbacks.onError && callbacks.onError(err)
|
||||
return
|
||||
}
|
||||
|
||||
socket.onmessage = (evt) => {
|
||||
onMessage && onMessage(evt.data)
|
||||
if (!socket) {
|
||||
console.error('connectSocket 未返回有效 socketTask')
|
||||
return
|
||||
}
|
||||
|
||||
socket.onerror = (err) => {
|
||||
onError && onError(err)
|
||||
}
|
||||
// 正确的事件注册方式:socketTask.onOpen / onMessage / onError / onClose
|
||||
socket.onOpen((res) => {
|
||||
console.log('WebSocket 已打开', res)
|
||||
// 检查回调是否已被清空
|
||||
if (callbacks && callbacks.onOpen) {
|
||||
try {
|
||||
callbacks.onOpen(res)
|
||||
} catch (e) {
|
||||
// 忽略回调错误
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
socket.onclose = (evt) => {
|
||||
onClose && onClose(evt)
|
||||
if (!manualClose) {
|
||||
socket.onMessage((evt) => {
|
||||
// H5 为 evt.data,小程序为 evt.data,统一兼容
|
||||
const data = evt && (evt.data || evt)
|
||||
// 检查回调是否已被清空
|
||||
if (callbacks && callbacks.onMessage) {
|
||||
try {
|
||||
callbacks.onMessage(data)
|
||||
} catch (e) {
|
||||
// 忽略回调错误,可能是组件已销毁
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
socket.onError((err) => {
|
||||
console.error('WebSocket 发生错误:', err)
|
||||
// 检查回调是否已被清空
|
||||
if (callbacks && callbacks.onError) {
|
||||
try {
|
||||
callbacks.onError(err)
|
||||
} catch (e) {
|
||||
// 忽略回调错误
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
socket.onClose((evt) => {
|
||||
console.log('WebSocket 已关闭', evt)
|
||||
// 检查回调是否已被清空
|
||||
if (callbacks && callbacks.onClose) {
|
||||
try {
|
||||
callbacks.onClose(evt)
|
||||
} catch (e) {
|
||||
// 忽略回调错误
|
||||
}
|
||||
}
|
||||
if (!manualClose && callbacks) {
|
||||
setTimeout(connect, 3000)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function close() {
|
||||
manualClose = true
|
||||
// 清空所有回调,防止在关闭过程中或关闭后触发回调
|
||||
callbacks = {
|
||||
onOpen: null,
|
||||
onClose: null,
|
||||
onError: null,
|
||||
onMessage: null
|
||||
}
|
||||
if (socket) {
|
||||
socket.close(1000, 'client close')
|
||||
try {
|
||||
socket.close(1000, 'client close')
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
socket = null
|
||||
}
|
||||
}
|
||||
|
||||
function send(data) {
|
||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(JSON.stringify(data));
|
||||
} else {
|
||||
console.error('WebSocket is not open. Cannot send message.');
|
||||
if (!socket) {
|
||||
console.error('WebSocket 未初始化,无法发送消息')
|
||||
return
|
||||
}
|
||||
|
||||
// uni-app 的 socketTask.send 使用对象形式
|
||||
socket.send({
|
||||
data: JSON.stringify(data),
|
||||
fail(err) {
|
||||
console.error('发送 WebSocket 消息失败:', err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 清空回调函数的方法
|
||||
function clearCallbacks() {
|
||||
callbacks = {
|
||||
onOpen: null,
|
||||
onClose: null,
|
||||
onError: null,
|
||||
onMessage: null
|
||||
}
|
||||
}
|
||||
|
||||
return { connect, close, send }
|
||||
return { connect, close, send, clearCallbacks }
|
||||
}
|
||||
|
||||
|
||||
@@ -73,13 +73,13 @@ function checkStorageSpace() {
|
||||
function checkUpdate(forceCheck = false) {
|
||||
// 1. 准备本地版本信息
|
||||
const localVersion = plus.runtime.version; // 基座版本
|
||||
const staticVersion = '1.0.0'; // 静态默认版本
|
||||
const staticVersion = '1.3.30'; // 静态默认版本
|
||||
// const localWgtVersion = staticVersion;
|
||||
const localWgtVersion = uni.getStorageSync('wgtVersion') || staticVersion; // 本地wgt版本(从存储获取或用默认)
|
||||
const currentVersion = compareVersion(localWgtVersion, localVersion) > 0
|
||||
? localWgtVersion
|
||||
: localVersion; // 当前有效版本(取两者较高者)
|
||||
|
||||
|
||||
// 2. 请求远程版本信息
|
||||
uni.request({
|
||||
url: `${baseURL}/version.json?t=${Date.now()}`, // 加时间戳防缓存
|
||||
@@ -151,24 +151,24 @@ function checkUpdate(forceCheck = false) {
|
||||
|
||||
// 显示更新提示模态框
|
||||
function showUpdateModal(remoteVersion, wgtUrl) {
|
||||
// uni.showModal({
|
||||
// title: '发现新版本',
|
||||
// content: `检测到新版本(${remoteVersion}),是否立即下载并更新?`,
|
||||
// confirmText: '立即更新',
|
||||
// cancelText: '退出',
|
||||
// showCancel: true,
|
||||
// success: (modalRes) => {
|
||||
// if (modalRes.confirm) {
|
||||
// // 用户确认更新:检查存储空间 -> 下载 -> 安装
|
||||
// handleConfirmUpdate(wgtUrl, remoteVersion);
|
||||
// } else {
|
||||
// // 直接退出
|
||||
uni.showModal({
|
||||
title: '发现新版本',
|
||||
content: `检测到新版本(${remoteVersion}),是否立即下载并更新?`,
|
||||
confirmText: '立即更新',
|
||||
cancelText: '暂不更新',
|
||||
showCancel: true,
|
||||
success: (modalRes) => {
|
||||
if (modalRes.confirm) {
|
||||
// 用户确认更新:检查存储空间 -> 下载 -> 安装
|
||||
handleConfirmUpdate(wgtUrl, remoteVersion);
|
||||
} else {
|
||||
// 直接退出
|
||||
|
||||
// // 用户取消更新:询问是否忽略该版本
|
||||
// handleCancelUpdate(remoteVersion);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// 用户取消更新:询问是否忽略该版本
|
||||
// handleCancelUpdate(remoteVersion);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
70
apps/hand-factory/utils/zinc1Request.js
Normal file
70
apps/hand-factory/utils/zinc1Request.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import store from '@/store'
|
||||
import config from '@/config'
|
||||
import { getZinc1Token } from '@/utils/auth'
|
||||
import errorCode from '@/utils/errorCode'
|
||||
import { toast, showConfirm, tansParams } from '@/utils/common'
|
||||
|
||||
let timeout = 10000
|
||||
// 固定使用 zinc1 的 baseUrl
|
||||
const baseUrl = 'http://140.143.206.120:10082/prod-api'
|
||||
|
||||
const zinc1Request = config => {
|
||||
// 是否需要设置 token
|
||||
const isToken = (config.header || {}).isToken === false
|
||||
config.header = config.header || {}
|
||||
// 使用Zinc1系统的token
|
||||
if (getZinc1Token() && !isToken) {
|
||||
config.header['Authorization'] = 'Bearer ' + getZinc1Token()
|
||||
}
|
||||
// get请求映射params参数
|
||||
if (config.params) {
|
||||
let url = config.url + '?' + tansParams(config.params)
|
||||
url = url.slice(0, -1)
|
||||
config.url = url
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
method: config.method || 'get',
|
||||
timeout: config.timeout || timeout,
|
||||
url: baseUrl + config.url,
|
||||
data: config.data,
|
||||
header: config.header,
|
||||
dataType: 'json'
|
||||
}).then(response => {
|
||||
let [error, res] = response
|
||||
if (error) {
|
||||
toast('后端接口连接异常')
|
||||
reject('后端接口连接异常')
|
||||
return
|
||||
}
|
||||
const code = res.data.code || 200
|
||||
const msg = errorCode[code] || res.data.msg || errorCode['default']
|
||||
if (code === 401) {
|
||||
// Zinc1系统是可选的,如果返回401,静默处理,不显示任何提示
|
||||
reject('Zinc1系统登录状态已过期')
|
||||
} else if (code === 500) {
|
||||
toast(msg)
|
||||
reject('500')
|
||||
} else if (code !== 200) {
|
||||
toast(msg)
|
||||
reject(code)
|
||||
}
|
||||
resolve(res.data)
|
||||
})
|
||||
.catch(error => {
|
||||
let { message } = error
|
||||
if (message === 'Network Error') {
|
||||
message = '后端接口连接异常'
|
||||
} else if (message.includes('timeout')) {
|
||||
message = '系统接口请求超时'
|
||||
} else if (message.includes('Request failed with status code')) {
|
||||
message = '系统接口' + message.substr(message.length - 3) + '异常'
|
||||
}
|
||||
toast(message)
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export default zinc1Request
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "klp 1.3.17",
|
||||
"version": "klp 1.3.30",
|
||||
"wgtUrl": "http://49.232.154.205:10900/fadapp-update/klp/klp.wgt",
|
||||
"apkUrl": "http://49.232.154.205:10900/fadapp-update/klp/klp.apk"
|
||||
}
|
||||
@@ -54,6 +54,7 @@
|
||||
"screenfull": "5.0.2",
|
||||
"sortablejs": "1.10.2",
|
||||
"splitpanes": "2.4.1",
|
||||
"three": "^0.158.0",
|
||||
"vue": "2.6.12",
|
||||
"vue-count-to": "1.0.13",
|
||||
"vue-cropper": "0.5.5",
|
||||
|
||||
BIN
apps/l2/public/models/光整机.glb
Normal file
BIN
apps/l2/public/models/光整机.glb
Normal file
Binary file not shown.
BIN
apps/l2/public/models/剪切机.glb
Normal file
BIN
apps/l2/public/models/剪切机.glb
Normal file
Binary file not shown.
BIN
apps/l2/public/models/卷取机.glb
Normal file
BIN
apps/l2/public/models/卷取机.glb
Normal file
Binary file not shown.
BIN
apps/l2/public/models/夹送矫直机.glb
Normal file
BIN
apps/l2/public/models/夹送矫直机.glb
Normal file
Binary file not shown.
BIN
apps/l2/public/models/开卷机.glb
Normal file
BIN
apps/l2/public/models/开卷机.glb
Normal file
Binary file not shown.
BIN
apps/l2/public/models/拉轿机.glb
Normal file
BIN
apps/l2/public/models/拉轿机.glb
Normal file
Binary file not shown.
BIN
apps/l2/public/models/清洗段.glb
Normal file
BIN
apps/l2/public/models/清洗段.glb
Normal file
Binary file not shown.
BIN
apps/l2/public/models/炉火段.glb
Normal file
BIN
apps/l2/public/models/炉火段.glb
Normal file
Binary file not shown.
BIN
apps/l2/public/models/锌锅.glb
Normal file
BIN
apps/l2/public/models/锌锅.glb
Normal file
Binary file not shown.
@@ -29,6 +29,14 @@ export function listPlan(query) {
|
||||
})
|
||||
}
|
||||
|
||||
export function getL3PickupRecommend(limit = 10) {
|
||||
return l2Request({
|
||||
url: '/api/pdi/l3PickupRecommend',
|
||||
method: 'get',
|
||||
params: { limit }
|
||||
})
|
||||
}
|
||||
|
||||
// 查询计划详细
|
||||
export function getPlan(coilId) {
|
||||
return l2Request({
|
||||
|
||||
278
apps/l2/src/config/productionLine.js
Normal file
278
apps/l2/src/config/productionLine.js
Normal file
@@ -0,0 +1,278 @@
|
||||
// 产线配置数据(替代后端 API)
|
||||
// 轨道节点配置说明:
|
||||
// trackNodes: 轨道节点数组,按顺序连接
|
||||
// 每个节点包含:
|
||||
// id: 节点唯一标识
|
||||
// name: 节点名称(可选,用于显示)
|
||||
// position: { x, y, z } 节点位置坐标
|
||||
// width: 轨道宽度(可选,默认1.2)
|
||||
// waypoints: 中间路径点数组(可选),用于定义从该节点到下一个节点的中间转弯点
|
||||
// 每个waypoint包含:{ x, y, z } 位置坐标
|
||||
// 示例:waypoints: [{ x: 10, y: 0.5, z: 5 }, { x: 15, y: 0.5, z: 5 }]
|
||||
// 轨道会依次经过:节点 -> waypoint1 -> waypoint2 -> ... -> 下一个节点
|
||||
export const productionLineConfig = {
|
||||
name: "产线展示",
|
||||
description: "基于节点的轨道系统,可手动调节每个节点的位置和高度。",
|
||||
// 运动扁钢(压钢带)参数
|
||||
steelFlow: {
|
||||
count: 4, // 扁钢数量(同时在轨道上运动的条数)
|
||||
length: 8, // 每条扁钢长度(沿轨道方向)
|
||||
widthRatio: 0.85, // 扁钢宽度=轨道宽度*比例
|
||||
thickness: 0.10, // 扁钢厚度
|
||||
speed: 6 // 运动速度(数值越大越快)
|
||||
},
|
||||
// 轨道节点配置 - 按顺序连接,轨道会平滑通过这些节点
|
||||
// 可以在节点之间添加 waypoints 来定义中间转弯点
|
||||
trackNodes: [
|
||||
{ id: "node_1", name: "起点", position: { x: 5.0, y: 5.0, z: 0.0 }, width: 1.8 },
|
||||
{ id: "node_2", name: "1#开卷机后", position: { x: 23.0, y: 5.2, z: 0.0 }, width: 1.2 },
|
||||
{ id: "node_3", name: "2#开卷机前", position: { x: 28.0, y: 5.2, z: 0.0 }, width: 1.2 },
|
||||
{ id: "node_4", name: "2#开卷机后", position: { x: 42.0, y: 5.2, z: 0.0 }, width: 1.2 },
|
||||
{
|
||||
id: "node_5",
|
||||
name: "夹送矫直前",
|
||||
position: { x: 46, y: 6.0, z: 0.0 },
|
||||
width: 1.2,
|
||||
// 示例:从上一个节点到当前节点,经过多个转弯点
|
||||
// waypoints: [
|
||||
// { x: -13.0, y: 3.0, z: 0.0 }, // 第一个转弯点
|
||||
// { x: -12.5, y: 4.5, z: 0.0 } // 第二个转弯点
|
||||
// ]
|
||||
},
|
||||
{ id: "node_6", name: "夹送矫直后", position: { x:55.0, y: 5.5, z: 0.0 }, width: 1.2 },
|
||||
{
|
||||
id: "node_7",
|
||||
name: "清洗段前",
|
||||
position: { x: 64, y: 2, z: 1.6 },
|
||||
width: 1.2,
|
||||
// 从清洗段前到清洗段后的中间转弯点
|
||||
// 轨道会依次经过:node_7 -> waypoint1 -> waypoint2 -> ... -> node_8
|
||||
waypoints: [
|
||||
// 在这里添加转弯点,例如:
|
||||
{ x: 70, y: 2.0, z: 0.0 }, // 转弯点1
|
||||
{ x: 72, y: 2.0, z: 13.0 }, // 转弯点2
|
||||
// { x: 90, y: 0.6, z: 12.0 } // 转弯点3
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "node_8",
|
||||
name: "清洗段后",
|
||||
position: { x: 92, y: 0.6, z: 13.6 },
|
||||
width: 1.2,
|
||||
// 示例:清洗段内部可能有多次转弯
|
||||
waypoints: [
|
||||
// { x: -2.0, y: 0.6, z: 5.0 }, // 转弯点1
|
||||
// { x: 0.0, y: 0.6, z: 9.0 }, // 转弯点2
|
||||
// { x: 1.0, y: 0.6, z: 11.5 } // 转弯点3
|
||||
]
|
||||
},
|
||||
{ id: "node_9", name: "炉火段前", position: { x:92, y:3, z: 2.2 }, width: 1.2 },
|
||||
{ id: "node_10", name: "炉火段后", position: { x: 92, y: 3, z: 2.2 }, width: 1.2,
|
||||
waypoints: [
|
||||
{ x: 92.0, y:5, z: 0.0 }, // 转弯点1
|
||||
{ x: 92.0, y: 5, z: -10.0 }, // 转弯点2
|
||||
// { x: 1.0, y: 0.6, z: 11.5 } // 转弯点3
|
||||
]
|
||||
},
|
||||
{ id: "node_11", name: "锌锅前", position: { x: 109, y: 3, z: -10.0 }, width: 1.2 },
|
||||
{ id: "node_12", name: "锌锅后", position: { x: 109, y:7, z: 4.0 }, width: 1.2,
|
||||
waypoints: [
|
||||
{ x: 109.0, y: 5, z: 13.0 }, // 转弯点1
|
||||
]
|
||||
|
||||
},
|
||||
{ id: "node_13", name: "光整机前",position: { x: 127.0, y: 5, z: 15.0 }, width: 1.2 },
|
||||
{ id: "node_14", name: "光整机后", position: { x: 130.0, y: 5, z: 15.0 }, width: 1.2 },
|
||||
{ id: "node_15", name: "拉矫机前", position: { x: 134.0, y: 7, z: 15.0 }, width: 1.2 },
|
||||
{ id: "node_16", name: "拉矫机后", position: { x: 142.0, y: 7, z: 15.0 }, width: 1.2 },
|
||||
{ id: "node_17", name: "剪切机前", position: { x:148.0, y: 5, z: 15.0 }, width: 1.2 },
|
||||
{ id: "node_18", name: "剪切机后", position: { x: 158.0, y: 5, z: 15.0 }, width: 1.2 },
|
||||
{ id: "node_19", name: "卷取机前", position: { x: 160.0, y: 7, z: 15.0 }, width: 1.2 },
|
||||
{ id: "node_20", name: "终点", position: { x: 165.0, y: 7, z: 15.0 }, width: 1.2 },
|
||||
{ id: "node_21", name: "结束", position: { x: 170.0, y: 2, z: 15.0 }, width: 1.2 },
|
||||
],
|
||||
// 设备配置(独立于轨道)
|
||||
models: [
|
||||
// 入口段(示例:开卷机可复用两次)
|
||||
{
|
||||
id: "unjcoiler_1",
|
||||
name: "1#开卷机",
|
||||
deviceCode: "POR1",
|
||||
file: "/models/开卷机.glb",
|
||||
position: { x: -24.0, y: 0.0, z: 0.0 },
|
||||
io: {
|
||||
entryDir: "-x",
|
||||
exitDir: "+x",
|
||||
leadOut:2,
|
||||
leadIn: 0,
|
||||
entryOffset: { y: 0 },
|
||||
exitOffset: { y: 1.2 },
|
||||
conveyor: { enabled: true, yOffset: 5.5, width: 1.2 }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "unjcoiler_2",
|
||||
name: "2#开卷机",
|
||||
deviceCode: "POR2",
|
||||
file: "/models/开卷机.glb",
|
||||
position: { x: -18.0, y: 0.0, z: 0.0 },
|
||||
io: {
|
||||
entryDir: "-x",
|
||||
exitDir: "+x",
|
||||
leadOut: 6,
|
||||
leadIn: 1,
|
||||
entryOffset: { y: 0.2 },
|
||||
exitOffset: { y: 0.2 },
|
||||
conveyor: { enabled: true, yOffset: 5.5, width: 1.2 }
|
||||
}
|
||||
},
|
||||
|
||||
// 夹送矫直
|
||||
{
|
||||
id: "pinch_leveler",
|
||||
name: "夹送矫直机",
|
||||
// 暂无对应的实时设备编码,可后续按需补充
|
||||
file: "/models/夹送矫直机.glb",
|
||||
position: { x: -12.0, y: 0.0, z: 0.0 },
|
||||
rotation: { x: 0.0, y: 3.1415926, z: 0.0 },
|
||||
io: {
|
||||
entryDir: "-x",
|
||||
exitDir: "+x",
|
||||
leadOut: 0,
|
||||
leadIn: 2.0,
|
||||
entryOffset: { y: 0.25 },
|
||||
exitOffset: { y: 0.25 },
|
||||
rampLength: 0,
|
||||
conveyor: { enabled: true, yOffset: 6, width: 1.2 }
|
||||
}
|
||||
},
|
||||
|
||||
// 清洗/炉火
|
||||
{
|
||||
id: "clean",
|
||||
name: "清洗段",
|
||||
deviceCode: "CLEAN",
|
||||
file: "/models/清洗段.glb",
|
||||
position: { x: -6, y: 0.0, z: 5.6 },
|
||||
io: {
|
||||
entryDir: "-x",
|
||||
exitDir: "+x",
|
||||
leadOut: 8.0,
|
||||
leadIn: 0,
|
||||
entryOffset: { y: 0, z:-4},
|
||||
exitOffset: { y: 0, z: 8 },
|
||||
rampLength: 4.0,
|
||||
conveyor: { enabled: false, yOffset: 0.6, zOffset: 0, width: 1.2 }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "furnace",
|
||||
name: "炉火段",
|
||||
deviceCode: "FUR1",
|
||||
file: "/models/炉火段.glb",
|
||||
position: { x: 0.0, y: 0.0, z: 2.2 },
|
||||
io: {
|
||||
entryDir: "-x",
|
||||
exitDir: "+x",
|
||||
leadOut: 2.0,
|
||||
leadIn: 2.0,
|
||||
entryOffset: { y: 0.25 },
|
||||
exitOffset: { y: 0.25 },
|
||||
rampLength: 3.5,
|
||||
conveyor: { enabled: true, yOffset: 0.25, width: 1.2 }
|
||||
}
|
||||
},
|
||||
|
||||
// 锌锅/后处理
|
||||
{
|
||||
id: "pot",
|
||||
name: "锌锅",
|
||||
deviceCode: "POT",
|
||||
file: "/models/锌锅.glb",
|
||||
position: { x: 6.0, y: 0.0, z: 0.0 },
|
||||
io: {
|
||||
entryDir: "-x",
|
||||
exitDir: "+z",
|
||||
leadOut: 1.8,
|
||||
leadIn: 2.2,
|
||||
entryOffset: { y: 0.35 },
|
||||
exitOffset: { y: 0.35, x: -0.6 },
|
||||
rampLength: 3.0,
|
||||
conveyor: { enabled: true, yOffset: 0.35, width: 1.2 }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "skinpass",
|
||||
name: "光整机",
|
||||
deviceCode: "TM",
|
||||
file: "/models/光整机.glb",
|
||||
position: { x: 6.0, y: 0.0, z: 18.0 },
|
||||
rotation: { x: 0.0, y: Math.PI / 2, z: 0.0 },
|
||||
io: {
|
||||
entryDir: "-z",
|
||||
exitDir: "+z",
|
||||
leadOut: 2.2,
|
||||
leadIn: 2.2,
|
||||
entryOffset: { y: 0.25 },
|
||||
exitOffset: { y: 0.25 },
|
||||
rampLength: 3.5,
|
||||
conveyor: { enabled: true, yOffset: 0.25, width: 1.2 }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "bridle",
|
||||
name: "拉矫机",
|
||||
deviceCode: "TL",
|
||||
file: "/models/拉轿机.glb",
|
||||
rotation: { x: 0.0, y: Math.PI / 2, z: 0.0 },
|
||||
position: { x: 8.0, y: 0.0, z: 15.0 },
|
||||
io: {
|
||||
entryDir: "-z",
|
||||
exitDir: "+x",
|
||||
leadOut: 2.0,
|
||||
leadIn: 2.0,
|
||||
entryOffset: { y: 0.25 },
|
||||
exitOffset: { y: 0.25 },
|
||||
rampLength: 3.0,
|
||||
conveyor: { enabled: true, yOffset: 0.25, width: 1.2 }
|
||||
}
|
||||
},
|
||||
|
||||
// 剪切/卷取
|
||||
{
|
||||
id: "shear",
|
||||
name: "剪切机",
|
||||
deviceCode: "EXC",
|
||||
file: "/models/剪切机.glb",
|
||||
position: { x: 16.0, y: 0.0, z: 15.0 },
|
||||
rotation: { x: 0.0, y: Math.PI / 2, z: 0.0 },
|
||||
io: {
|
||||
entryDir: "-x",
|
||||
exitDir: "+x",
|
||||
leadOut: 2.0,
|
||||
leadIn: 2.0,
|
||||
entryOffset: { y: 0.3 },
|
||||
exitOffset: { y: 0.3 },
|
||||
rampLength: 3.0,
|
||||
conveyor: { enabled: true, yOffset: 0.3, width: 1.2 }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "coiler",
|
||||
name: "卷取机",
|
||||
deviceCode: "TR",
|
||||
file: "/models/卷取机.glb",
|
||||
position: { x: 24.0, y: 0.0, z: 22.0 },
|
||||
rotation: { x: 0.0, y: Math.PI / 2, z: 0.0 },
|
||||
io: {
|
||||
entryDir: "-x",
|
||||
exitDir: "+x",
|
||||
leadOut: 2.0,
|
||||
leadIn: 2.0,
|
||||
entryOffset: { y: 0.3 },
|
||||
exitOffset: { y: 0.3 },
|
||||
conveyor: { enabled: true, yOffset: 0.3, width: 1.2 }
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -57,7 +57,7 @@ $theme-text-gray: #a1a6af;
|
||||
// 仅维护组件自身样式,无定位属性
|
||||
.current-time {
|
||||
padding: 1.2vw 1.8vw;
|
||||
background: white;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
backdrop-filter: blur(8px);
|
||||
border: 1px solid rgba(167, 172, 180, 0.3);
|
||||
border-radius: 8px;
|
||||
@@ -91,4 +91,4 @@ $theme-text-gray: #a1a6af;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
<template>
|
||||
<div class="industrial-dashboard">
|
||||
<!-- Header: 系统标题和当前时间 -->
|
||||
<div class="dashboard-header">
|
||||
<div class="header-left">
|
||||
<h1 class="system-title">生产监控大屏</h1>
|
||||
<p class="system-subtitle">实时生产数据与设备状态</p>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<CurrentTime />
|
||||
<!-- 顶部:产线三维展示(时间显示放入3D组件内部) -->
|
||||
<div class="production-line-section">
|
||||
<div class="production-line-view">
|
||||
<ProductionLine />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -82,9 +78,9 @@
|
||||
<span>系统告警信息</span>
|
||||
<el-badge :value="alarmData.length" class="alarm-badge" v-if="alarmData.length > 0"></el-badge>
|
||||
</div>
|
||||
<MiniTable
|
||||
<MiniTable
|
||||
v-loading="tableLoading"
|
||||
:columns="alarmColumns"
|
||||
:columns="alarmColumns"
|
||||
:data="alarmData"
|
||||
:highlightOnRowClick="true"
|
||||
tableHeight="280px"
|
||||
@@ -96,7 +92,7 @@
|
||||
<div slot="header" class="card-header">
|
||||
<span>换辊信息</span>
|
||||
</div>
|
||||
<MiniTable
|
||||
<MiniTable
|
||||
v-loading="rollHistoryLoading"
|
||||
:columns="rollHistoryColumns"
|
||||
:data="rollHistoryData"
|
||||
@@ -116,7 +112,7 @@
|
||||
<i class="el-icon-arrow-right"></i>
|
||||
</el-button>
|
||||
</div>
|
||||
<MiniTable
|
||||
<MiniTable
|
||||
v-loading="planLoading"
|
||||
:columns="planColumns"
|
||||
:data="planData"
|
||||
@@ -137,7 +133,7 @@
|
||||
<i class="el-icon-arrow-right"></i>
|
||||
</el-button>
|
||||
</div>
|
||||
<TrackMeasure
|
||||
<TrackMeasure
|
||||
v-loading="measureLoading"
|
||||
:columns="measureColumns"
|
||||
:data="measureData"
|
||||
@@ -155,9 +151,9 @@
|
||||
<span>快速访问</span>
|
||||
</div>
|
||||
<div class="quick-access-grid">
|
||||
<div
|
||||
class="access-item"
|
||||
v-for="(card, index) in featureCards"
|
||||
<div
|
||||
class="access-item"
|
||||
v-for="(card, index) in featureCards"
|
||||
:key="index"
|
||||
@click="$router.push(card.path)"
|
||||
>
|
||||
@@ -177,14 +173,14 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CurrentTime from "./components/CurrentTime.vue";
|
||||
import HomeMain from "./components/HomeMain.vue";
|
||||
import MiniTable from "./components/MiniTable.vue";
|
||||
import ProductionLine from "./l2/productLine/ProductLine.vue";
|
||||
// 引入日志API / 生产相关API
|
||||
import { getLogDataPage } from "@/api/l2/log";
|
||||
import { getLogDataPage } from "@/api/l2/log";
|
||||
import { getRollHistorytList } from '@/api/l2/roller'
|
||||
import { listPlan } from "@/api/l2/plan";
|
||||
import TrackMeasure from "@/components/TrackMeasure/index.vue";
|
||||
@@ -192,7 +188,7 @@ import { getCurrentProducingPlan, getCurrentProcessParams } from "@/api/business
|
||||
|
||||
export default {
|
||||
name: "Index",
|
||||
components: { CurrentTime, HomeMain, MiniTable, TrackMeasure },
|
||||
components: { HomeMain, MiniTable, TrackMeasure, ProductionLine },
|
||||
data() {
|
||||
return {
|
||||
// KPI指标数据
|
||||
@@ -431,30 +427,16 @@ $theme-text-gray: #c9cdcf;
|
||||
min-height: calc(100vh - 60px);
|
||||
}
|
||||
|
||||
/* 仪表板头部 */
|
||||
.dashboard-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
/* 顶部产线展示区 */
|
||||
.production-line-section {
|
||||
margin-bottom: 20px;
|
||||
padding: 20px;
|
||||
background: #ffffff; // 简洁白色背景,避免低级渐变色
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.header-left {
|
||||
.system-title {
|
||||
color: #000;
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.system-subtitle {
|
||||
color: #000;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
.production-line-view {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -707,4 +689,4 @@ $theme-text-gray: #c9cdcf;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
<!-- 数据表格 -->
|
||||
<el-table v-loading="tableLoading" :data="tableData" border style="width: 100%; margin-top: 20px;"
|
||||
@row-click="handleRowClick" highlight-current-row>
|
||||
<el-table-column prop="seqid" label="序号" width="80" align="center"></el-table-column>
|
||||
<el-table-column type="index" label="序号" width="80" align="center"></el-table-column>
|
||||
<el-table-column prop="timestamp" label="发生时间" width="180" align="center"></el-table-column>
|
||||
<el-table-column prop="module" label="报警模块" align="center">
|
||||
<template slot-scope="scope">
|
||||
@@ -120,7 +120,7 @@ export default {
|
||||
logtext: '',
|
||||
status: '',
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
pageSize: 50
|
||||
},
|
||||
// 表格数据
|
||||
tableData: [],
|
||||
|
||||
@@ -18,6 +18,12 @@
|
||||
<el-input v-model="formData.entryMatId"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 入场钢卷号 -->
|
||||
<el-col :span="8">
|
||||
<el-form-item label="入场钢卷号" prop="enterCoilNo">
|
||||
<el-input v-model="formData.enterCoilNo"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 分切数 -->
|
||||
<el-col :span="8">
|
||||
<el-form-item label="分切数" prop="subId">
|
||||
|
||||
@@ -53,22 +53,13 @@ export default {
|
||||
{ label: '带钢速度', value: 'stripSpeed' },
|
||||
{ label: '1#开卷机张力', value: 'tensionPorBr1' },
|
||||
{ label: '2#开卷机张力', value: 'tensionPorBr2' },
|
||||
{ label: '清洗电压', value: 'cleaningVoltage' },
|
||||
{ label: '清洗电流', value: 'cleaningCurrent' },
|
||||
{ label: '碱液浓度', value: 'alkaliConcentration' },
|
||||
{ label: '碱液温度', value: 'alkaliTemperature' },
|
||||
{ label: '预热段出口板温', value: 'phfExitStripTemp' },
|
||||
{ label: '加热段出口板温', value: 'rtfExitStripTemp' },
|
||||
{ label: '冷却段出口板温', value: 'jcsExitStripTemp' },
|
||||
{ label: '均热段出口板温', value: 'scsExitStripTemp' },
|
||||
{ label: '锌锅温度', value: 'potTemperature' },
|
||||
{ label: '锌锅功率', value: 'zincPotPower' },
|
||||
{ label: '燃气消耗量', value: 'gasConsumption' },
|
||||
{ label: '冷却塔带钢温度', value: 'coolingTowerStripTemp' },
|
||||
{ label: '光整机张力', value: 'tensionBr5Tm' },
|
||||
{ label: '光整机出口速度', value: 'stripSpeedTmExit' },
|
||||
{ label: '拉矫机延伸率', value: 'tlElongation' },
|
||||
{ label: '拉矫机张力', value: 'tensionTlBr7' }
|
||||
{ label: 'BR1-BR2张力', value: 'tensionBr1Br2' },
|
||||
{ label: 'BR2-BR3张力', value: 'tensionBr2Br3' },
|
||||
{ label: '入口活套套量', value: 'celCapacity' },
|
||||
{ label: '出口活套套量', value: 'cxlCapacity' },
|
||||
{ label: 'PH炉温度', value: 'phFurnaceTemperatureActual' },
|
||||
{ label: 'NOF1炉温度', value: 'nof1FurnaceTemperatureActual' },
|
||||
{ label: 'NOF1炉控制输出', value: 'nof1FurnaceTemperatureControlOutput' }
|
||||
],
|
||||
treeProps: {
|
||||
children: 'children',
|
||||
|
||||
@@ -37,9 +37,10 @@
|
||||
|
||||
<!-- 卡片网格布局 -->
|
||||
<div v-loading="tableLoading">
|
||||
<el-table :data="tableData" style="width: 100%" border stripe @row-click="handleRowClick" highlight-current-row height="300px">
|
||||
<el-table :data="tableData" style="width: 100%" border stripe @row-click="handleRowClick" highlight-current-row>
|
||||
<el-table-column prop="exitMatId" label="成品卷号"></el-table-column>
|
||||
<el-table-column prop="entryMatId" label="来料卷号"></el-table-column>
|
||||
<el-table-column prop="enterCoilNo" label="入场钢卷号"></el-table-column>
|
||||
<el-table-column prop="planNo" label="计划单号"></el-table-column>
|
||||
|
||||
<el-table-column prop="status" label="状态"></el-table-column>
|
||||
@@ -74,6 +75,18 @@
|
||||
<div v-if="tableData.length === 0 && !tableLoading" class="empty-data">
|
||||
<el-empty description="暂无数据"></el-empty>
|
||||
</div>
|
||||
<!-- 分页组件 -->
|
||||
<el-pagination
|
||||
v-if="total > 0"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="queryForm.pageNum"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:page-size="queryForm.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
style="margin-top: 15px; text-align: right;"
|
||||
></el-pagination>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -143,10 +156,17 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'basicInfo',
|
||||
queryForm: { coilid: '', startDate: '', endDate: '' },
|
||||
queryForm: {
|
||||
coilid: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
pageNum: 1,
|
||||
pageSize: 20
|
||||
},
|
||||
printOpen: false,
|
||||
certificateVisible: false, // 质保书对话框显示状态
|
||||
tableData: [],
|
||||
total: 0, // 总记录数
|
||||
tableLoading: false,
|
||||
btnLoading: false,
|
||||
dialogVisible: false,
|
||||
@@ -171,27 +191,51 @@ export default {
|
||||
this.getPdoList()
|
||||
},
|
||||
methods: {
|
||||
// 获取列表数据(保持不变)
|
||||
// 获取列表数据(支持分页)
|
||||
getPdoList() {
|
||||
this.tableLoading = true
|
||||
getPdoList(this.queryForm).then(res => {
|
||||
this.tableData = res.data.map(item => ({ ...item, deleteLoading: false }))
|
||||
if (res.code === 200) {
|
||||
// 处理分页数据
|
||||
this.tableData = (res.rows || res.data || []).map(item => ({ ...item, deleteLoading: false }))
|
||||
this.total = res.total || 0
|
||||
} else {
|
||||
this.$message.error(res.msg || '获取数据失败')
|
||||
this.tableData = []
|
||||
this.total = 0
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(err)
|
||||
this.$message.error('获取数据失败')
|
||||
this.tableData = []
|
||||
this.total = 0
|
||||
}).finally(() => {
|
||||
this.tableLoading = false
|
||||
this.btnLoading = false
|
||||
})
|
||||
},
|
||||
// 分页大小改变
|
||||
handleSizeChange(val) {
|
||||
this.queryForm.pageSize = val
|
||||
this.queryForm.pageNum = 1 // 重置到第一页
|
||||
this.getPdoList()
|
||||
},
|
||||
// 当前页改变
|
||||
handleCurrentChange(val) {
|
||||
this.queryForm.pageNum = val
|
||||
this.getPdoList()
|
||||
},
|
||||
// 查询(保持不变)
|
||||
handleQuery() {
|
||||
this.btnLoading = true;
|
||||
this.queryForm.pageNum = 1; // 重置到第一页
|
||||
this.getPdoList()
|
||||
},
|
||||
// 重置(保持不变)
|
||||
handleReset() {
|
||||
this.$refs.queryForm.resetFields();
|
||||
this.queryForm.pageNum = 1;
|
||||
this.queryForm.pageSize = 20;
|
||||
this.getPdoList()
|
||||
},
|
||||
// 行点击(保持不变)
|
||||
@@ -209,6 +253,7 @@ export default {
|
||||
this.dialogTitle = '新增实绩';
|
||||
// 初始化空表单数据(传递给子组件)
|
||||
this.formData = {
|
||||
enterCoilNo: '',
|
||||
subId: 0,
|
||||
startPosition: 0,
|
||||
endPosition: 0,
|
||||
|
||||
113
apps/l2/src/views/l2/plan/components/L3PickupRecommendPanel.vue
Normal file
113
apps/l2/src/views/l2/plan/components/L3PickupRecommendPanel.vue
Normal file
@@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<div class="l3-pickup-recommend-panel">
|
||||
<div class="section-header">
|
||||
<div>
|
||||
<div class="section-title">三级领料推荐</div>
|
||||
<div class="section-subtitle">来自L3待处理领料队列(actionType=501, actionStatus=0)</div>
|
||||
</div>
|
||||
<el-button type="text" size="mini" @click="fetchList">刷新</el-button>
|
||||
</div>
|
||||
|
||||
<div class="section-body" v-loading="loading">
|
||||
<div v-if="recommendations.length" class="card-list">
|
||||
<div
|
||||
v-for="item in recommendations"
|
||||
:key="item.actionId"
|
||||
class="recommend-card"
|
||||
@click="apply(item)"
|
||||
>
|
||||
<div class="card-title">{{ item.coilId || '-' }}</div>
|
||||
<div class="card-meta">
|
||||
<span>入场钢卷号:{{ item.enterCoilNo || '-' }}</span>
|
||||
<span>优先级:{{ item.priority != null ? item.priority : '-' }}</span>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<span>来源coilId:{{ item.sourceCoilId || '-' }}</span>
|
||||
<span>扫码时间:{{ formatDate(item.scanTime) }}</span>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<span>点击应用到左侧表单</span>
|
||||
<i class="el-icon-top-right" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-else description="暂无三级领料推荐" :image-size="90" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { parseTime } from '@/utils/ruoyi'
|
||||
import { getL3PickupRecommend } from '@/api/l2/plan'
|
||||
|
||||
export default {
|
||||
name: 'L3PickupRecommendPanel',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
recommendations: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.fetchList()
|
||||
},
|
||||
methods: {
|
||||
async fetchList() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await getL3PickupRecommend(10)
|
||||
this.recommendations = Array.isArray(res.data) ? res.data : []
|
||||
} catch (e) {
|
||||
console.error('fetch l3 pickup recommend error:', e)
|
||||
this.$message?.error('获取三级领料推荐失败')
|
||||
this.recommendations = []
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
apply(item) {
|
||||
this.$emit('apply-recommendation', {
|
||||
coilid: item.coilId || '',
|
||||
enterCoilNo: item.enterCoilNo || ''
|
||||
})
|
||||
},
|
||||
formatDate(v) {
|
||||
if (!v) return '-'
|
||||
return parseTime(v, '{y}-{m}-{d} {h}:{i}:{s}')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.l3-pickup-recommend-panel {
|
||||
background: #f9fafc;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
}
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.section-title { font-weight: 600; color: #303133; }
|
||||
.section-subtitle { font-size: 12px; color: #909399; }
|
||||
.section-body { max-height: 220px; overflow-y: auto; }
|
||||
.card-list { display: grid; gap: 8px; }
|
||||
.recommend-card {
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.recommend-card:hover {
|
||||
border-color: #409eff;
|
||||
box-shadow: 0 6px 18px rgba(64, 158, 255, 0.15);
|
||||
}
|
||||
.card-title { font-weight: 600; color: #303133; margin-bottom: 6px; }
|
||||
.card-meta { display: flex; justify-content: space-between; font-size: 12px; color: #606266; margin-bottom: 2px; }
|
||||
.card-footer { display: flex; justify-content: space-between; font-size: 12px; color: #909399; margin-top: 4px; }
|
||||
</style>
|
||||
@@ -1,16 +1,17 @@
|
||||
<template>
|
||||
<div class="plan-recommend-panel">
|
||||
<!-- 钢种推荐 -->
|
||||
<div class="recommend-section steel-panel">
|
||||
<div class="section-header">
|
||||
<div>
|
||||
<div class="section-title">钢种推荐</div>
|
||||
<div class="section-subtitle">锁定钢种后自动调取面板库中的典型工艺主数据</div>
|
||||
<div class="section-subtitle">根据钢种查询历史工艺参数组合</div>
|
||||
</div>
|
||||
<el-tag v-if="steelGradeName" size="mini" effect="dark">{{ steelGradeName }}</el-tag>
|
||||
</div>
|
||||
<div class="section-body" v-loading="loadingSteel">
|
||||
<template v-if="steelRecommendations.length">
|
||||
<div class="card-list two-cols">
|
||||
<div class="card-list">
|
||||
<div
|
||||
class="recommend-card"
|
||||
v-for="item in steelRecommendations"
|
||||
@@ -18,27 +19,13 @@
|
||||
@click="emitSteelGrade(item)"
|
||||
>
|
||||
<div class="card-header">
|
||||
<div class="card-title">
|
||||
{{ item.steelGradeLabel || '钢种' }}
|
||||
</div>
|
||||
<el-tag size="mini" type="info">
|
||||
厚度 {{ formatNumber(item.entryThick) }} mm
|
||||
</el-tag>
|
||||
<div class="card-title">{{ item.steelGradeLabel || '钢种' }}</div>
|
||||
<el-tag size="mini" type="info">厚度 {{ formatNumber(item.thick) }} mm</el-tag>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<div>
|
||||
<span class="label">屈服点</span>
|
||||
<span class="value">{{ formatNumber(item.yieldPoint) }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="label">延伸率</span>
|
||||
<span class="value">{{ formatNumber(item.spmElongation) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<div>
|
||||
<span class="label">轧制力</span>
|
||||
<span class="value">{{ formatNumber(item.spmRollforce) }}</span>
|
||||
<span class="label">屈服强度</span>
|
||||
<span class="value">{{ formatNumber(item.yieldStren) }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="label">更新时间</span>
|
||||
@@ -46,169 +33,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<span>点击写入钢种及主键</span>
|
||||
<span>点击应用此组合</span>
|
||||
<i class="el-icon-top-right" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-empty
|
||||
v-else
|
||||
description="选择钢种后即可联动查询"
|
||||
:image-size="100"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="recommend-section thickness-panel">
|
||||
<div class="section-header">
|
||||
<div>
|
||||
<div class="section-title">厚度/屈服点联动</div>
|
||||
<div class="section-subtitle">输入厚度后,自动搜集多面板库对应的屈服点档案</div>
|
||||
</div>
|
||||
<el-tag v-if="entryThick" size="mini" effect="plain">{{ formatNumber(entryThick) }} mm</el-tag>
|
||||
</div>
|
||||
<div class="section-body" v-loading="loadingThickness">
|
||||
<template v-if="thicknessRecommendations.length">
|
||||
<div class="card-list">
|
||||
<div
|
||||
class="recommend-card"
|
||||
v-for="item in thicknessRecommendations"
|
||||
:key="item._id"
|
||||
@click="emitThickness(item)"
|
||||
>
|
||||
<div class="card-header">
|
||||
<div class="card-title">
|
||||
{{ formatNumber(item.entryThick) }} mm
|
||||
</div>
|
||||
<el-tag size="mini" type="success">
|
||||
屈服点 {{ formatNumber(item.yieldPoint) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<div>
|
||||
<span class="label">钢种</span>
|
||||
<span class="value">{{ item.steelGradeLabel || '-' }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="label">数据来源</span>
|
||||
<span class="value">{{ item.sourceLabel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<span>{{ item.updateTime ? '最近更新 ' + formatDate(item.updateTime) : '点击写入厚度/屈服点' }}</span>
|
||||
<i class="el-icon-top-right" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-empty
|
||||
v-else
|
||||
description="填写厚度后可获取推荐屈服点"
|
||||
:image-size="100"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="recommend-section width-panel">
|
||||
<div class="section-header">
|
||||
<div>
|
||||
<div class="section-title">宽度/弯辊力联动</div>
|
||||
<div class="section-subtitle">根据宽度&轧制力,匹配弯辊力与宽度组合</div>
|
||||
</div>
|
||||
<el-tag v-if="entryWidth" size="mini" type="info">{{ formatNumber(entryWidth) }} mm</el-tag>
|
||||
</div>
|
||||
<div class="section-body" v-loading="loadingWidth">
|
||||
<template v-if="widthRecommendations.length">
|
||||
<div class="card-list">
|
||||
<div
|
||||
class="recommend-card"
|
||||
v-for="item in widthRecommendations"
|
||||
:key="item._id"
|
||||
@click="emitWidth(item)"
|
||||
>
|
||||
<div class="card-header">
|
||||
<div class="card-title">{{ formatNumber(item.width) }} mm</div>
|
||||
<el-tag size="mini" effect="plain">轧制力 {{ formatNumber(item.rollForce) }}</el-tag>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<div>
|
||||
<span class="label">弯辊力</span>
|
||||
<span class="value">{{ formatNumber(item.bendForce) }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="label">数据来源</span>
|
||||
<span class="value">{{ item.sourceLabel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<span>点击写入宽度/轧制力</span>
|
||||
<i class="el-icon-top-right" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-empty
|
||||
v-else
|
||||
description="填写宽度后可获取推荐数据"
|
||||
:image-size="100"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="recommend-section rollforce-panel">
|
||||
<div class="section-header">
|
||||
<div>
|
||||
<div class="section-title">轧制力反查</div>
|
||||
<div class="section-subtitle">输入轧制力后,反查对应厚度/屈服点组合</div>
|
||||
</div>
|
||||
<el-tag v-if="spmRollforce" size="mini" type="warning">{{ formatNumber(spmRollforce) }}</el-tag>
|
||||
</div>
|
||||
<div class="section-body" v-loading="loadingRollforce">
|
||||
<template v-if="rollforceFocusRecommendations.length">
|
||||
<div class="card-list">
|
||||
<div
|
||||
class="recommend-card"
|
||||
v-for="item in rollforceFocusRecommendations"
|
||||
:key="item._id"
|
||||
@click="emitRollforce(item)"
|
||||
>
|
||||
<div class="card-header">
|
||||
<div class="card-title">{{ formatNumber(item.spmRollforce) }}</div>
|
||||
<el-tag size="mini" type="success">{{ item.sourceLabel }}</el-tag>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<div>
|
||||
<span class="label">厚度</span>
|
||||
<span class="value">{{ formatNumber(item.entryThick) }} mm</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="label">屈服点</span>
|
||||
<span class="value">{{ formatNumber(item.yieldPoint) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-meta">
|
||||
<div>
|
||||
<span class="label">延伸率</span>
|
||||
<span class="value">{{ formatNumber(item.spmElongation) }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="label">钢种</span>
|
||||
<span class="value">{{ item.steelGradeLabel || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<span>点击写入相关字段</span>
|
||||
<i class="el-icon-top-right" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-empty
|
||||
v-else
|
||||
description="填写轧制力后可获取推荐数据"
|
||||
:image-size="100"
|
||||
/>
|
||||
<el-empty v-else description="选择钢种后即可联动查询" :image-size="100" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -216,9 +47,10 @@
|
||||
|
||||
<script>
|
||||
import { parseTime } from '@/utils/ruoyi'
|
||||
import { listRollforce } from '@/api/business/rollforce'
|
||||
import { listTension } from '@/api/business/tension'
|
||||
import { listBendforce } from '@/api/business/bendforce'
|
||||
import { listTensionAllLine } from '@/api/business/fullLineTension'
|
||||
import { listTensionLeveler } from '@/api/business/levelerTension'
|
||||
import { listTensionStraightener } from '@/api/business/straightenerTension'
|
||||
import { listTensionAnnealingFurnace } from '@/api/business/annealingFurnaceTension'
|
||||
|
||||
let uid = 0
|
||||
const createId = () => ++uid
|
||||
@@ -226,45 +58,20 @@ const createId = () => ++uid
|
||||
export default {
|
||||
name: 'PlanRecommendPanel',
|
||||
props: {
|
||||
steelGrade: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
},
|
||||
entryThick: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
},
|
||||
entryWidth: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
},
|
||||
spmRollforce: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
},
|
||||
steelGradeList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
steelGrade: { type: [String, Number], default: null },
|
||||
entryThick: { type: [String, Number], default: null },
|
||||
steelGradeList: { type: Array, default: () => [] }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
steelRecommendations: [],
|
||||
thicknessRecommendations: [],
|
||||
widthRecommendations: [],
|
||||
rollforceFocusRecommendations: [],
|
||||
loadingSteel: false,
|
||||
loadingThickness: false,
|
||||
loadingWidth: false,
|
||||
loadingRollforce: false,
|
||||
steelFetchToken: 0,
|
||||
thicknessFetchToken: 0,
|
||||
widthFetchToken: 0,
|
||||
rollforceFetchToken: 0,
|
||||
steelDebounceTimer: null,
|
||||
thicknessDebounceTimer: null,
|
||||
widthDebounceTimer: null,
|
||||
rollforceDebounceTimer: null
|
||||
thicknessDebounceTimer: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -276,92 +83,85 @@ export default {
|
||||
steelGrade: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
if (val === undefined || val === null || val === '') {
|
||||
if (!val) {
|
||||
this.steelRecommendations = []
|
||||
return
|
||||
}
|
||||
clearTimeout(this.steelDebounceTimer)
|
||||
this.steelDebounceTimer = setTimeout(() => {
|
||||
this.fetchSteelRecommendations()
|
||||
}, 200)
|
||||
this.steelDebounceTimer = setTimeout(() => this.fetchSteelRecommendations(), 200)
|
||||
}
|
||||
},
|
||||
entryThick: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
if (val === undefined || val === null || val === '') {
|
||||
if (!val) {
|
||||
this.thicknessRecommendations = []
|
||||
return
|
||||
}
|
||||
clearTimeout(this.thicknessDebounceTimer)
|
||||
this.thicknessDebounceTimer = setTimeout(() => {
|
||||
this.fetchThicknessRecommendations()
|
||||
}, 200)
|
||||
}
|
||||
},
|
||||
entryWidth: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
if (val === undefined || val === null || val === '') {
|
||||
this.widthRecommendations = []
|
||||
return
|
||||
}
|
||||
clearTimeout(this.widthDebounceTimer)
|
||||
this.widthDebounceTimer = setTimeout(() => {
|
||||
this.fetchWidthRecommendations()
|
||||
}, 200)
|
||||
}
|
||||
},
|
||||
spmRollforce: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
if (val === undefined || val === null || val === '') {
|
||||
this.rollforceFocusRecommendations = []
|
||||
return
|
||||
}
|
||||
clearTimeout(this.rollforceDebounceTimer)
|
||||
this.rollforceDebounceTimer = setTimeout(() => {
|
||||
this.fetchRollforceFocus()
|
||||
}, 200)
|
||||
this.thicknessDebounceTimer = setTimeout(() => this.fetchThicknessRecommendations(), 200)
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearTimeout(this.steelDebounceTimer)
|
||||
clearTimeout(this.thicknessDebounceTimer)
|
||||
clearTimeout(this.widthDebounceTimer)
|
||||
clearTimeout(this.rollforceDebounceTimer)
|
||||
},
|
||||
methods: {
|
||||
getSteelGradeLabel(gradeId) {
|
||||
if (gradeId === undefined || gradeId === null) return ''
|
||||
const gid = String(gradeId)
|
||||
getSteelGradeLabel(grade) {
|
||||
if (grade === undefined || grade === null) return ''
|
||||
const gradeStr = String(grade)
|
||||
const match = this.steelGradeList.find(
|
||||
item =>
|
||||
String(item.gradeid) === gid ||
|
||||
String(item.gradeId) === gid ||
|
||||
String(item.name) === gid ||
|
||||
String(item.gradeName || '') === gid
|
||||
(item) => String(item.gradeid) === gradeStr ||
|
||||
String(item.gradeId) === gradeStr ||
|
||||
String(item.name || '') === gradeStr ||
|
||||
String(item.gradeName || '') === gradeStr
|
||||
)
|
||||
return match ? (match.name || match.gradeName || gid) : gid
|
||||
return match ? match.name || match.gradeName : gradeStr
|
||||
},
|
||||
|
||||
async fetchAllTensionData(query) {
|
||||
const [allLineRes, levelerRes, straightenerRes, furnaceRes] = await Promise.allSettled([
|
||||
listTensionAllLine(query),
|
||||
listTensionLeveler(query),
|
||||
listTensionStraightener(query),
|
||||
listTensionAnnealingFurnace(query)
|
||||
])
|
||||
|
||||
const records = []
|
||||
if (allLineRes.status === 'fulfilled') {
|
||||
records.push(...(allLineRes.value.rows || []).map(r => ({ ...r, sourceLabel: '全线张力' })))
|
||||
}
|
||||
if (levelerRes.status === 'fulfilled') {
|
||||
records.push(...(levelerRes.value.rows || []).map(r => ({ ...r, sourceLabel: '平整机张力' })))
|
||||
}
|
||||
if (straightenerRes.status === 'fulfilled') {
|
||||
records.push(...(straightenerRes.value.rows || []).map(r => ({ ...r, sourceLabel: '矫直机张力' })))
|
||||
}
|
||||
if (furnaceRes.status === 'fulfilled') {
|
||||
records.push(...(furnaceRes.value.rows || []).map(r => ({ ...r, sourceLabel: '退火炉张力' })))
|
||||
}
|
||||
return records
|
||||
},
|
||||
|
||||
async fetchSteelRecommendations() {
|
||||
const token = ++this.steelFetchToken
|
||||
this.loadingSteel = true
|
||||
try {
|
||||
const gradeLabel = this.getSteelGradeLabel(this.steelGrade)
|
||||
const response = await listRollforce({
|
||||
const allRecords = await this.fetchAllTensionData({
|
||||
pageNum: 1,
|
||||
pageSize: 8,
|
||||
steelGrade: gradeLabel || this.steelGrade,
|
||||
gradeId: this.steelGrade
|
||||
pageSize: 50, // Fetch more to get variety
|
||||
steelGrade: gradeLabel || this.steelGrade
|
||||
})
|
||||
|
||||
if (token !== this.steelFetchToken) return
|
||||
const rows = Array.isArray(response.rows) ? response.rows : []
|
||||
this.steelRecommendations = rows.map(row => this.mapRollforceRow(row))
|
||||
|
||||
const mapped = allRecords.map(row => this.mapTensionRow(row))
|
||||
this.steelRecommendations = this.uniqueRecords(mapped, ['steelGrade', 'thick', 'yieldStren']).slice(0, 8)
|
||||
} catch (error) {
|
||||
console.error('fetchSteelRecommendations error:', error)
|
||||
this.$message?.error('钢种推荐获取失败,请稍后重试')
|
||||
this.$message?.error('钢种推荐获取失败')
|
||||
this.steelRecommendations = []
|
||||
} finally {
|
||||
if (token === this.steelFetchToken) {
|
||||
@@ -369,38 +169,24 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async fetchThicknessRecommendations() {
|
||||
const rawValue = this.entryThick
|
||||
if (rawValue === undefined || rawValue === null || rawValue === '') {
|
||||
this.thicknessRecommendations = []
|
||||
return
|
||||
}
|
||||
const token = ++this.thicknessFetchToken
|
||||
this.loadingThickness = true
|
||||
try {
|
||||
const query = {
|
||||
const allRecords = await this.fetchAllTensionData({
|
||||
pageNum: 1,
|
||||
pageSize: 8,
|
||||
thick: rawValue
|
||||
}
|
||||
const [rollforceRes, tensionRes] = await Promise.allSettled([
|
||||
listRollforce(query),
|
||||
listTension(query)
|
||||
])
|
||||
pageSize: 50,
|
||||
thick: this.entryThick
|
||||
})
|
||||
|
||||
if (token !== this.thicknessFetchToken) return
|
||||
const records = []
|
||||
if (rollforceRes.status === 'fulfilled') {
|
||||
const rows = Array.isArray(rollforceRes.value.rows) ? rollforceRes.value.rows : []
|
||||
records.push(...rows.map(row => this.mapRollforceRow(row, '光整机轧制力')))
|
||||
}
|
||||
if (tensionRes.status === 'fulfilled') {
|
||||
const rows = Array.isArray(tensionRes.value.rows) ? tensionRes.value.rows : []
|
||||
records.push(...rows.map(row => this.mapTensionRow(row)))
|
||||
}
|
||||
this.thicknessRecommendations = this.uniqueRecords(records).slice(0, 8)
|
||||
|
||||
const mapped = allRecords.map(row => this.mapTensionRow(row))
|
||||
this.thicknessRecommendations = this.uniqueRecords(mapped, ['steelGrade', 'thick', 'yieldStren']).slice(0, 8)
|
||||
} catch (error) {
|
||||
console.error('fetchThicknessRecommendations error:', error)
|
||||
this.$message?.error('厚度推荐获取失败,请稍后重试')
|
||||
this.$message?.error('厚度推荐获取失败')
|
||||
this.thicknessRecommendations = []
|
||||
} finally {
|
||||
if (token === this.thicknessFetchToken) {
|
||||
@@ -408,173 +194,53 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
async fetchWidthRecommendations() {
|
||||
const widthVal = Number(this.entryWidth)
|
||||
if (Number.isNaN(widthVal)) {
|
||||
this.widthRecommendations = []
|
||||
return
|
||||
}
|
||||
const token = ++this.widthFetchToken
|
||||
this.loadingWidth = true
|
||||
try {
|
||||
const response = await listBendforce({
|
||||
pageNum: 1,
|
||||
pageSize: 50,
|
||||
width: widthVal
|
||||
})
|
||||
if (token !== this.widthFetchToken) return
|
||||
const rows = Array.isArray(response.rows) ? response.rows : []
|
||||
const tolerance = Math.max(2, widthVal * 0.01)
|
||||
const matches = rows
|
||||
.filter(row => {
|
||||
const value = Number(row.width)
|
||||
return !Number.isNaN(value) && Math.abs(value - widthVal) <= tolerance
|
||||
})
|
||||
.map(row => this.mapBendforceRow(row))
|
||||
this.widthRecommendations = matches.slice(0, 6)
|
||||
} catch (error) {
|
||||
console.error('fetchWidthRecommendations error:', error)
|
||||
this.$message?.error('宽度推荐获取失败,请稍后重试')
|
||||
this.widthRecommendations = []
|
||||
} finally {
|
||||
if (token === this.widthFetchToken) {
|
||||
this.loadingWidth = false
|
||||
}
|
||||
}
|
||||
},
|
||||
async fetchRollforceFocus() {
|
||||
const rollforceVal = Number(this.spmRollforce)
|
||||
if (Number.isNaN(rollforceVal)) {
|
||||
this.rollforceFocusRecommendations = []
|
||||
return
|
||||
}
|
||||
const token = ++this.rollforceFetchToken
|
||||
this.loadingRollforce = true
|
||||
try {
|
||||
const response = await listRollforce({
|
||||
pageNum: 1,
|
||||
pageSize: 50,
|
||||
value1: rollforceVal
|
||||
})
|
||||
if (token !== this.rollforceFetchToken) return
|
||||
const rows = Array.isArray(response.rows) ? response.rows : []
|
||||
const tolerance = Math.max(5, rollforceVal * 0.02)
|
||||
const matches = rows
|
||||
.filter(row => {
|
||||
const value = Number(row.value1)
|
||||
return !Number.isNaN(value) && Math.abs(value - rollforceVal) <= tolerance
|
||||
})
|
||||
.map(row => this.mapRollforceByValueRow(row))
|
||||
this.rollforceFocusRecommendations = matches.slice(0, 6)
|
||||
} catch (error) {
|
||||
console.error('fetchRollforceFocus error:', error)
|
||||
this.$message?.error('轧制力推荐获取失败,请稍后重试')
|
||||
this.rollforceFocusRecommendations = []
|
||||
} finally {
|
||||
if (token === this.rollforceFetchToken) {
|
||||
this.loadingRollforce = false
|
||||
}
|
||||
}
|
||||
},
|
||||
mapRollforceRow(row, sourceLabel = '光整机轧制力') {
|
||||
const gradeLabel =
|
||||
row.steelGradeName ||
|
||||
row.steelGradeLabel ||
|
||||
this.getSteelGradeLabel(row.steelGrade) ||
|
||||
row.steelGrade
|
||||
return {
|
||||
_id: `rollforce-${row.id || createId()}`,
|
||||
sourceLabel,
|
||||
steelGrade: row.steelGrade,
|
||||
steelGradeLabel: gradeLabel,
|
||||
entryThick: row.thick,
|
||||
yieldPoint: row.yieldStren,
|
||||
spmElongation: row.elong,
|
||||
spmRollforce: row.value1,
|
||||
updateTime: row.updateTime || row.createTime
|
||||
}
|
||||
},
|
||||
mapRollforceByValueRow(row) {
|
||||
return {
|
||||
...this.mapRollforceRow(row, '轧制力面板'),
|
||||
_id: `rollforce-focus-${row.id || createId()}`,
|
||||
spmRollforce: row.value1
|
||||
}
|
||||
},
|
||||
|
||||
mapTensionRow(row) {
|
||||
const gradeLabel = row.steelGradeName || this.getSteelGradeLabel(row.steelGrade) || row.steelGrade
|
||||
const gradeLabel = this.getSteelGradeLabel(row.steelGrade)
|
||||
return {
|
||||
_id: `tension-${row.id || createId()}`,
|
||||
sourceLabel: '全线张力',
|
||||
sourceLabel: row.sourceLabel,
|
||||
steelGrade: row.steelGrade,
|
||||
steelGradeLabel: gradeLabel || '',
|
||||
entryThick: row.thick,
|
||||
yieldPoint: row.yieldStren,
|
||||
spmElongation: row.elong,
|
||||
spmRollforce: row.value1,
|
||||
steelGradeLabel: gradeLabel,
|
||||
thick: row.thick,
|
||||
yieldStren: row.yieldStren,
|
||||
updateTime: row.updateTime || row.createTime
|
||||
}
|
||||
},
|
||||
mapBendforceRow(row) {
|
||||
return {
|
||||
_id: `bendforce-${row.id || createId()}`,
|
||||
sourceLabel: '弯辊力面板',
|
||||
width: row.width,
|
||||
rollForce: row.rollForce,
|
||||
bendForce: row.value1,
|
||||
updateTime: row.updateTime || row.createTime
|
||||
}
|
||||
},
|
||||
uniqueRecords(records) {
|
||||
|
||||
uniqueRecords(records, keys) {
|
||||
const seen = new Set()
|
||||
return records.filter(item => {
|
||||
const key = [
|
||||
item.steelGrade || '',
|
||||
item.entryThick || '',
|
||||
item.yieldPoint || '',
|
||||
item.sourceLabel
|
||||
].join('|')
|
||||
const key = keys.map(k => item[k] || '').join('|')
|
||||
if (seen.has(key)) return false
|
||||
seen.add(key)
|
||||
return true
|
||||
})
|
||||
},
|
||||
|
||||
emitSteelGrade(item) {
|
||||
this.$emit('apply-steel-grade', {
|
||||
this.$emit('apply-recommendation', {
|
||||
steelGrade: item.steelGrade,
|
||||
entryThick: item.entryThick,
|
||||
yieldPoint: item.yieldPoint,
|
||||
spmElongation: item.spmElongation,
|
||||
spmRollforce: item.spmRollforce
|
||||
entryThick: item.thick,
|
||||
yieldPoint: item.yieldStren
|
||||
})
|
||||
},
|
||||
|
||||
emitThickness(item) {
|
||||
this.$emit('apply-thickness', {
|
||||
entryThick: item.entryThick,
|
||||
yieldPoint: item.yieldPoint
|
||||
})
|
||||
},
|
||||
emitWidth(item) {
|
||||
this.$emit('apply-width', {
|
||||
entryWidth: item.width,
|
||||
spmRollforce: item.rollForce
|
||||
})
|
||||
},
|
||||
emitRollforce(item) {
|
||||
this.$emit('apply-rollforce', {
|
||||
spmRollforce: item.spmRollforce,
|
||||
entryThick: item.entryThick,
|
||||
yieldPoint: item.yieldPoint,
|
||||
spmElongation: item.spmElongation,
|
||||
steelGrade: item.steelGrade
|
||||
this.$emit('apply-recommendation', {
|
||||
steelGrade: item.steelGrade,
|
||||
entryThick: item.thick,
|
||||
yieldPoint: item.yieldStren
|
||||
})
|
||||
},
|
||||
|
||||
formatNumber(value) {
|
||||
if (value === null || value === undefined || value === '') return '-'
|
||||
const num = Number(value)
|
||||
if (Number.isNaN(num)) return value
|
||||
return Number.isInteger(num) ? num.toString() : num.toFixed(2)
|
||||
},
|
||||
|
||||
formatDate(value) {
|
||||
if (!value) return '-'
|
||||
return parseTime(value, '{y}-{m}-{d}')
|
||||
@@ -586,7 +252,7 @@ export default {
|
||||
<style scoped>
|
||||
.plan-recommend-panel {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||
gap: 16px;
|
||||
height: 100%;
|
||||
padding-left: 16px;
|
||||
@@ -599,9 +265,6 @@ export default {
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -631,14 +294,8 @@ export default {
|
||||
}
|
||||
|
||||
.card-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.card-list.two-cols {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
@@ -671,31 +328,21 @@ export default {
|
||||
.card-title {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
flex: 1 1 100%;
|
||||
line-height: 1.4;
|
||||
word-break: break-all;
|
||||
}
|
||||
.card-header .el-tag {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.card-meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
justify-content: space-between;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.card-meta .label {
|
||||
color: #909399;
|
||||
margin-right: 4px;
|
||||
min-width: 54px;
|
||||
}
|
||||
|
||||
.card-meta .value {
|
||||
color: #303133;
|
||||
font-weight: 600;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
@@ -703,17 +350,7 @@ export default {
|
||||
color: #606266;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.steel-panel .card-list.two-cols {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.thickness-panel .card-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
</div>
|
||||
|
||||
<el-row v-else :gutter="16">
|
||||
<el-col :span="14">
|
||||
<el-form :model="form" label-width="150px" v-loading="loading">
|
||||
<el-row>
|
||||
<el-col>
|
||||
<el-form :model="form" v-loading="loading">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="开卷机张力">
|
||||
<el-input v-model="form.porTension" @change="syncModal" />
|
||||
@@ -48,7 +48,7 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="平整机入口张力">
|
||||
<el-input v-model="form.levelerEntryTension" @change="syncModal" />
|
||||
@@ -66,7 +66,7 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="炉区张力">
|
||||
<el-input v-model="form.furTension" @change="syncModal" />
|
||||
@@ -130,11 +130,14 @@ const REQUIRED_FIELDS = ['entryThick', 'yieldPoint', 'steelGrade']
|
||||
|
||||
const FIELD_LABEL_MAP = {
|
||||
entryThick: '入口厚度',
|
||||
yieldPoint: '屈服点',
|
||||
yieldPoint: '屈服强度',
|
||||
steelGrade: '钢种'
|
||||
}
|
||||
|
||||
const createDefaultForm = () => ({
|
||||
// 关键基础字段(用于后端入库/关联;同时不影响推荐计算)
|
||||
steelGrade: undefined,
|
||||
|
||||
// 全线张力
|
||||
porTension: undefined,
|
||||
celTension: undefined,
|
||||
@@ -217,7 +220,10 @@ export default {
|
||||
},
|
||||
generateSignature() {
|
||||
// 只要这些字段变化,就重新推荐
|
||||
return REQUIRED_FIELDS.map((key) => (this.income && this.income[key]) ?? '').join('|')
|
||||
return REQUIRED_FIELDS.map((key) => {
|
||||
const v = this.income && this.income[key]
|
||||
return v === null || v === undefined ? '' : v
|
||||
}).join('|')
|
||||
},
|
||||
async handleIncomeChange() {
|
||||
if (!this.canGenerate) {
|
||||
@@ -234,11 +240,11 @@ export default {
|
||||
|
||||
// 评分排序:优先 steelGrade 匹配,其次厚度差、屈服差、最后按更新时间
|
||||
scoreRow(row, cond) {
|
||||
const rowSteel = (row.steelGrade ?? row.steel_grade ?? '').toString()
|
||||
const rowSteel = ((row.steelGrade !== undefined && row.steelGrade !== null) ? row.steelGrade : (row.steel_grade !== undefined && row.steel_grade !== null ? row.steel_grade : '')).toString()
|
||||
const rowThick = Number(row.thick)
|
||||
const rowYield = Number(row.yieldStren ?? row.yield_stren)
|
||||
const rowYield = Number((row.yieldStren !== undefined && row.yieldStren !== null) ? row.yieldStren : row.yield_stren)
|
||||
|
||||
const condSteel = (cond.steelGrade ?? '').toString()
|
||||
const condSteel = ((cond.steelGrade === null || cond.steelGrade === undefined) ? '' : cond.steelGrade).toString()
|
||||
const condThick = Number(cond.thick)
|
||||
const condYield = Number(cond.yieldStren)
|
||||
|
||||
@@ -280,9 +286,7 @@ export default {
|
||||
// list 接口支持条件过滤的话会减少数据量(后端已支持三字段)
|
||||
steelGrade,
|
||||
thick: entryThick,
|
||||
yieldStren: yieldPoint,
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
yieldStren: yieldPoint
|
||||
}
|
||||
|
||||
const [allLineRes, levelerRes, straightenerRes, furnaceRes] = await Promise.allSettled([
|
||||
@@ -352,6 +356,11 @@ export default {
|
||||
this.syncModal()
|
||||
},
|
||||
syncModal() {
|
||||
// 注意:父组件保存工艺参数时需要基础字段(钢种/厚度/屈服强度)
|
||||
// 这里将 income 中的基础字段合并进 form,再回传给父组件
|
||||
this.$set(this.form, 'steelGrade', this.income && this.income.steelGrade)
|
||||
this.$set(this.form, 'thick', this.income && this.income.entryThick)
|
||||
this.$set(this.form, 'yieldStren', this.income && this.income.yieldPoint)
|
||||
this.$emit('input', this.form)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,22 +3,22 @@
|
||||
<el-tab-pane label="全线张力">
|
||||
<el-descriptions :column="2">
|
||||
<el-descriptions-item label="开卷机张力">
|
||||
{{ setupForm.porTension ?? '-' }}
|
||||
{{ setupForm.porTension == null ? '-' : setupForm.porTension }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="入口活套张力">
|
||||
{{ setupForm.celTension ?? '-' }}
|
||||
{{ setupForm.celTension == null ? '-' : setupForm.celTension }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="清洗段张力">
|
||||
{{ setupForm.cleanTension ?? '-' }}
|
||||
{{ setupForm.cleanTension == null ? '-' : setupForm.cleanTension }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="钝化段张力">
|
||||
{{ setupForm.passivationTension ?? '-' }}
|
||||
{{ setupForm.passivationTension == null ? '-' : setupForm.passivationTension }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="出口活套张力">
|
||||
{{ setupForm.cxlTension ?? '-' }}
|
||||
{{ setupForm.cxlTension == null ? '-' : setupForm.cxlTension }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="卷取机张力">
|
||||
{{ setupForm.trTension ?? '-' }}
|
||||
{{ setupForm.trTension == null ? '-' : setupForm.trTension }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
@@ -26,10 +26,10 @@
|
||||
<el-tab-pane label="平整机张力">
|
||||
<el-descriptions :column="1">
|
||||
<el-descriptions-item label="平整机入口张力">
|
||||
{{ setupForm.levelerEntryTension ?? '-' }}
|
||||
{{ setupForm.levelerEntryTension == null ? '-' : setupForm.levelerEntryTension }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="平整机出口张力">
|
||||
{{ setupForm.levelerExitTension ?? '-' }}
|
||||
{{ setupForm.levelerExitTension == null ? '-' : setupForm.levelerExitTension }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
@@ -37,7 +37,7 @@
|
||||
<el-tab-pane label="矫直机张力">
|
||||
<el-descriptions :column="1">
|
||||
<el-descriptions-item label="矫直机出口张力">
|
||||
{{ setupForm.straightenerExitTension ?? '-' }}
|
||||
{{ setupForm.straightenerExitTension == null ? '-' : setupForm.straightenerExitTension }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
@@ -45,10 +45,10 @@
|
||||
<el-tab-pane label="退火炉张力">
|
||||
<el-descriptions :column="1">
|
||||
<el-descriptions-item label="炉区张力">
|
||||
{{ setupForm.furTension ?? '-' }}
|
||||
{{ setupForm.furTension == null ? '-' : setupForm.furTension }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="冷却塔张力">
|
||||
{{ setupForm.towerTension ?? '-' }}
|
||||
{{ setupForm.towerTension == null ? '-' : setupForm.towerTension }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
@@ -200,11 +200,12 @@
|
||||
<el-descriptions-item label="计划号">{{ currentRow.planid }}</el-descriptions-item>
|
||||
<el-descriptions-item label="钢卷号">{{ currentRow.coilid }}</el-descriptions-item>
|
||||
<el-descriptions-item label="钢种">{{ currentRow.steelGrade }}</el-descriptions-item>
|
||||
<el-descriptions-item label="屈服点">{{ currentRow.yieldPoint }}</el-descriptions-item>
|
||||
<el-descriptions-item label="屈服强度">{{ currentRow.yieldPoint }}</el-descriptions-item>
|
||||
<el-descriptions-item label="重量(t)">{{ currentRow.entryWeight }}</el-descriptions-item>
|
||||
<el-descriptions-item label="厚度(mm)">{{ currentRow.entryThick }}</el-descriptions-item>
|
||||
<el-descriptions-item label="宽度(mm)">{{ currentRow.entryWidth }}</el-descriptions-item>
|
||||
<el-descriptions-item label="长度(mm)">{{ currentRow.entryLength }}</el-descriptions-item>
|
||||
<el-descriptions-item label="内径(mm)">{{ currentRow.entryInnerDiameter }}</el-descriptions-item>
|
||||
<el-descriptions-item label="外径(mm)">{{ currentRow.entryOuterDiameter }}</el-descriptions-item>
|
||||
<el-descriptions-item label="延伸率(%)">{{ currentRow.spmElongation }}</el-descriptions-item>
|
||||
<el-descriptions-item label="SPM轧制力">{{ currentRow.spmRollforce }}</el-descriptions-item>
|
||||
@@ -233,6 +234,7 @@
|
||||
append-to-body>
|
||||
<div class="plan-dialog-body">
|
||||
<div class="plan-dialog-left">
|
||||
<l3-pickup-recommend-panel @apply-recommendation="handleApplyL3PickupRecommendation" />
|
||||
<el-form :model="form" ref="form" label-width="90px" :rules="rules" size="mini" class="plan-base-form">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
@@ -245,11 +247,16 @@
|
||||
<el-input v-model="form.coilid" placeholder="请输入钢卷号" maxLength="32"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="入场钢卷号" prop="enterCoilNo">
|
||||
<el-input v-model="form.enterCoilNo" placeholder="请输入入场钢卷号" maxLength="255"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="钢种" prop="steelGrade">
|
||||
<el-select v-model="form.steelGrade" placeholder="请选择钢种">
|
||||
<el-option v-for="item in steelGradeList" :key="item.gradeid" :label="item.name"
|
||||
:value="item.gradeid"></el-option>
|
||||
:value="item.name"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -264,8 +271,8 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="屈服点" prop="yieldPoint">
|
||||
<el-input v-model="form.yieldPoint" placeholder="请输入屈服点" type="number" step="0.01" min="0"></el-input>
|
||||
<el-form-item label="屈服强度" prop="yieldPoint">
|
||||
<el-input v-model="form.yieldPoint" placeholder="请输入屈服强度" type="number" step="0.01" min="0"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
@@ -283,6 +290,12 @@
|
||||
<el-input v-model="form.entryLength" placeholder="请输入入口长度" type="number" step="1" min="0"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="内径(mm)" prop="entryInnerDiameter">
|
||||
<el-input v-model="form.entryInnerDiameter" placeholder="请输入入口内径" type="number" step="1"
|
||||
min="0"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="外径(mm)" prop="entryOuterDiameter">
|
||||
<el-input v-model="form.entryOuterDiameter" placeholder="请输入入口外径" type="number" step="1"
|
||||
@@ -331,10 +344,13 @@
|
||||
<div class="plan-dialog-right">
|
||||
<process-recommend-panel v-if="spmFlag" :recommendation="processRecommendation"
|
||||
@apply-one="handleProcessApplyOne" @apply-all="handleProcessApplyAll" />
|
||||
<plan-recommend-panel v-else :steel-grade="form.steelGrade" :entry-thick="form.entryThick"
|
||||
:entry-width="form.entryWidth" :spm-rollforce="form.spmRollforce" :steel-grade-list="steelGradeList"
|
||||
@apply-steel-grade="handleApplySteelGrade" @apply-thickness="handleApplyThickness"
|
||||
@apply-width="handleApplyWidth" @apply-rollforce="handleApplyRollforce" />
|
||||
<plan-recommend-panel
|
||||
v-else
|
||||
:steel-grade="form.steelGrade"
|
||||
:entry-thick="form.entryThick"
|
||||
:steel-grade-list="steelGradeList"
|
||||
@apply-recommendation="handleApplyRecommendation"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 弹窗底部按钮 -->
|
||||
@@ -356,6 +372,7 @@ import SetupForm from './components/setupForm.vue'
|
||||
import SetupPane from './components/setupPane.vue'
|
||||
import PlanRecommendPanel from './components/PlanRecommendPanel.vue'
|
||||
import ProcessRecommendPanel from './components/ProcessRecommendPanel.vue'
|
||||
import L3PickupRecommendPanel from './components/L3PickupRecommendPanel.vue'
|
||||
|
||||
// 标准日期格式化方法(优化时间处理逻辑,适配接口日期格式)
|
||||
function parseTime(time, format = "{yyyy}-{mm}-{dd} {hh}:{ii}:{ss}") {
|
||||
@@ -399,7 +416,8 @@ export default {
|
||||
SetupForm,
|
||||
SetupPane,
|
||||
PlanRecommendPanel,
|
||||
ProcessRecommendPanel
|
||||
ProcessRecommendPanel,
|
||||
L3PickupRecommendPanel
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -432,6 +450,7 @@ export default {
|
||||
id: null, // 主键ID(组件内部用,提交时后端自动处理)
|
||||
seqid: null, // 顺序号(number,接口要求)
|
||||
coilid: "", // 钢卷号(string,必填)
|
||||
enterCoilNo: "", // 入场钢卷号(string)
|
||||
unitCode: "", // 机组号(string,接口要求)
|
||||
dummyCoilFlag: 0, // 虚卷标识(number,默认0:非虚卷)
|
||||
planid: "", // 计划ID(string,接口要求)
|
||||
@@ -602,9 +621,11 @@ export default {
|
||||
this.steelGradeList = res.data
|
||||
})
|
||||
},
|
||||
handleApplySteelGrade(payload) {
|
||||
handleApplyRecommendation(payload) {
|
||||
if (!payload) return
|
||||
if (payload.steelGrade) {
|
||||
|
||||
// PlanRecommendPanel 传回 steelGrade 可能是钢种名,也可能是 gradeid
|
||||
if (payload.steelGrade !== undefined && payload.steelGrade !== null && payload.steelGrade !== '') {
|
||||
const gid = String(payload.steelGrade)
|
||||
const match = this.steelGradeList.find(item =>
|
||||
String(item.gradeid) === gid ||
|
||||
@@ -613,31 +634,24 @@ export default {
|
||||
String(item.gradeName || '') === gid
|
||||
)
|
||||
// 下拉选择器以 gradeid 为 value,确保回填 id 以显示名称
|
||||
this.form.steelGrade = match ? match.gradeid : payload.steelGrade
|
||||
this.form.steelGrade = match ? (match.name || match.gradeName || payload.steelGrade) : payload.steelGrade
|
||||
}
|
||||
|
||||
if (payload.entryThick !== undefined && payload.entryThick !== null && payload.entryThick !== '') {
|
||||
this.form.entryThick = payload.entryThick
|
||||
}
|
||||
if (payload.yieldPoint !== undefined && payload.yieldPoint !== null && payload.yieldPoint !== '') {
|
||||
this.form.yieldPoint = payload.yieldPoint
|
||||
}
|
||||
if (payload.entryThick) this.form.entryThick = payload.entryThick
|
||||
if (payload.entryWidth) this.form.entryWidth = payload.entryWidth
|
||||
if (payload.yieldPoint) this.form.yieldPoint = payload.yieldPoint
|
||||
if (payload.spmElongation) this.form.spmElongation = payload.spmElongation
|
||||
if (payload.spmRollforce) this.form.spmRollforce = payload.spmRollforce
|
||||
},
|
||||
handleApplyThickness(payload) {
|
||||
handleApplyL3PickupRecommendation(payload) {
|
||||
if (!payload) return
|
||||
if (payload.entryThick) this.form.entryThick = payload.entryThick
|
||||
if (payload.yieldPoint) this.form.yieldPoint = payload.yieldPoint
|
||||
},
|
||||
handleApplyWidth(payload) {
|
||||
if (!payload) return
|
||||
if (payload.entryWidth) this.form.entryWidth = payload.entryWidth
|
||||
if (payload.spmRollforce) this.form.spmRollforce = payload.spmRollforce
|
||||
},
|
||||
handleApplyRollforce(payload) {
|
||||
if (!payload) return
|
||||
if (payload.spmRollforce) this.form.spmRollforce = payload.spmRollforce
|
||||
if (payload.entryThick) this.form.entryThick = payload.entryThick
|
||||
if (payload.yieldPoint) this.form.yieldPoint = payload.yieldPoint
|
||||
if (payload.spmElongation) this.form.spmElongation = payload.spmElongation
|
||||
if (payload.steelGrade) this.form.steelGrade = payload.steelGrade
|
||||
if (payload.coilid !== undefined) {
|
||||
this.form.coilid = payload.coilid || ''
|
||||
}
|
||||
if (payload.enterCoilNo !== undefined) {
|
||||
this.form.enterCoilNo = payload.enterCoilNo || ''
|
||||
}
|
||||
},
|
||||
handleProcessRecommendation(payload) {
|
||||
this.processRecommendation = payload
|
||||
@@ -810,6 +824,7 @@ export default {
|
||||
status: "NEW", // 新增计划默认状态为 新建
|
||||
planid: "",
|
||||
planType: "",
|
||||
enterCoilNo: "",
|
||||
originCoilid: "",
|
||||
yieldPoint: null,
|
||||
zincCoatingThickness: null,
|
||||
@@ -914,6 +929,14 @@ export default {
|
||||
this.dialogTitle = "编辑计划"; // 设置弹窗标题
|
||||
// 深拷贝行数据(避免修改原表格数据)
|
||||
this.form = JSON.parse(JSON.stringify(row));
|
||||
// 确保回显时,form.steelGrade 始终是钢种名称,以便 el-select 正确显示
|
||||
if (this.form.steelGrade) {
|
||||
const gradeId = String(this.form.steelGrade);
|
||||
const match = this.steelGradeList.find(item => String(item.gradeid) === gradeId);
|
||||
if (match) {
|
||||
this.form.steelGrade = match.name;
|
||||
}
|
||||
}
|
||||
this.spmFlag = false
|
||||
|
||||
// 1. ID转字符串(避免精度丢失)
|
||||
@@ -1057,11 +1080,19 @@ export default {
|
||||
},
|
||||
|
||||
generateSpmParams() {
|
||||
addSetup({
|
||||
// 注意:工艺参数入库需要 steelGrade/thick/yieldStren 等基础字段。
|
||||
// 这里 thick/yieldStren 后端字段名对应 pdi_setup.thick / pdi_setup.yield_stren
|
||||
// 前端基础信息里分别是 entryThick / yieldPoint
|
||||
let data = {
|
||||
coilid: this.form.coilid,
|
||||
planid: this.form.planid,
|
||||
steelGrade: this.form.steelGrade,
|
||||
thick: this.form.entryThick,
|
||||
yieldStren: this.form.yieldPoint,
|
||||
...this.setupForm
|
||||
}).then(res => {
|
||||
}
|
||||
console.log(data)
|
||||
addSetup(data).then(res => {
|
||||
this.$message.success("工艺参数创建成功");
|
||||
})
|
||||
}
|
||||
|
||||
1390
apps/l2/src/views/l2/productLine/ProductLine.vue
Normal file
1390
apps/l2/src/views/l2/productLine/ProductLine.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -66,7 +66,7 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col
|
||||
v-for="setup in setups"
|
||||
:key="setup.ID"
|
||||
:key="setup.id || setup.ID || setup.planid || setup.coilid"
|
||||
:xs="24"
|
||||
:sm="12"
|
||||
:md="8"
|
||||
@@ -85,22 +85,9 @@
|
||||
</div>
|
||||
|
||||
<div class="card-subtitle">
|
||||
<span>入口厚度: {{ setup.entryThick || '-' }}</span>
|
||||
<span>入口宽度: {{ setup.entryWidth || '-' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="card-subtitle">
|
||||
<span>入口重量: {{ setup.entryWeight || '-' }}</span>
|
||||
<span>入口长度: {{ setup.entryLength || '-' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="card-subtitle">
|
||||
<span>拉伸机延伸率: {{ setup.tlElong || '-' }}</span>
|
||||
<span>轧机轧制力: {{ setup.tmRollforce || '-' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="card-subtitle">
|
||||
<span>轧机弯辊力: {{ setup.tmBendforce || '-' }}</span>
|
||||
<span>钢种: {{ setup.steelGrade || '-' }}</span>
|
||||
<span>厚度: {{ setup.thick || '-' }}</span>
|
||||
<span>屈服强度: {{ setup.yieldStren || '-' }}</span>
|
||||
<span v-if="setup.updateTime">更新时间: {{ formatTime(setup.updateTime) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -166,56 +153,47 @@ import { listPlan } from '@/api/l2/plan'
|
||||
|
||||
// 传动字段定义(中文界面,贴合工业场景)
|
||||
const DRIVE_FIELDS = [
|
||||
// 全线张力(与 pdi_setup / PdiSetups 字段对齐)
|
||||
{ key: 'porTension', label: '开卷机张力' },
|
||||
{ key: 'celTension', label: '入口活套张力' },
|
||||
{ key: 'cleanTension', label: '清洗段张力' },
|
||||
{ key: 'furTension', label: '炉区张力' },
|
||||
{ key: 'towerTension', label: '冷却塔张力' },
|
||||
{ key: 'tmNoneTension', label: '轧机无张力' },
|
||||
{ key: 'tmEntryTension', label: '轧机入口张力' },
|
||||
{ key: 'tmExitTension', label: '轧机出口张力' },
|
||||
{ key: 'tlNoneTension', label: '拉伸机无张力' },
|
||||
{ key: 'tlExitTension', label: '拉伸机出口张力' },
|
||||
{ key: 'coatTension', label: '后处理段张力' },
|
||||
{ key: 'passivationTension', label: '钝化段张力' },
|
||||
{ key: 'cxlTension', label: '出口活套张力' },
|
||||
{ key: 'trTension', label: '卷取机张力' },
|
||||
|
||||
{ key: 'tlElong', label: '拉伸机延伸率' },
|
||||
{ key: 'tlLvlMesh1', label: '拉伸机矫直辊间隙1' },
|
||||
{ key: 'tlLvlMesh2', label: '拉伸机矫直辊间隙2' },
|
||||
{ key: 'tlAcbMesh', label: '拉伸机防侧弯间隙' },
|
||||
// 平整机
|
||||
{ key: 'levelerEntryTension', label: '平整机入口张力' },
|
||||
{ key: 'levelerExitTension', label: '平整机出口张力' },
|
||||
|
||||
{ key: 'tmBendforce', label: '轧机弯辊力' },
|
||||
{ key: 'tmAcrMesh', label: '轧机防皱辊间隙' },
|
||||
{ key: 'tmBrMesh', label: '轧机防颤辊间隙' },
|
||||
{ key: 'tmRollforce', label: '轧机轧制力' }
|
||||
// 矫直机
|
||||
{ key: 'straightenerExitTension', label: '矫直机出口张力' },
|
||||
|
||||
// 退火炉
|
||||
{ key: 'furTension', label: '炉区张力' },
|
||||
{ key: 'towerTension', label: '冷却塔张力' }
|
||||
]
|
||||
|
||||
// OPC地址映射(保持原有配置,不影响功能)
|
||||
const DRIVE_ADDRESS = {
|
||||
// 全线张力
|
||||
porTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionPorBR1',
|
||||
celTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR3',
|
||||
cleanTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR1BR2',
|
||||
furTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionFur1',
|
||||
towerTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionFur2',
|
||||
tmNoneTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR5BR6',
|
||||
tmEntryTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR5TM',
|
||||
tmExitTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionTMBR6',
|
||||
tlNoneTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR6BR7',
|
||||
tlExitTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionTLBR7',
|
||||
coatTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR7BR8',
|
||||
// 原系统里 passivation 对应“BR7-BR8”段(原 coatTension 地址),这里沿用该地址
|
||||
passivationTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR7BR8',
|
||||
cxlTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR8BR9',
|
||||
trTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR9TR',
|
||||
|
||||
tlElong: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.TLElongation',
|
||||
tlLvlMesh1: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.LevelingMesh1',
|
||||
tlLvlMesh2: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.LevelingMesh2',
|
||||
tlAcbMesh: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.AntiCrossBowUnitMesh',
|
||||
// 平整机(原 tm* 地址沿用)
|
||||
levelerEntryTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionBR5TM',
|
||||
levelerExitTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionTMBR6',
|
||||
|
||||
tmBendforce: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.TMBendforce',
|
||||
tmAcrMesh: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.ACRMesh',
|
||||
tmBrMesh: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.BRMesh',
|
||||
tmRollforce: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.TMRollforce'
|
||||
// 矫直机(原 tl* 地址沿用)
|
||||
straightenerExitTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionTLBR7',
|
||||
|
||||
// 退火炉
|
||||
furTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionFur1',
|
||||
towerTension: 'ns=2;s=ProcessCGL.PLCLine.L2Setup.tensionFur2'
|
||||
}
|
||||
|
||||
export default {
|
||||
@@ -295,13 +273,14 @@ export default {
|
||||
this.setups = setupList.map(s => {
|
||||
const params = {}
|
||||
this.driveFields.forEach(f => {
|
||||
// s 是后端 listSetup 返回的 PdiSetups(字段名与 driveFields.key 对齐)
|
||||
const fromSetup = s ? s[f.key] : undefined
|
||||
const fromLast = this.lastSuccess?.values?.[f.key]
|
||||
|
||||
// 优先级:当前配置值 > 上次成功值 > 空字符串
|
||||
if (fromSetup !== undefined && fromSetup !== null && String(fromSetup) !== '') {
|
||||
params[f.key] = String(fromSetup)
|
||||
} else if (fromLast !== undefined && fromLast !== null) {
|
||||
} else if (fromLast !== undefined && fromLast !== null && String(fromLast) !== '') {
|
||||
params[f.key] = String(fromLast)
|
||||
} else {
|
||||
params[f.key] = ''
|
||||
|
||||
@@ -46,45 +46,60 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 按模板渲染可编辑表单 -->
|
||||
<el-form :model="form" label-position="top" size="mini">
|
||||
<div class="group-list">
|
||||
<div v-for="group in groupedItems" :key="group.groupKey" class="group-section">
|
||||
<div class="group-header">
|
||||
<span class="group-title">{{ group.groupTitle }}</span>
|
||||
<span class="group-count">({{ group.items.length }} 项)</span>
|
||||
</div>
|
||||
<!-- 表格形式展示/编辑(无下拉分组,直接按组+序号排序展示) -->
|
||||
<el-form :model="form" size="mini">
|
||||
<el-table
|
||||
:data="tableItems"
|
||||
border
|
||||
stripe
|
||||
size="mini"
|
||||
row-key="__rowKey"
|
||||
height="calc(100vh - 260px)"
|
||||
:cell-class-name="cellClassName"
|
||||
class="param-table"
|
||||
>
|
||||
<el-table-column prop="groupTitle" label="分组" min-width="90" />
|
||||
<el-table-column prop="itemNo" label="序号" width="70" />
|
||||
<el-table-column prop="labelEn" label="参数" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="paramCode" label="编码" min-width="140" show-overflow-tooltip />
|
||||
|
||||
<!-- 每行三个输入框 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8" v-for="item in group.items" :key="item.templateItemId || item.paramCode">
|
||||
<el-form-item :label="item.labelEn">
|
||||
<el-input v-model="form[item.paramCode]" :placeholder="getPlaceholder(item)"
|
||||
:class="{ 'is-changed': isChangedFromLast(item) }" />
|
||||
<el-table-column label="当前值" min-width="180">
|
||||
<template slot-scope="{ row }">
|
||||
<el-input
|
||||
v-model="form[row.paramCode]"
|
||||
:placeholder="getPlaceholder(row)"
|
||||
size="mini"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 辅助信息:上次/默认值(常驻,不会像 placeholder 一样消失) -->
|
||||
<div class="field-hint">
|
||||
<span v-if="getLastValue(item) !== undefined" class="hint-item">
|
||||
上次成功:<b>{{ getLastValue(item) }}</b>
|
||||
</span>
|
||||
<span v-if="getDefaultValue(item) !== undefined" class="hint-item">
|
||||
默认值:<b>{{ getDefaultValue(item) }}</b>
|
||||
</span>
|
||||
<span v-if="isChangedFromLast(item)" class="hint-item changed">
|
||||
已修改
|
||||
</span>
|
||||
</div>
|
||||
<el-table-column label="上次成功" min-width="120">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-if="getLastValue(row) !== undefined">{{ getLastValue(row) }}</span>
|
||||
<span v-else class="muted">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 编辑点位 -->
|
||||
<div v-if="editTemplate" class="addr-inline">
|
||||
<span class="addr-label">点位地址:</span>
|
||||
<el-input v-model="item.address" size="mini" placeholder="ns=2;s=..." />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
<el-table-column label="默认值" min-width="120">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-if="getDefaultValue(row) !== undefined">{{ getDefaultValue(row) }}</span>
|
||||
<span v-else class="muted">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="状态" width="80">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag v-if="isChangedFromLast(row)" type="warning" size="mini">已修改</el-tag>
|
||||
<span v-else class="muted">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column v-if="editTemplate" label="点位地址" min-width="220">
|
||||
<template slot-scope="{ row }">
|
||||
<el-input v-model="row.address" size="mini" placeholder="ns=2;s=..." />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form>
|
||||
|
||||
<div v-if="!loading && templateItems.length === 0" class="empty-data">
|
||||
@@ -139,29 +154,34 @@ export default {
|
||||
if (String(en) === '0') return false
|
||||
return true
|
||||
})
|
||||
.sort((a, b) => (a.itemNo || 0) - (b.itemNo || 0))
|
||||
},
|
||||
|
||||
// 按 paramCode 前缀分组(如 NOF1 / NOF2 / RTF1 / SF ...)
|
||||
// 表格数据(扁平化所有分组,添加组标题)
|
||||
tableItems() {
|
||||
return this.templateItems
|
||||
.map(item => ({
|
||||
...item,
|
||||
groupKey: this.getGroupKey(item),
|
||||
groupTitle: this.getGroupTitle(this.getGroupKey(item)),
|
||||
__rowKey: `${item.paramCode}_${item.templateItemId || ''}`
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
// 先按组名排序
|
||||
const groupCompare = String(a.groupKey).localeCompare(
|
||||
String(b.groupKey),
|
||||
undefined,
|
||||
{ numeric: true }
|
||||
)
|
||||
if (groupCompare !== 0) return groupCompare
|
||||
|
||||
// 同组内按 itemNo 排序
|
||||
return (a.itemNo || 0) - (b.itemNo || 0)
|
||||
})
|
||||
},
|
||||
|
||||
// 兼容旧代码,保留但不再使用
|
||||
groupedItems() {
|
||||
const groupsMap = new Map()
|
||||
const items = this.templateItems
|
||||
|
||||
items.forEach(it => {
|
||||
const key = this.getGroupKey(it)
|
||||
if (!groupsMap.has(key)) groupsMap.set(key, [])
|
||||
groupsMap.get(key).push(it)
|
||||
})
|
||||
|
||||
// Map -> Array,并按组名排序(NOF1, NOF2... 这种会自然排序更好)
|
||||
const groups = Array.from(groupsMap.entries()).map(([groupKey, groupItems]) => ({
|
||||
groupKey,
|
||||
groupTitle: this.getGroupTitle(groupKey),
|
||||
items: groupItems
|
||||
}))
|
||||
|
||||
groups.sort((a, b) => String(a.groupKey).localeCompare(String(b.groupKey), undefined, { numeric: true }))
|
||||
return groups
|
||||
return []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -169,6 +189,13 @@ export default {
|
||||
this.reload()
|
||||
},
|
||||
methods: {
|
||||
cellClassName({ row, column }) {
|
||||
// 高亮:当前值与上次成功不一致时,给“当前值”这一列加底色
|
||||
if (column && column.label === '当前值' && this.isChangedFromLast(row)) {
|
||||
return 'cell-changed'
|
||||
}
|
||||
return ''
|
||||
},
|
||||
pickItemFields(it) {
|
||||
if (!it) return {}
|
||||
// 仅挑后端支持保存的字段,避免把多余字段/响应结构带回去
|
||||
@@ -513,58 +540,21 @@ export default {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.group-list {
|
||||
/* 表格辅助样式 */
|
||||
.param-table {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.group-section {
|
||||
padding: 10px 0 6px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
.muted {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
.group-section:first-child {
|
||||
border-top: none;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.group-header {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
margin: 4px 0 10px;
|
||||
}
|
||||
|
||||
.group-title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.group-count {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
/* 字段提示信息 */
|
||||
.field-hint {
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
margin-top: 4px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.hint-item {
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.hint-item.changed {
|
||||
color: #e6a23c;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 修改过的输入框高亮 */
|
||||
:deep(.el-input.is-changed .el-input__inner) {
|
||||
border-color: #e6a23c;
|
||||
/* 修改过的单元格高亮(当前值列) */
|
||||
:deep(.el-table .cell-changed) {
|
||||
background-color: #fdf6ec;
|
||||
}
|
||||
|
||||
:deep(.el-table .cell-changed .el-input__inner) {
|
||||
border-color: #e6a23c;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -142,35 +142,35 @@ export default {
|
||||
|
||||
// 字段schema(表格列/表单)
|
||||
schemaAllLine: [
|
||||
{ prop: 'steelGrade', label: '钢种', required: true },
|
||||
{ prop: 'thick', label: '厚度', required: true },
|
||||
{ prop: 'yieldStren', label: '屈服强度', required: true },
|
||||
{ prop: 'value1', label: '开卷机张力' },
|
||||
{ prop: 'value2', label: '入口活套张力' },
|
||||
{ prop: 'value3', label: '清洗段张力' },
|
||||
{ prop: 'value4', label: '钝化段张力' },
|
||||
{ prop: 'value5', label: '出口活套张力' },
|
||||
{ prop: 'value6', label: '卷取机张力' }
|
||||
{ prop: 'steelGrade', label: '钢种', required: true, minWidth: 65 },
|
||||
{ prop: 'thick', label: '厚度', required: true, minWidth: 65 },
|
||||
{ prop: 'yieldStren', label: '屈服强度', required: true, minWidth: 90 },
|
||||
{ prop: 'value1', label: '开卷机张力', minWidth: 120 },
|
||||
{ prop: 'value2', label: '入口活套张力', minWidth: 120 },
|
||||
{ prop: 'value3', label: '清洗段张力', minWidth: 120 },
|
||||
{ prop: 'value4', label: '钝化段张力', minWidth: 120 },
|
||||
{ prop: 'value5', label: '出口活套张力', minWidth: 120 },
|
||||
{ prop: 'value6', label: '卷取机张力', minWidth: 120 }
|
||||
],
|
||||
schemaLeveler: [
|
||||
{ prop: 'steelGrade', label: '钢种', required: true },
|
||||
{ prop: 'thick', label: '厚度', required: true },
|
||||
{ prop: 'yieldStren', label: '屈服强度', required: true },
|
||||
{ prop: 'value1', label: '平整机入口张力' },
|
||||
{ prop: 'value2', label: '平整机出口张力' }
|
||||
{ prop: 'steelGrade', label: '钢种', required: true, minWidth: 65 },
|
||||
{ prop: 'thick', label: '厚度', required: true, minWidth: 65 },
|
||||
{ prop: 'yieldStren', label: '屈服强度', required: true, minWidth: 90 },
|
||||
{ prop: 'value1', label: '平整机入口张力', minWidth: 120 },
|
||||
{ prop: 'value2', label: '平整机出口张力', minWidth: 120 }
|
||||
],
|
||||
schemaStraightener: [
|
||||
{ prop: 'steelGrade', label: '钢种', required: true },
|
||||
{ prop: 'thick', label: '厚度', required: true },
|
||||
{ prop: 'yieldStren', label: '屈服强度', required: true },
|
||||
{ prop: 'value1', label: '矫直机出口张力' }
|
||||
{ prop: 'steelGrade', label: '钢种', required: true, minWidth: 65 },
|
||||
{ prop: 'thick', label: '厚度', required: true, minWidth: 65 },
|
||||
{ prop: 'yieldStren', label: '屈服强度', required: true, minWidth: 90 },
|
||||
{ prop: 'value1', label: '矫直机出口张力', minWidth: 120 }
|
||||
],
|
||||
schemaAnnealingFurnace: [
|
||||
{ prop: 'steelGrade', label: '钢种', required: true },
|
||||
{ prop: 'thick', label: '厚度', required: true },
|
||||
{ prop: 'yieldStren', label: '屈服强度', required: true },
|
||||
{ prop: 'value1', label: '炉区张力' },
|
||||
{ prop: 'value2', label: '冷却塔张力' }
|
||||
{ prop: 'steelGrade', label: '钢种', required: true, minWidth: 65 },
|
||||
{ prop: 'thick', label: '厚度', required: true, minWidth: 65 },
|
||||
{ prop: 'yieldStren', label: '屈服强度', required: true, minWidth: 90 },
|
||||
{ prop: 'value1', label: '炉区张力', minWidth: 120 },
|
||||
{ prop: 'value2', label: '冷却塔张力', minWidth: 120 }
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
<template>
|
||||
<div class="tension-table-container">
|
||||
<div class="table-body">
|
||||
<!-- 查询区域 -->
|
||||
<el-form :inline="true" size="mini" class="query-form" @submit.native.prevent>
|
||||
<el-form-item v-for="k in acceptKeys" :key="k" :label="getLabel(k)">
|
||||
<el-input
|
||||
v-if="k !== 'steelGrade'"
|
||||
v-model="queryParams[k]"
|
||||
clearable
|
||||
:placeholder="`请输入${getLabel(k)}`"
|
||||
@keyup.enter.native="handleQuery"
|
||||
/>
|
||||
<el-select
|
||||
v-else
|
||||
v-model="queryParams[k]"
|
||||
clearable
|
||||
:placeholder="`请选择${getLabel(k)}`"
|
||||
@keyup.enter.native="handleQuery"
|
||||
>
|
||||
<el-option v-for="item in steelGradeList" :key="item.gradeid" :label="item.name" :value="item.name" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
|
||||
@@ -26,19 +37,17 @@
|
||||
</div>
|
||||
|
||||
<!-- 表格 -->
|
||||
<el-table v-loading="loading" :data="list" border stripe height="calc(100% - 148px)">
|
||||
<el-table-column type="index" label="#" width="50" />
|
||||
|
||||
<el-table v-loading="loading" :data="list" border stripe height="100%">
|
||||
<el-table-column
|
||||
v-for="col in schema"
|
||||
:key="col.prop"
|
||||
:prop="col.prop"
|
||||
:label="col.label"
|
||||
:min-width="col.minWidth || 120"
|
||||
:min-width="col.minWidth || 85"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
|
||||
<el-table-column label="操作" width="180" fixed="right">
|
||||
<el-table-column label="操作" width="130">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="mini" icon="el-icon-edit" @click="handleEdit(scope.row)">编辑</el-button>
|
||||
<el-button type="text" size="mini" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button>
|
||||
@@ -46,6 +55,8 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
@@ -61,7 +72,10 @@
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12" v-for="col in schema" :key="'f-' + col.prop">
|
||||
<el-form-item :label="col.label" :prop="col.prop">
|
||||
<el-input v-model="form[col.prop]" :disabled="isEdit && acceptKeys.includes(col.prop)" />
|
||||
<el-input v-if="col.prop !== 'steelGrade'" v-model="form[col.prop]" :disabled="isEdit && acceptKeys.includes(col.prop)" />
|
||||
<el-select v-else v-model="form[col.prop]" :disabled="isEdit && acceptKeys.includes(col.prop)" placeholder="请选择钢种">
|
||||
<el-option v-for="item in steelGradeList" :key="item.gradeid" :label="item.name" :value="item.name" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -75,6 +89,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getSteelGradeList, addSteelGrade, updateSteelGrade, deleteSteelGrade, getSteelGradeInfo } from '@/api/l2/steelGrade'
|
||||
|
||||
export default {
|
||||
name: 'TensionTable',
|
||||
props: {
|
||||
@@ -108,14 +124,21 @@ export default {
|
||||
isEdit: false,
|
||||
form: fm,
|
||||
rules: {},
|
||||
submitLoading: false
|
||||
submitLoading: false,
|
||||
steelGradeList: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.buildRules()
|
||||
this.getList()
|
||||
this.fetchSteelGradeList()
|
||||
},
|
||||
methods: {
|
||||
fetchSteelGradeList() {
|
||||
getSteelGradeList().then(res => {
|
||||
this.steelGradeList = res.rows || res.data || []
|
||||
})
|
||||
},
|
||||
getLabel(prop) {
|
||||
return (this.schema.find(s => s.prop === prop) || {}).label || prop
|
||||
},
|
||||
@@ -217,6 +240,22 @@ export default {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.table-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 让 el-table 真正占满剩余高度,避免出现大量空白行/操作列重复显示 */
|
||||
.table-body ::v-deep .el-table {
|
||||
flex: 1;
|
||||
}
|
||||
.table-body ::v-deep .el-table__body-wrapper {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.query-form {
|
||||
padding: 10px 10px 0;
|
||||
border: 1px solid #ebeef5;
|
||||
|
||||
@@ -74,29 +74,29 @@
|
||||
<div class="param-row">
|
||||
<div class="param-item">
|
||||
<span class="param-label">开卷机张力:</span>
|
||||
<span class="param-value">{{ currentRow.porTension ?? '-' }}</span>
|
||||
<span class="param-value">{{ currentRow.porTension == null ? '-' : currentRow.porTension }}</span>
|
||||
</div>
|
||||
<div class="param-item">
|
||||
<span class="param-label">入口活套张力:</span>
|
||||
<span class="param-value">{{ currentRow.celTension ?? '-' }}</span>
|
||||
<span class="param-value">{{ currentRow.celTension == null ? '-' : currentRow.celTension }}</span>
|
||||
</div>
|
||||
<div class="param-item">
|
||||
<span class="param-label">清洗段张力:</span>
|
||||
<span class="param-value">{{ currentRow.cleanTension ?? '-' }}</span>
|
||||
<span class="param-value">{{ currentRow.cleanTension == null ? '-' : currentRow.cleanTension }}</span>
|
||||
</div>
|
||||
<div class="param-item">
|
||||
<span class="param-label">钝化段张力:</span>
|
||||
<span class="param-value">{{ currentRow.passivationTension ?? '-' }}</span>
|
||||
<span class="param-value">{{ currentRow.passivationTension == null ? '-' : currentRow.passivationTension }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="param-row">
|
||||
<div class="param-item">
|
||||
<span class="param-label">出口活套张力:</span>
|
||||
<span class="param-value">{{ currentRow.cxlTension ?? '-' }}</span>
|
||||
<span class="param-value">{{ currentRow.cxlTension == null ? '-' : currentRow.cxlTension }}</span>
|
||||
</div>
|
||||
<div class="param-item">
|
||||
<span class="param-label">卷取机张力:</span>
|
||||
<span class="param-value">{{ currentRow.trTension ?? '-' }}</span>
|
||||
<span class="param-value">{{ currentRow.trTension == null ? '-' : currentRow.trTension }}</span>
|
||||
</div>
|
||||
<div class="param-item"></div>
|
||||
<div class="param-item"></div>
|
||||
@@ -109,11 +109,11 @@
|
||||
<div class="param-row">
|
||||
<div class="param-item">
|
||||
<span class="param-label">平整机入口张力:</span>
|
||||
<span class="param-value">{{ currentRow.levelerEntryTension ?? '-' }}</span>
|
||||
<span class="param-value">{{ currentRow.levelerEntryTension == null ? '-' : currentRow.levelerEntryTension }}</span>
|
||||
</div>
|
||||
<div class="param-item">
|
||||
<span class="param-label">平整机出口张力:</span>
|
||||
<span class="param-value">{{ currentRow.levelerExitTension ?? '-' }}</span>
|
||||
<span class="param-value">{{ currentRow.levelerExitTension == null ? '-' : currentRow.levelerExitTension }}</span>
|
||||
</div>
|
||||
<div class="param-item"></div>
|
||||
<div class="param-item"></div>
|
||||
@@ -126,7 +126,7 @@
|
||||
<div class="param-row">
|
||||
<div class="param-item">
|
||||
<span class="param-label">矫直机出口张力:</span>
|
||||
<span class="param-value">{{ currentRow.straightenerExitTension ?? '-' }}</span>
|
||||
<span class="param-value">{{ currentRow.straightenerExitTension == null ? '-' : currentRow.straightenerExitTension }}</span>
|
||||
</div>
|
||||
<div class="param-item"></div>
|
||||
<div class="param-item"></div>
|
||||
@@ -140,11 +140,11 @@
|
||||
<div class="param-row">
|
||||
<div class="param-item">
|
||||
<span class="param-label">炉区张力:</span>
|
||||
<span class="param-value">{{ currentRow.furTension ?? '-' }}</span>
|
||||
<span class="param-value">{{ currentRow.furTension == null ? '-' : currentRow.furTension }}</span>
|
||||
</div>
|
||||
<div class="param-item">
|
||||
<span class="param-label">冷却塔张力:</span>
|
||||
<span class="param-value">{{ currentRow.towerTension ?? '-' }}</span>
|
||||
<span class="param-value">{{ currentRow.towerTension == null ? '-' : currentRow.towerTension }}</span>
|
||||
</div>
|
||||
<div class="param-item"></div>
|
||||
<div class="param-item"></div>
|
||||
@@ -223,25 +223,12 @@ export default {
|
||||
cleanTension: null,
|
||||
furTension: null,
|
||||
towerTension: null,
|
||||
tmNoneTension: null,
|
||||
tmEntryTension: null,
|
||||
tmExitTension: null,
|
||||
tmRollforce: null,
|
||||
tmBendforce: null,
|
||||
tmAcrMesh: null,
|
||||
tmBrMesh: null,
|
||||
tlNoneTension: null,
|
||||
tlExitTension: null,
|
||||
tlElong: null,
|
||||
tlLvlMesh1: null,
|
||||
tlLvlMesh2: null,
|
||||
tlAcbMesh: null,
|
||||
coatTension: null,
|
||||
// 已废弃的旧字段
|
||||
cxlTension: null,
|
||||
trTension: null,
|
||||
createTime: null,
|
||||
updateTime: null,
|
||||
TYPE: null
|
||||
// TYPE: null(旧字段)
|
||||
},
|
||||
// 表单参数
|
||||
form: {},
|
||||
@@ -281,25 +268,12 @@ export default {
|
||||
cleanTension: null,
|
||||
furTension: null,
|
||||
towerTension: null,
|
||||
tmNoneTension: null,
|
||||
tmEntryTension: null,
|
||||
tmExitTension: null,
|
||||
tmRollforce: null,
|
||||
tmBendforce: null,
|
||||
tmAcrMesh: null,
|
||||
tmBrMesh: null,
|
||||
tlNoneTension: null,
|
||||
tlExitTension: null,
|
||||
tlElong: null,
|
||||
tlLvlMesh1: null,
|
||||
tlLvlMesh2: null,
|
||||
tlAcbMesh: null,
|
||||
coatTension: null,
|
||||
// 已废弃的旧字段
|
||||
cxlTension: null,
|
||||
trTension: null,
|
||||
createTime: null,
|
||||
updateTime: null,
|
||||
TYPE: null
|
||||
// TYPE: null(旧字段)
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
|
||||
@@ -207,12 +207,23 @@ import { listStoppage, updateStoppage, deleteStoppage } from '@/api/l2/stop'; //
|
||||
export default {
|
||||
name: 'StoppageManagement',
|
||||
data() {
|
||||
// 计算默认时间范围:近一个月(从本月1号到今天)
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = now.getMonth() + 1; // getMonth() 返回 0-11,需要加1
|
||||
const day = now.getDate();
|
||||
|
||||
// 开始时间:本月1号
|
||||
const startDate = `${year}-${String(month).padStart(2, '0')}-01`;
|
||||
// 结束时间:今天
|
||||
const endDate = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
|
||||
|
||||
return {
|
||||
// 查询表单数据
|
||||
queryForm: {
|
||||
// 只保留年月日YYYY-mm-dd格式
|
||||
startDate: '2023-08-13',
|
||||
endDate: '2025-08-20'
|
||||
startDate: startDate,
|
||||
endDate: endDate
|
||||
},
|
||||
// 表格数据
|
||||
tableData: [],
|
||||
@@ -264,7 +275,17 @@ export default {
|
||||
// 获取停机记录列表
|
||||
getStoppageList() {
|
||||
this.tableLoading = true;
|
||||
listStoppage(this.queryForm)
|
||||
// 构建查询参数,结束时间设置为当天的23:59:59
|
||||
const queryParams = {
|
||||
...this.queryForm
|
||||
};
|
||||
if (queryParams.endDate) {
|
||||
queryParams.endDate = queryParams.endDate + ' 23:59:59';
|
||||
}
|
||||
if (queryParams.startDate) {
|
||||
queryParams.startDate = queryParams.startDate + ' 00:00:00';
|
||||
}
|
||||
listStoppage(queryParams)
|
||||
.then(response => {
|
||||
this.tableLoading = false;
|
||||
this.btnLoading = false;
|
||||
|
||||
@@ -31,7 +31,23 @@ export default {
|
||||
return {
|
||||
prevDriveData: {}, // 存储上一次的驱动数据,用于对比变化
|
||||
blinkKeyMap: {}, // 每个key独立的闪烁状态(对象形式:{ key1: true, key2: false })
|
||||
timerMap: {} // 每个key独立的定时器缓存,用于清除旧定时器
|
||||
timerMap: {}, // 每个key独立的定时器缓存,用于清除旧定时器
|
||||
labelMap: {
|
||||
jcf1FurnaceTemperatureActual: 'jcf1炉温(℃)',
|
||||
jcf2FurnaceTemperatureActual: 'jcf2炉温(℃)',
|
||||
lbzFurnaceTemperatureActual: 'lbz炉温(℃)',
|
||||
lthFurnaceTemperatureActual: 'lth炉温(℃)',
|
||||
nof1FurnaceTemperatureActual: 'nof1炉温(℃)',
|
||||
nof2FurnaceTemperatureActual: 'nof2炉温(℃)',
|
||||
nof3FurnaceTemperatureActual: 'nof3炉温(℃)',
|
||||
nof4FurnaceTemperatureActual: 'nof4炉温(℃)',
|
||||
nof5FurnaceTemperatureActual: 'nof5炉温(℃)',
|
||||
phFurnaceTemperatureActual: 'ph炉温(℃)',
|
||||
rtf1FurnaceTemperatureActual: 'rtf1炉温(℃)',
|
||||
rtf2FurnaceTemperatureActual: 'rtf2炉温(℃)',
|
||||
sfFurnaceTemperatureActual: 'sf炉温(℃)',
|
||||
tdsFurnaceTemperatureActual: 'tds炉温(℃)'
|
||||
}
|
||||
}
|
||||
},
|
||||
// 筛选包含Actual(不区分大小写)的键值对
|
||||
@@ -92,10 +108,7 @@ export default {
|
||||
// 格式化标签名(将驼峰命名转为中文式分段,提升可读性)
|
||||
formatLabel(key) {
|
||||
if (!key) return '';
|
||||
// 驼峰命名转空格分隔
|
||||
const result = key.replace(/([A-Z])/g, ' $1');
|
||||
// 首字母大写
|
||||
return result.charAt(0).toUpperCase() + result.slice(1);
|
||||
return this.labelMap[key] || key;
|
||||
},
|
||||
// 格式化值(数字类型保留4位小数,提升展示美观度)
|
||||
formatValue(value) {
|
||||
|
||||
@@ -907,7 +907,7 @@ import { getDriveSetupValue, getFurnaceSetupValue } from '@/api/l2/setupValue'
|
||||
return this.buildSectionMetrics('COAT', [
|
||||
'stripSpeedTmExit', 'avrCoatingWeightTop', 'avrCoatingWeightBottom',
|
||||
'tmElongation', 'tlElongation', 'tensionBr6toBr7Br8', 'tensionBr8Tm',
|
||||
'tensionTmBr9', 'tlElongation'])
|
||||
'tensionTmBr9'])
|
||||
},
|
||||
exitSectionMetrics() {
|
||||
return this.buildSectionMetrics('EXIT', ['speedExitSection', 'coilLength', 'cxlLength', 'cxlCapacity', 'tensionBr9Tr'])
|
||||
@@ -1310,14 +1310,14 @@ import { getDriveSetupValue, getFurnaceSetupValue } from '@/api/l2/setupValue'
|
||||
title: '甩尾操作提示',
|
||||
type: 'warning',
|
||||
duration: 5000,
|
||||
needAlert: true
|
||||
needAlert: false
|
||||
},
|
||||
THROW_TAIL: {
|
||||
icon: '⚠️',
|
||||
title: '甩尾操作提示',
|
||||
type: 'warning',
|
||||
duration: 5000,
|
||||
needAlert: true
|
||||
needAlert: false
|
||||
},
|
||||
ALL_RETURN: {
|
||||
icon: '↩️',
|
||||
|
||||
7
pnpm-lock.yaml
generated
7
pnpm-lock.yaml
generated
@@ -83,6 +83,9 @@ importers:
|
||||
splitpanes:
|
||||
specifier: 2.4.1
|
||||
version: 2.4.1
|
||||
three:
|
||||
specifier: ^0.158.0
|
||||
version: 0.158.0
|
||||
vue:
|
||||
specifier: 2.6.12
|
||||
version: 2.6.12
|
||||
@@ -14235,6 +14238,10 @@ packages:
|
||||
webpack: 4.47.0
|
||||
dev: true
|
||||
|
||||
/three@0.158.0:
|
||||
resolution: {integrity: sha512-TALj4EOpdDPF1henk2Q+s17K61uEAAWQ7TJB68nr7FKxqwyDr3msOt5IWdbGm4TaWKjrtWS8DJJWe9JnvsWOhQ==}
|
||||
dev: false
|
||||
|
||||
/three@0.180.0:
|
||||
resolution: {integrity: sha512-o+qycAMZrh+TsE01GqWUxUIKR1AL0S8pq7zDkYOQw8GqfX8b8VoCKYUoHbhiX5j+7hr8XsuHDVU6+gkQJQKg9w==}
|
||||
dev: false
|
||||
|
||||
Reference in New Issue
Block a user