<template>
|
<view class="page">
|
<!-- 刀具选择区 -->
|
<view class="top-section-grid">
|
<!-- 第一行:移除刀具目录按钮 -->
|
<view class="form-row">
|
<view class="form-cell">
|
<label class="form-label">刀具编号:</label>
|
<input class="input small-font" v-model="selectedToolNo" placeholder="请通过刀具目录选择" disabled />
|
</view>
|
<view class="form-cell form-cell-flex">
|
<label class="form-label highlight-label">使用上限:</label>
|
<input class="input small-font input-use-limit" type="number" v-model="useLimitInput" placeholder="每次上刀时手填上限" :disabled="!selectedToolNo || loadingForm" />
|
<label class="form-label" style="margin-left:16px;">寿命比预警值:</label>
|
<view class="input-with-unit">
|
<input class="input small-font input-life-warn"
|
v-model="lifeWarnInput"
|
placeholder="如90"
|
:disabled="!selectedToolNo || loadingForm" />
|
<span class="unit-text">%</span>
|
</view>
|
</view>
|
</view>
|
|
<!-- 第二行:在最后添加刀具目录按钮 -->
|
<view class="form-row">
|
<!-- 刀具名称 -->
|
<view class="form-cell">
|
<label class="form-label">刀具名称:</label>
|
<input class="input small-font input-tool-name" v-model="toolName" placeholder="刀具带出" disabled />
|
</view>
|
<!-- 规格型号 -->
|
<view class="form-cell">
|
<label class="form-label">规格型号:</label>
|
<input class="input small-font input-tool-model" v-model="toolModel" placeholder="刀具带出" disabled />
|
</view>
|
<!-- 刀具目录按钮右移 -->
|
<view class="form-cell tool-dir-cell">
|
<button class="btn-blue large-btn" @click="openToolDialog" :disabled="loadingTools">刀具目录</button>
|
</view>
|
<!-- 新增:刷新按钮 -->
|
<view class="form-cell" style="flex:0 0 auto; margin-left:16px;">
|
<button class="btn-blue large-btn" @click="handleRefresh" :disabled="loadingForm || submitting">刷新</button>
|
</view>
|
</view>
|
</view> <!-- 这里闭合 top-section-grid -->
|
<!-- 操作按钮 - 移出 top-section-grid -->
|
<view class="button-row">
|
<button class="save-btn" @click="handleUpTool" :disabled="submitting || loadingForm">上刀提交(=调机开始)</button>
|
<button class="save-btn" @click="handleDownTool" :disabled="submitting || loadingForm">下刀提交</button>
|
<button class="cancel-btn" @click="cancel" :disabled="submitting || loadingForm">取消</button>
|
</view>
|
|
<!-- 刀具目录弹窗 -->
|
<view v-if="showToolDialog" class="dialog-overlay">
|
<view class="dialog">
|
<view class="form-group">
|
<input v-model="searchKey" placeholder="输入刀具编码、名称模糊搜索" class="input" @input="onSearchKeyInput" />
|
<button class="btn-blue" @click="searchTool" :disabled="loadingTools">搜索</button>
|
</view>
|
<view class="tool-list">
|
<button v-for="tool in filteredTools"
|
:key="tool.no"
|
class="tool-btn"
|
:class="{ active: activeToolNo === tool.no }"
|
@click="selectTool(tool)">
|
{{ tool.no }} | {{ tool.name }}
|
</button>
|
</view>
|
<view class="dialog-actions">
|
<div style="display: flex; align-items: center;">
|
<button class="btn-blue" @click="prevPage" :disabled="pageIndex === 1 || loadingTools">上一页</button>
|
<span style="margin: 0 12px;">第{{ pageIndex }}页 / 共{{ totalPages }}页</span>
|
<button class="btn-blue" @click="nextPage" :disabled="pageIndex === totalPages || loadingTools">下一页</button>
|
</div>
|
<div style="display: flex; gap: 18px; align-items: center;">
|
<button class="btn-blue" @click="confirmTool">确定</button>
|
<button class="btn-disabled" @click="closeToolDialog">取消</button>
|
</div>
|
</view>
|
</view>
|
</view>
|
|
<!-- 刀具使用记录表格 -->
|
<view class="table-section">
|
<table class="styled-table">
|
<thead>
|
<tr>
|
<th style="width:8%">刀具编号</th>
|
<th style="width:14%">刀具名称</th>
|
<th style="width:12%">上刀时间</th>
|
<th class="num" style="width:8%">上刀计数</th>
|
<th style="width:12%">下刀时间</th>
|
<th class="num" style="width:8%">下刀计数</th>
|
<th class="num" style="width:8%">使用次数</th>
|
<th class="num" style="width:8%">使用上限</th>
|
<th class="num" style="width:8%">寿命比%</th>
|
<th class="num" style="width:8%">寿命比预警值</th>
|
<th style="width:8%">预警状态</th>
|
</tr>
|
</thead>
|
<tbody>
|
<tr v-for="(item, idx) in toolRecords" :key="item.id" :class="{'row-odd': idx % 2 === 0}">
|
<td>{{ item.no }}</td>
|
<td class="left">{{ item.name }}</td>
|
<td>{{ item.upTime }}</td>
|
<td class="num">{{ item.upCount != null ? item.upCount : '' }}</td>
|
<td>{{ item.downTime }}</td>
|
<!--<td class="num">{{ item.downCount != null ? item.downCount : '' }}</td>-->
|
<!-- 下刀计数改为实时显示 currentCjNum -->
|
<td class="num">{{ item.currentCjNum != null ? item.currentCjNum : '' }}</td>
|
<td class="num">{{ item.useCount != null ? item.useCount : '' }}</td>
|
<td class="num">{{ item.useLimit != null ? item.useLimit : '' }}</td>
|
<td class="num">{{ item.lifePercent }}</td>
|
<td class="num">{{ item.lifeWarn }}</td>
|
<td :class="item.warnStatus === '预警' ? 'warn-cell' : (item.warnStatus === '正常' ? 'ok-cell' : '')">
|
<span v-if="item.warnStatus === '预警'" class="warn-badge">警告</span>
|
<span v-else>{{ item.warnStatus }}</span>
|
</td>
|
</tr>
|
<tr v-if="!toolRecords.length">
|
<td colspan="11">暂无数据</td>
|
</tr>
|
</tbody>
|
</table>
|
</view>
|
|
<!-- 说明 -->
|
<!--<view class="tool-desc">
|
<p style="color:red;">'使用上限'以下刀时的'使用上限'为计算标准</p>
|
<p style="color:red;">寿命比预警值默认为90%</p>
|
</view>-->
|
</view>
|
</template>
|
|
<script>
|
export default {
|
data() {
|
return {
|
machineNo: '',//机台编码
|
workOrderNo: '',//工单号
|
activeToolNo: '', // 当前选中的刀具编号
|
pageIndex: 1,
|
pageSize: 20, //单页显示的刀具数量
|
total: 0,
|
toolList: [],
|
selectedToolNo: '',
|
toolName: '',
|
toolModel: '',
|
showToolDialog: false,
|
searchKey: '',
|
filteredTools: [],
|
useLimitInput: '',
|
lifeWarnInput: '', // 新增:寿命比预警值原始输入
|
toolRecords: [],
|
loadingTools: false,
|
loadingForm: false,
|
submitting: false,
|
_searchTimer: null,
|
workOrderCurrentCjNum: null, // 工单当前数采
|
|
// 自动保存相关
|
autoSaveTimer: null,
|
isDirty: false, // 表单是否有未保存变更
|
autoSaveIntervalMs: 5 * 60 * 1000, // 默认 5 分钟
|
autoSaveEnabled: true,
|
autoSaveActionName: 'handleUpTool' // 自动触发的方法名,可改为自定义保存方法
|
};
|
},
|
computed: {
|
totalPages() {
|
return Math.max(1, Math.ceil(this.total / this.pageSize) || 1);
|
}
|
},
|
watch: {
|
// 标记脏数据:按需监听字段变化
|
selectedToolNo() { this.isDirty = true; },
|
useLimitInput() { this.isDirty = true; },
|
lifeWarnInput() { this.isDirty = true; },
|
toolName() { this.isDirty = true; },
|
toolModel() { this.isDirty = true; }
|
},
|
methods: {
|
//查询当前数采数,作为下刀计数实时显示
|
async fetchCurrentCjNum(toolNo) {
|
if (!this.machineNo) return null;
|
try {
|
const res = await this.$post({
|
url: '/Womdaa/GetWomdaasByShow',
|
data: JSON.stringify({ machineNo: this.machineNo }),
|
headers: { 'Content-Type': 'application/json' }
|
});
|
if (res.status === 0 && Array.isArray(res.data?.tbBillList)) {
|
const found = res.data.tbBillList.find(x =>
|
x.cutterId === toolNo || x.cutteR_ID === toolNo
|
);
|
return found ? (found.CurrentCjNum ?? found.currentCjNum ?? null) : null;
|
}
|
} catch (e) {
|
console.warn('获取currentCjNum失败', e);
|
}
|
return null;
|
},
|
|
// 新增:刷新按钮处理方法
|
async handleRefresh() {
|
// 刷新刀具列表和表单数据
|
this.fetchTools('');
|
if (this.machineNo && this.workOrderNo) {
|
await this.fetchFormData();
|
await this.fetchDefaultToolFromWorkOrder();
|
}
|
this.$showMessage('刷新完成');
|
},
|
// 自动保存:启动
|
startAutoSave() {
|
if (!this.autoSaveEnabled) return;
|
this.stopAutoSave();
|
this.autoSaveTimer = setInterval(() => {
|
this.autoSaveTick();
|
}, this.autoSaveIntervalMs);
|
},
|
// 自动保存:停止
|
stopAutoSave() {
|
if (this.autoSaveTimer) {
|
clearInterval(this.autoSaveTimer);
|
this.autoSaveTimer = null;
|
}
|
},
|
// 自动保存:每次定时执行时的逻辑
|
async autoSaveTick() {
|
if (!this.autoSaveEnabled) return;
|
if (!this.isDirty) return;
|
if (this.submitting || this.loadingForm) return;
|
|
const fn = this.autoSaveActionName && typeof this[this.autoSaveActionName] === 'function'
|
? this[this.autoSaveActionName]
|
: null;
|
if (!fn) {
|
console.warn('自动保存:未找到方法', this.autoSaveActionName);
|
return;
|
}
|
|
try {
|
this.submitting = true;
|
await fn.call(this); // 调用保存方法(例如 handleUpTool)
|
// 如果保存成功,清脏标记(保存方法内部若失败没有抛出可保持此方式)
|
this.isDirty = false;
|
} catch (e) {
|
console.error('自动保存失败:', e);
|
} finally {
|
this.submitting = false;
|
}
|
},
|
|
// 新增:寿命比预警值归一化 (返回 0~1 或 null)
|
normalizeLifeWarn(v) {
|
if (v == null) return null;
|
const raw = String(v).trim().replace(/[%%]/g, '');
|
if (raw === '') return null;
|
const num = Number(raw);
|
if (!isFinite(num) || num <= 0) return null;
|
return num > 1 ? (num / 100) : num;
|
},
|
openToolDialog() {
|
this.showToolDialog = true;
|
this.pageIndex = 1;
|
this.fetchTools(this.searchKey);
|
},
|
closeToolDialog() {
|
this.showToolDialog = false;
|
},
|
async fetchTools(searchKey) {
|
this.loadingTools = true;
|
try {
|
const res = await this.$post({
|
url: '/MesCutterLedger/QueryTools',
|
data: JSON.stringify({
|
searchKey,
|
pageIndex: this.pageIndex,
|
pageSize: this.pageSize
|
}),
|
headers: { 'Content-Type': 'application/json' }
|
});
|
|
if (res.status === 0) {
|
const payload = Array.isArray(res.data) ? res.data
|
: (res.data && res.data.tbBillList) ? res.data.tbBillList
|
: (res.data && res.data.data) ? res.data.data
|
: [];
|
|
const getField = (obj, ...keys) => {
|
for (const k of keys) if (obj?.[k] !== undefined && obj?.[k] !== null) return obj[k];
|
return null;
|
};
|
|
// 这里需要把 lifeWarn 字段也带出来
|
const mapped = (payload || []).map(t => ({
|
no: getField(t, 'cutterId', 'CUTTER_ID', 'cutteR_ID', 'daA001', 'no'),
|
name: getField(t, 'cutterName', 'CUTTER_NAME', 'cutteR_NAME', 'name'),
|
model: getField(t, 'cutterModel', 'CUTTER_MODEL', 'cutteR_MODEL', 'model'),
|
lifeWarn: getField(t, 'modlLifeWorning', 'lifeWarn', 'LIFE_WARN', 'lifE_WARN')
|
}));
|
|
this.filteredTools = mapped;
|
this.toolList = mapped.slice();
|
|
const totalFromRes = Number(
|
res.data?.total ?? res.data?.totalCount ?? res.total ?? res.totalCount ?? mapped.length
|
);
|
this.total = Number.isFinite(totalFromRes) ? totalFromRes : mapped.length;
|
if (this.pageIndex > this.totalPages) {
|
this.pageIndex = this.totalPages;
|
}
|
} else {
|
this.$showMessage(res.message || '查询失败');
|
}
|
} catch (err) {
|
console.error('fetchTools 错误:', err);
|
this.$showMessage('查询刀具失败,请检查网络或接口');
|
} finally {
|
this.loadingTools = false;
|
}
|
},
|
async prevPage() {
|
if (this.pageIndex > 1) {
|
this.pageIndex--;
|
await this.fetchTools(this.searchKey);
|
}
|
},
|
async nextPage() {
|
if (this.pageIndex < this.totalPages) {
|
this.pageIndex++;
|
await this.fetchTools(this.searchKey);
|
}
|
},
|
onSearchKeyInput() {
|
clearTimeout(this._searchTimer);
|
this._searchTimer = setTimeout(() => {
|
this.searchTool();
|
}, 300);
|
},
|
async searchTool() {
|
this.pageIndex = 1;
|
await this.fetchTools(this.searchKey);
|
},
|
selectTool(tool) {
|
this.selectedToolNo = tool.no;
|
this.toolName = tool.name;
|
this.toolModel = tool.model;
|
this.activeToolNo = tool.no;
|
// 新增:带出寿命比预警值
|
if (tool.lifeWarn !== undefined && tool.lifeWarn !== null) {
|
// 格式化为百分比字符串
|
const warn = Number(tool.lifeWarn);
|
this.lifeWarnInput = warn <= 1 ? (warn * 100).toFixed(0) : warn.toFixed(0);
|
} else {
|
this.lifeWarnInput = '';
|
}
|
},
|
confirmTool() {
|
this.showToolDialog = false;
|
},
|
async setUseLimit() {
|
if (!this.selectedToolNo) {
|
this.$showMessage('请选择刀具后再设置上限');
|
return;
|
}
|
const v = Number(this.useLimitInput);
|
if (isNaN(v) || v <= 0) {
|
this.$showMessage('请输入有效的使用上限');
|
return;
|
}
|
this.toolRecords = this.toolRecords.map(r => {
|
if (r.no === this.selectedToolNo) {
|
return { ...r, useLimit: v };
|
}
|
return r;
|
});
|
this.toolList = this.toolList.map(t => t.no === this.selectedToolNo ? { ...t, useLimit: v } : t);
|
this.$showMessage('使用上限已设置(仅前端显示)');
|
},
|
// 上刀时传递 sdjs = 工单 currentCjNum
|
async handleUpTool() {
|
if (!this.workOrderNo) { this.$showMessage('工单号不能为空'); return; }
|
if (!this.machineNo) { this.$showMessage('机台号不能为空'); return; }
|
if (!this.selectedToolNo) { this.$showMessage('刀具编号不能为空'); return; }
|
//上刀强制录入使用上限
|
if (!this.useLimitInput) { this.$showMessage('使用上限不能为空'); return; }
|
const useLimit = Number(this.useLimitInput);
|
if (isNaN(useLimit) || useLimit <= 0) { this.$showMessage('请输入有效的使用上限'); return; }
|
|
// sdjs 使用工单 currentCjNum
|
const sdjs = this.workOrderCurrentCjNum != null ? Number(this.workOrderCurrentCjNum) : null;
|
const lifeWarnRatio = this.normalizeLifeWarn(this.lifeWarnInput);
|
const payload = {
|
workOrderNo: this.workOrderNo,
|
machineNo: this.machineNo,
|
toolNo: this.selectedToolNo,
|
type: '上机',
|
useLimit,
|
sdjs,// 上刀计数(工单当前数采)
|
modlLifeWorning: lifeWarnRatio // 新增
|
};
|
try {
|
this.submitting = true;
|
const res = await this.$post({
|
url: '/MesCutterLedger/SubmitToolAction',
|
data: JSON.stringify(payload),
|
headers: { 'Content-Type': 'application/json' }
|
});
|
if (res.data && res.data.outSum === "0") {
|
uni.showToast({
|
title: res.data.outMsg || '',
|
icon: 'error'
|
});
|
return;
|
} else if (res.data && res.data.outMsg) {
|
uni.showToast({
|
title: res.data.outMsg,
|
icon: 'none'
|
});
|
}
|
// 后续逻辑继续执行
|
if (res.status === 0) {
|
await this.fetchFormData();
|
}
|
} catch (err) {
|
console.error(err);
|
this.$showMessage('上刀提交失败,请检查网络');
|
throw err; // 抛出以便自动保存逻辑捕获并保留 isDirty
|
} finally {
|
this.submitting = false;
|
}
|
},
|
// 下刀时传递 xdjs = 工单 currentCjNum
|
async handleDownTool() {
|
if (!this.workOrderNo) { this.$showMessage('工单号不能为空'); return; }
|
if (!this.machineNo) { this.$showMessage('机台号不能为空'); return; }
|
if (!this.selectedToolNo) { this.$showMessage('刀具编号不能为空'); return; }
|
//下刀不强制录入使用上限
|
//if (!this.useLimitInput) { this.$showMessage('使用上限不能为空'); return; }
|
const useLimit = Number(this.useLimitInput);
|
//if (isNaN(useLimit) || useLimit <= 0) { this.$showMessage('请输入有效的使用上限'); return; }
|
|
// 下刀计数同样取工单最新采集数
|
const xdjs = this.workOrderCurrentCjNum != null ? Number(this.workOrderCurrentCjNum) : null;
|
const lifeWarnRatio = this.normalizeLifeWarn(this.lifeWarnInput);
|
const payload = {
|
workOrderNo: this.workOrderNo,
|
machineNo: this.machineNo,
|
toolNo: this.selectedToolNo,
|
type: '下机',
|
useLimit,
|
xdjs,// 下刀计数
|
modlLifeWorning: lifeWarnRatio // 新增
|
};
|
try {
|
this.submitting = true;
|
const res = await this.$post({
|
url: '/MesCutterLedger/SubmitToolAction',
|
data: JSON.stringify(payload),
|
headers: { 'Content-Type': 'application/json' }
|
});
|
if (res.data && res.data.outSum === "0") {
|
uni.showToast({
|
title: res.data.outMsg || '',
|
icon: 'error'
|
});
|
return;
|
} else if (res.data && res.data.outMsg) {
|
uni.showToast({
|
title: res.data.outMsg,
|
icon: 'none'
|
});
|
}
|
// 后续逻辑继续执行
|
if (res.status === 0) {
|
await this.fetchFormData();
|
}
|
} catch (err) {
|
console.error(err);
|
this.$showMessage('下刀提交失败,请检查网络');
|
throw err;
|
} finally {
|
this.submitting = false;
|
}
|
},
|
cancel() {
|
this.selectedToolNo = '';
|
this.toolName = '';
|
this.toolModel = '';
|
this.useLimitInput = '';
|
this.lifeWarnInput = ''; // 新增:清空
|
this.isDirty = false;
|
},
|
async fetchFormData() {
|
if (!this.workOrderNo || !this.machineNo) {
|
console.warn('工单号或机台号为空,跳过获取表单数据');
|
return;
|
}
|
this.loadingForm = true;
|
const payload = {
|
workOrderNo: this.workOrderNo.trim(),
|
machineNo: this.machineNo.trim()
|
};
|
try {
|
const res = await this.$post({
|
url: '/MesCutterLedger/GetFormData',
|
data: JSON.stringify(payload),
|
headers: { 'Content-Type': 'application/json' }
|
});
|
if (res.status !== 0) {
|
this.$showMessage(res.message || '获取表单数据失败');
|
this.toolRecords = [];
|
return;
|
}
|
const list = Array.isArray(res.data) ? res.data
|
: (res.data && res.data.tbBillList) ? res.data.tbBillList
|
: (res.data && res.data.data) ? res.data.data
|
: [];
|
|
const getField = (obj, ...keys) => {
|
for (const k of keys) if (obj?.[k] !== undefined && obj?.[k] !== null) return obj[k];
|
return null;
|
};
|
|
const mapped = (list || []).map(t => {
|
const upTimeRaw = getField(t, 'uP_TIME', 'UP_TIME', 'uPTime', 'UPTIME', 'UpTime');
|
const downTimeRaw = getField(t, 'dowN_TIME', 'DOWN_TIME', 'downTime', 'DOWNTIME');
|
const lifeWarnRaw = getField(t, 'lifE_WARN', 'LIFE_WARN', 'lifeWarn', 'LIFEWARN');
|
const currentCjNum = getField(t, 'currentCjNum', 'CURRENTCJNUM', 'CurrentCjNum');
|
|
const useCount = getField(t, 'usE_COUNT', 'USE_COUNT', 'useCount');
|
const useLimit = getField(t, 'usE_LIMIT', 'USE_LIMIT', 'useLimit');
|
|
let percent = '';
|
if (useCount != null && useLimit != null && !isNaN(useCount) && !isNaN(useLimit) && Number(useLimit) > 0) {
|
percent = ((Number(useCount) / Number(useLimit)) * 100).toFixed(0) + '%';
|
}
|
|
const parseNumber = v => {
|
if (v === null || v === undefined || v === '') return null;
|
const s = String(v).replace(/[,%%]/g, '').trim();
|
const n = parseFloat(s);
|
return Number.isFinite(n) ? n : null;
|
};
|
const formatPercent = n => {
|
if (n === null || n === undefined || isNaN(n)) return '';
|
if (n <= 1) return `${(n * 100).toFixed(0)}%`;
|
return `${Number(n).toFixed(0)}%`;
|
};
|
const lifeWarnNum = parseNumber(lifeWarnRaw);
|
|
let warnStatus = getField(t, 'status', 'STATUS') || '';
|
if (lifeWarnNum !== null && useCount != null && useLimit != null && !isNaN(useCount) && !isNaN(useLimit) && Number(useLimit) > 0) {
|
const percentNum = Number(useCount) / Number(useLimit);
|
warnStatus = (percentNum >= lifeWarnNum) ? '预警' : '正常';
|
} else {
|
warnStatus = warnStatus || '未知';
|
}
|
|
return {
|
id: getField(t, 'id', 'ID') || `${getField(t, 'cutteR_ID') || getField(t, 'CUTTER_ID') || ''}-${upTimeRaw || ''}`,
|
no: getField(t, 'cutteR_ID', 'CUTTER_ID', 'cutterId', 'no') || '',
|
name: getField(t, 'cutteR_NAME', 'CUTTER_NAME', 'cutterName', 'name') || '',
|
upTime: this.formatDateTime(upTimeRaw),
|
upCount: getField(t, 'uP_COUNT', 'UP_COUNT', 'upCount') ?? '',
|
downTime: this.formatDateTime(downTimeRaw),
|
downCount: getField(t, 'dowN_COUNT', 'DOWN_COUNT', 'downCount') ?? '',
|
useCount: useCount ?? '',
|
useLimit: useLimit ?? '',
|
lifePercent: percent,
|
lifeWarn: formatPercent(lifeWarnNum),
|
warnStatus,
|
currentCjNum
|
};
|
});
|
|
// 新增:按上刀时间降序排序(越晚的越上面)
|
mapped.sort((a, b) => {
|
// 时间格式如 "10-24 16:03",转为 Date 对象比较
|
const parse = s => {
|
if (!s) return 0;
|
// 补年份,假设都是今年
|
const year = new Date().getFullYear();
|
return new Date(`${year}-${s.replace(/-/g, '-')}:00`).getTime();
|
};
|
return parse(b.upTime) - parse(a.upTime); // 注意这里顺序反过来
|
});
|
|
this.toolRecords = mapped;
|
const totalFromRes = Number(
|
res.data?.total ?? res.data?.totalCount ?? res.total ?? res.totalCount ?? mapped.length
|
);
|
this.total = Number.isFinite(totalFromRes) ? totalFromRes : mapped.length;
|
} catch (error) {
|
console.error('获取表单数据错误:', error);
|
this.$showMessage('获取数据失败,请检查网络连接');
|
this.toolRecords = [];
|
} finally {
|
this.loadingForm = false;
|
}
|
},
|
// 自动带出工单刀具信息,并获取工单最新采集数
|
async fetchDefaultToolFromWorkOrder() {
|
if (!this.machineNo) return;
|
try {
|
const res = await this.$post({
|
url: '/Womdaa/GetWomdaasByShow',
|
data: JSON.stringify({ machineNo: this.machineNo }),
|
headers: { 'Content-Type': 'application/json' }
|
});
|
if (res.status === 0 && Array.isArray(res.data?.tbBillList) && res.data.tbBillList.length > 0) {
|
const order = res.data.tbBillList[0];
|
this.selectedToolNo = order.cutterId || order.cutteR_ID || '';
|
this.toolName = order.cutterName || order.cutteR_NAME || '';
|
this.toolModel = order.cutterModel || order.cutteR_MODEL || '';
|
// 关键:获取工单最新采集数
|
this.workOrderCurrentCjNum = order.CurrentCjNum ?? order.currentCjNum ?? null;
|
// 新增:自动填充寿命比预警值
|
if (order.modlLifeWorning !== undefined && order.modlLifeWorning !== null) {
|
const warn = Number(order.modlLifeWorning);
|
this.lifeWarnInput = warn <= 1 ? (warn * 100).toFixed(0) : warn.toFixed(0);
|
} else {
|
this.lifeWarnInput = '';
|
}
|
} else {
|
this.workOrderCurrentCjNum = null;
|
}
|
} catch (e) {
|
console.warn('自动带出工单刀具失败', e);
|
this.workOrderCurrentCjNum = null;
|
}
|
},
|
formatDateTime(dateTimeStr) {
|
if (!dateTimeStr) return '';
|
try {
|
const s = String(dateTimeStr).trim();
|
if (/^\d{10}$/.test(s)) {
|
const d = new Date(Number(s) * 1000);
|
return `${d.getMonth() + 1}-${d.getDate()} ${d.getHours()}:${String(d.getMinutes()).padStart(2, '0')}`;
|
}
|
if (/^\d{13}$/.test(s)) {
|
const d = new Date(Number(s));
|
return `${d.getMonth() + 1}-${d.getDate()} ${d.getHours()}:${String(d.getMinutes()).padStart(2, '0')}`;
|
}
|
const date = new Date(dateTimeStr);
|
if (!isNaN(date.getTime())) {
|
return `${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${String(date.getMinutes()).padStart(2, '0')}`;
|
}
|
const match = String(dateTimeStr).match(/(\d{1,4}[-\/]\d{1,2}[-\/]\d{1,2}).*?(\d{1,2}:\d{2})/);
|
if (match) return `${match[1].replace(/-/g, '/').replace(/^\d{4}\//, (m) => m)} ${match[2]}`;
|
return String(dateTimeStr);
|
} catch {
|
return String(dateTimeStr);
|
}
|
}
|
},
|
mounted() {
|
this.fetchTools('');
|
this.machineNo = uni.getStorageSync('machineNo') || '';
|
this.workOrderNo = uni.getStorageSync('daa001') || '';
|
// 去除默认预警值 (90% -> 0.9)
|
//this.lifeWarnInput = '90';
|
|
if (this.machineNo && this.workOrderNo) {
|
this.fetchFormData();
|
this.fetchDefaultToolFromWorkOrder();
|
} else {
|
console.warn('机台号或工单号为空,无法获取表单数据');
|
}
|
|
// 启动自动保存定时器
|
//this.startAutoSave();
|
},
|
beforeDestroy() {
|
// 清理定时器,防止内存泄漏
|
this.stopAutoSave();
|
}
|
};
|
</script>
|
|
<style scoped>
|
.top-section-grid {
|
display: flex;
|
flex-direction: column;
|
justify-content: center;
|
align-items: flex-start;
|
gap: 12px;
|
margin-bottom: 2vh;
|
width: 99vw;
|
max-width: 1920px;
|
margin-left: auto;
|
margin-right: auto;
|
padding: 12px;
|
background: #f8f9fa;
|
border-radius: 8px;
|
border: 1px solid #e9ecef;
|
}
|
|
.form-row {
|
display: flex;
|
flex-direction: row;
|
justify-content: flex-start;
|
align-items: center;
|
width: 100%;
|
margin-bottom: 12px;
|
}
|
|
.form-row:last-child {
|
margin-bottom: 0;
|
}
|
|
.form-cell {
|
display: flex;
|
align-items: center;
|
min-width: 0;
|
margin-right: 0;
|
}
|
|
.form-cell:nth-child(1) {
|
flex: 0 0 28%;
|
min-width: 180px;
|
}
|
|
.form-cell:nth-child(2) {
|
flex: 0 0 32%;
|
min-width: 180px;
|
justify-content: flex-start;
|
}
|
|
.form-cell:nth-child(3) {
|
flex: 0 0 auto;
|
min-width: 0;
|
max-width: 220px; /* 限制最大宽度,避免撑开 */
|
justify-content: flex-end;
|
}
|
|
.form-label {
|
width: auto;
|
font-weight: bold;
|
color: #333;
|
white-space: nowrap;
|
margin-right: 4px;
|
flex-shrink: 0;
|
}
|
|
.input {
|
padding: 14px 20px;
|
border: 1px solid #dcdfe6;
|
flex: 1;
|
margin-right: 0;
|
border-radius: 4px;
|
background: #fff;
|
min-width: 0;
|
height: 56px !important;
|
box-sizing: border-box;
|
}
|
|
.input.small-font {
|
/* 不设置字体大小 */
|
}
|
|
.input:disabled {
|
background-color: #f5f7fa;
|
color: #c0c4cc;
|
cursor: not-allowed;
|
}
|
|
.input-with-unit {
|
display: flex;
|
align-items: center;
|
gap: 0;
|
}
|
|
.input-with-unit .input {
|
width: 60px !important;
|
min-width: 0 !important;
|
margin-right: 2px !important;
|
text-align: center;
|
padding-left: 2px;
|
padding-right: 2px;
|
}
|
|
.input-with-unit .input {
|
margin-right: 4px !important;
|
width: 60px !important;
|
min-width: 0 !important;
|
text-align: center;
|
}
|
|
.unit-text {
|
margin-left: 0 !important;
|
padding-left: 0 !important;
|
font-size: 22px !important;
|
}
|
|
.btn-blue, .btn-blue.large-btn {
|
background-color: #1890ff;
|
color: white;
|
border: none;
|
padding: 14px 32px;
|
border-radius: 4px;
|
cursor: pointer;
|
font-weight: 500;
|
white-space: nowrap;
|
height: 70px !important;
|
box-sizing: border-box;
|
transition: all 0.3s;
|
flex-shrink: 0;
|
min-width: 140px;
|
margin-left: 24px;
|
}
|
|
.btn-blue.large-btn {
|
padding: 0 60px;
|
min-width: 180px;
|
height: 72px !important;
|
margin-left: 0 !important;
|
font-size: 1.3em;
|
box-shadow: 0 2px 8px rgba(24,144,255,0.08);
|
line-height: 72px; /* 垂直居中 */
|
display: inline-block; /* 保证line-height生效 */
|
vertical-align: middle;
|
}
|
|
.btn-blue:hover {
|
background-color: #40a9ff;
|
}
|
|
.btn-blue:active {
|
background-color: #096dd9;
|
}
|
|
.btn-blue:disabled {
|
background: #bae7ff;
|
color: #91d5ff;
|
cursor: not-allowed;
|
}
|
|
.button-row {
|
display: flex;
|
justify-content: center;
|
gap: 32px;
|
margin: 2vh 0;
|
}
|
|
.save-btn, .cancel-btn {
|
width: 28%;
|
padding: 1.5vh;
|
background-color: #00A2E9;
|
color: white;
|
border: none;
|
text-align: center;
|
border-radius: 6px;
|
transition: background 0.2s;
|
}
|
|
.cancel-btn {
|
background-color: #eee;
|
color: #333;
|
}
|
|
.save-btn:hover {
|
background-color: #40a9ff;
|
}
|
|
.save-btn:active {
|
background-color: #096dd9;
|
}
|
|
.save-btn:disabled, .cancel-btn:disabled {
|
opacity: 0.6;
|
cursor: not-allowed;
|
}
|
|
/* 弹窗样式 */
|
.dialog-overlay {
|
position: fixed;
|
top: 0;
|
left: 0;
|
right: 0;
|
bottom: 0;
|
background: rgba(0,0,0,0.18);
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
z-index: 1000;
|
}
|
|
.dialog {
|
background: #fff;
|
padding: 3vh 3vw;
|
border-radius: 18px;
|
width: 90vw;
|
max-width: 1600px;
|
min-height: 70vh;
|
max-height: 85vh;
|
box-sizing: border-box;
|
box-shadow: 0 8px 32px rgba(0,0,0,0.12);
|
display: flex;
|
flex-direction: column;
|
}
|
|
.form-group {
|
display: flex;
|
gap: 12px;
|
align-items: center;
|
margin-bottom: 2vh;
|
flex-shrink: 0;
|
}
|
|
.form-group .input {
|
flex: 1;
|
min-width: 300px;
|
border-radius: 6px;
|
background: #f8f8f8;
|
}
|
|
.tool-list {
|
display: flex;
|
flex-wrap: wrap;
|
margin: 1vh 0;
|
flex: 1;
|
overflow-y: auto;
|
gap: 12px;
|
align-content: flex-start;
|
}
|
|
.tool-btn {
|
flex: 0 0 calc(20% - 10px);
|
box-sizing: border-box;
|
padding: 10px 12px;
|
background: #f5f5f5;
|
border: 1px solid #d0d0d0;
|
border-radius: 8px;
|
cursor: pointer;
|
color: #333;
|
text-align: left;
|
min-height: 50px;
|
transition: background 0.15s, color 0.15s;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
white-space: nowrap;
|
line-height: 1.4;
|
}
|
|
.tool-btn.active {
|
background: #00A2E9;
|
color: #fff;
|
font-weight: bold;
|
border: 1.5px solid #0086c2;
|
}
|
|
.dialog-actions {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-top: 2vh;
|
padding-top: 2vh;
|
border-top: 1px solid #eee;
|
flex-shrink: 0;
|
}
|
|
.dialog-actions > div:first-child {
|
display: flex;
|
align-items: center;
|
gap: 24px;
|
}
|
|
.dialog-actions > div:last-child {
|
display: flex;
|
flex-direction: row;
|
gap: 18px;
|
align-items: center;
|
}
|
|
.dialog-actions .btn-blue {
|
padding: 10px 24px;
|
min-width: 120px;
|
}
|
|
.btn-disabled {
|
background: #f2f2f2;
|
color: #999;
|
border: none;
|
padding: 10px 24px;
|
border-radius: 4px;
|
font-weight: 500;
|
cursor: not-allowed;
|
box-shadow: none;
|
min-width: 120px;
|
}
|
|
.table-section {
|
display: flex;
|
justify-content: center;
|
margin: 1vh 0;
|
overflow-x: auto;
|
width: 100%; /* 确保表格容器占满宽度 */
|
}
|
|
table.styled-table {
|
max-width: 1800px; /* 从1600px增加到1800px */
|
width: 98vw; /* 从95vw增加到98vw */
|
margin: 0 auto;
|
border-collapse: separate;
|
border-spacing: 0;
|
border: 2px solid #bfbfbf;
|
background: #fff;
|
border-radius: 12px;
|
overflow: hidden;
|
box-shadow: 0 2px 12px rgba(0,0,0,0.06);
|
}
|
|
table.styled-table thead th {
|
background: #fafafa;
|
border-bottom: 2px solid #bfbfbf;
|
padding: 16px 10px;
|
font-weight: bold;
|
text-align: center;
|
}
|
|
table.styled-table tbody td {
|
border-bottom: 1px solid #e8e8e8;
|
padding: 14px 10px;
|
vertical-align: middle;
|
text-align: center;
|
}
|
|
.table-section table th:first-child, .table-section table td:first-child {
|
border-left: 2px solid #bfbfbf;
|
}
|
|
.table-section table th:last-child, .table-section table td:last-child {
|
border-right: 2px solid #bfbfbf;
|
}
|
|
.row-odd {
|
background: #fff;
|
}
|
|
.row-odd + tr {
|
background: #fafafa;
|
}
|
|
.num {
|
text-align: center;
|
padding-right: 0;
|
font-variant-numeric: tabular-nums;
|
}
|
|
.left {
|
text-align: left;
|
padding-left: 8px;
|
}
|
|
.warn-cell {
|
color: #d93025;
|
font-weight: bold;
|
}
|
|
.ok-cell {
|
color: #333;
|
}
|
|
.warn-badge {
|
display: inline-block;
|
background: #ff4d4f;
|
color: #fff;
|
padding: 2px 6px;
|
border-radius: 3px;
|
font-weight: bold;
|
}
|
|
/* 响应式调整 */
|
@media (max-width: 1200px) {
|
.form-cell {
|
margin: 0 4px;
|
}
|
|
.form-label {
|
width: 90px;
|
}
|
|
.input {
|
padding: 10px 14px;
|
}
|
|
.btn-blue {
|
padding: 10px 18px;
|
}
|
}
|
|
.input-life-warn {
|
width: 48px !important; /* 稍微放宽,避免字体溢出 */
|
min-width: 0 !important;
|
margin-right: 4px;
|
padding-left: 2px;
|
padding-right: 2px;
|
font-size: 22px !important;
|
text-align: center;
|
box-sizing: border-box;
|
}
|
|
.input,
|
.input.small-font,
|
.input-use-limit,
|
.input-tool-model,
|
.input-tool-name,
|
.input-life-warn {
|
font-size: 22px !important;
|
}
|
|
.input-tool-model {
|
width: 320px !important; /* 原为200px,现加长 */
|
min-width: 0 !important;
|
margin-right: 16px;
|
}
|
|
.input-tool-name {
|
width: 120px !important;
|
min-width: 0 !important;
|
margin-right: 16px;
|
}
|
|
.highlight-label {
|
color: #1890ff !important;
|
}
|
|
.tool-dir-cell {
|
flex: 0 0 auto !important;
|
justify-content: flex-start !important;
|
display: flex !important;
|
align-items: center;
|
margin-left: 16px; /* 与规格型号输入框拉开距离 */
|
}
|
|
.form-cell-flex {
|
display: flex;
|
align-items: center;
|
flex: 0 0 60%;
|
min-width: 320px;
|
}
|
|
.form-cell-flex .form-label {
|
margin-right: 4px;
|
width: auto;
|
}
|
|
.form-cell-flex .input-use-limit {
|
margin-right: 12px;
|
}
|
|
.input-use-limit {
|
width: 450px !important;
|
min-width: 0 !important;
|
margin-right: 16px;
|
flex: none !important; /* 关键:彻底禁止flex拉伸 */
|
}
|
|
</style>
|