数据贯通完成,规程重构
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package com.klp.framework.client;
|
package com.klp.framework.client;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import com.klp.framework.config.SqlServerApiProperties;
|
import com.klp.framework.config.SqlServerApiProperties;
|
||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpEntity;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
@@ -73,7 +74,9 @@ public class SqlServerApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class TableSchemaRequest {
|
public static class TableSchemaRequest {
|
||||||
|
@JsonProperty("table_type")
|
||||||
private String tableType;
|
private String tableType;
|
||||||
|
@JsonProperty("table_name")
|
||||||
private String tableName;
|
private String tableName;
|
||||||
|
|
||||||
public TableSchemaRequest() {
|
public TableSchemaRequest() {
|
||||||
@@ -102,6 +105,7 @@ public class SqlServerApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class ExecuteSqlRequest {
|
public static class ExecuteSqlRequest {
|
||||||
|
@JsonProperty("table_type")
|
||||||
private String tableType;
|
private String tableType;
|
||||||
private String sql;
|
private String sql;
|
||||||
private Map<String, Object> params;
|
private Map<String, Object> params;
|
||||||
@@ -311,10 +315,23 @@ public class SqlServerApiClient {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExecuteSqlResponse queryPlanList() {
|
public ExecuteSqlResponse queryPlanList(int page, int pageSize) {
|
||||||
|
int endRow = page * pageSize;
|
||||||
|
int startRow = endRow - pageSize;
|
||||||
|
Map<String, Object> params = new java.util.HashMap<>();
|
||||||
|
params.put("startRow", startRow);
|
||||||
|
params.put("endRow", endRow);
|
||||||
return executeSql(
|
return executeSql(
|
||||||
"oracle",
|
"oracle",
|
||||||
"select * from JXPLTCM.PLTCM_PDI_PLAN order by INSDATE desc",
|
"select * from (select t.*, ROWNUM rn from (select * from JXPLTCM.PLTCM_PDI_PLAN order by INSDATE desc) t where ROWNUM <= :endRow) where rn > :startRow",
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExecuteSqlResponse queryPlanCount() {
|
||||||
|
return executeSql(
|
||||||
|
"oracle",
|
||||||
|
"select count(*) as total from JXPLTCM.PLTCM_PDI_PLAN",
|
||||||
emptyParams()
|
emptyParams()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public class SqlServerApiClientConfig {
|
|||||||
public RestTemplate sqlServerApiRestTemplate(RestTemplateBuilder builder) {
|
public RestTemplate sqlServerApiRestTemplate(RestTemplateBuilder builder) {
|
||||||
return builder
|
return builder
|
||||||
.setConnectTimeout(Duration.ofSeconds(5))
|
.setConnectTimeout(Duration.ofSeconds(5))
|
||||||
.setReadTimeout(Duration.ofSeconds(30))
|
.setReadTimeout(Duration.ofSeconds(60))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,12 +32,24 @@ public class SqlServerApiBusinessService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计划列表:查询所有计划,按时间倒序。
|
* 计划列表(分页):按时间倒序,page 从 1 开始。
|
||||||
* <p>
|
|
||||||
* 这是后续按 coilId 关联 SEG、实时数据的入口。
|
|
||||||
*/
|
*/
|
||||||
public PlanListView getPlanList() {
|
public PlanListView getPlanList(int page, int pageSize) {
|
||||||
return PlanListView.fromExecuteSqlResponse(client.queryPlanList());
|
return PlanListView.fromExecuteSqlResponse(client.queryPlanList(page, pageSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计划总数。
|
||||||
|
*/
|
||||||
|
public long getPlanCount() {
|
||||||
|
SqlServerApiClient.ExecuteSqlResponse resp = client.queryPlanCount();
|
||||||
|
List<Map<String, Object>> rows = asRowList(resp);
|
||||||
|
if (rows.isEmpty()) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
Object total = rows.get(0).get("total");
|
||||||
|
Number n = asNumber(total);
|
||||||
|
return n == null ? 0L : n.longValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.klp.web.controller.sqlserver;
|
package com.klp.framework.sqlserver;
|
||||||
|
|
||||||
import com.klp.common.core.domain.R;
|
import com.klp.common.core.domain.R;
|
||||||
import com.klp.framework.service.SqlServerApiBusinessService;
|
import com.klp.framework.service.SqlServerApiBusinessService;
|
||||||
@@ -6,8 +6,12 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sql-server-api 业务查询接口。
|
* sql-server-api 业务查询接口。
|
||||||
* <p>
|
* <p>
|
||||||
@@ -21,11 +25,24 @@ public class SqlServerApiController {
|
|||||||
private final SqlServerApiBusinessService businessService;
|
private final SqlServerApiBusinessService businessService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计划列表。
|
* 计划列表(分页)。
|
||||||
|
* page 从 1 开始,默认第 1 页,每页 20 条。
|
||||||
*/
|
*/
|
||||||
@GetMapping("/plans")
|
@GetMapping("/plans")
|
||||||
public R<SqlServerApiBusinessService.PlanListView> planList() {
|
public R<SqlServerApiBusinessService.PlanListView> planList(
|
||||||
return R.ok(businessService.getPlanList());
|
@RequestParam(defaultValue = "1") int page,
|
||||||
|
@RequestParam(defaultValue = "20") int pageSize) {
|
||||||
|
return R.ok(businessService.getPlanList(page, pageSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计划总数,用于前端分页器。
|
||||||
|
*/
|
||||||
|
@GetMapping("/plans/count")
|
||||||
|
public R<Map<String, Long>> planCount() {
|
||||||
|
Map<String, Long> result = new HashMap<>();
|
||||||
|
result.put("total", businessService.getPlanCount());
|
||||||
|
return R.ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ klp:
|
|||||||
# 开发环境文件存储目录
|
# 开发环境文件存储目录
|
||||||
directory-path: testDirectory
|
directory-path: testDirectory
|
||||||
|
|
||||||
|
--- # sql-server-api 中间件配置(开发/测试环境)
|
||||||
|
sql-server-api:
|
||||||
|
host: 140.143.206.120
|
||||||
|
port: 15000
|
||||||
|
base-url: http://${sql-server-api.host}:${sql-server-api.port}
|
||||||
|
|
||||||
--- # OEE 聚合(klp-da)多服务地址配置
|
--- # OEE 聚合(klp-da)多服务地址配置
|
||||||
da:
|
da:
|
||||||
oee:
|
oee:
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ klp:
|
|||||||
# 生产环境文件存储目录
|
# 生产环境文件存储目录
|
||||||
directory-path: /home/ubuntu/oa/folder
|
directory-path: /home/ubuntu/oa/folder
|
||||||
|
|
||||||
|
--- # sql-server-api 中间件配置(生产环境)
|
||||||
|
sql-server-api:
|
||||||
|
host: 192.168.0.219
|
||||||
|
port: 15000
|
||||||
|
base-url: http://${sql-server-api.host}:${sql-server-api.port}
|
||||||
|
|
||||||
--- # OEE 聚合(klp-da)多服务地址配置
|
--- # OEE 聚合(klp-da)多服务地址配置
|
||||||
da:
|
da:
|
||||||
oee:
|
oee:
|
||||||
|
|||||||
@@ -66,14 +66,6 @@ user:
|
|||||||
lockTime: 10
|
lockTime: 10
|
||||||
|
|
||||||
# sql-server-api 中间件配置
|
# sql-server-api 中间件配置
|
||||||
sql-server-api:
|
|
||||||
# 请求地址,请替换为实际 IP
|
|
||||||
host: 127.0.0.1
|
|
||||||
# 请求端口,请替换为实际端口
|
|
||||||
port: 8080
|
|
||||||
# 基础访问地址(可直接注入使用)
|
|
||||||
base-url: http://${sql-server-api.host}:${sql-server-api.port}
|
|
||||||
|
|
||||||
# Spring配置
|
# Spring配置
|
||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
|
|||||||
@@ -1,36 +1,50 @@
|
|||||||
import axios from 'axios'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
export default function createTimingFetch(url) {
|
// 计划列表(分页)
|
||||||
const request = axios.create({
|
export function getTimingPlanList(page = 1, pageSize = 20) {
|
||||||
baseURL: 'http://' + url,
|
return request({
|
||||||
headers: {
|
url: '/sql-server-api/plans',
|
||||||
'Content-Type': 'application/json'
|
method: 'get',
|
||||||
},
|
params: { page, pageSize }
|
||||||
timeout: 10000
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计划总数
|
||||||
|
export function getTimingPlanCount() {
|
||||||
|
return request({
|
||||||
|
url: '/sql-server-api/plans/count',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计划详情
|
||||||
|
export function getTimingPlanDetail(coilId) {
|
||||||
|
return request({
|
||||||
|
url: '/sql-server-api/plans/' + coilId,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 钢卷实际 SEG,按入口卷号查询
|
||||||
|
export function getTimingSegByEncoilId(encoilId) {
|
||||||
|
return request({
|
||||||
|
url: '/sql-server-api/seg/' + encoilId,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 钢卷实际 SEG,按出口卷号查询
|
||||||
|
export function getTimingSegByExcoilId(excoilId) {
|
||||||
|
return request({
|
||||||
|
url: '/sql-server-api/seg-by-excoil/' + excoilId,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 实时数据:Gauge + Shape
|
||||||
|
export function getTimingRealtimeData(matId) {
|
||||||
|
return request({
|
||||||
|
url: '/sql-server-api/realtime/' + matId,
|
||||||
|
method: 'get'
|
||||||
})
|
})
|
||||||
|
|
||||||
request.interceptors.response.use(response => response.data)
|
|
||||||
|
|
||||||
return {
|
|
||||||
getPlanList: () => request({
|
|
||||||
url: '/sql-server-api/plans',
|
|
||||||
method: 'get'
|
|
||||||
}),
|
|
||||||
getPlanDetail: (coilId) => request({
|
|
||||||
url: `/sql-server-api/plans/${coilId}`,
|
|
||||||
method: 'get'
|
|
||||||
}),
|
|
||||||
getSegByEncoilId: (encoilId) => request({
|
|
||||||
url: `/sql-server-api/seg/${encoilId}`,
|
|
||||||
method: 'get'
|
|
||||||
}),
|
|
||||||
getSegByExcoilId: (excoilId) => request({
|
|
||||||
url: `/sql-server-api/seg-by-excoil/${excoilId}`,
|
|
||||||
method: 'get'
|
|
||||||
}),
|
|
||||||
getRealtimeData: (matId) => request({
|
|
||||||
url: `/sql-server-api/realtime/${matId}`,
|
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,151 +1,204 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="timing-page acid-page">
|
<div class="acid-view">
|
||||||
<el-card class="page-card" shadow="never">
|
<div class="filter-bar">
|
||||||
<div slot="header" class="card-header">
|
<el-input
|
||||||
<span>酸轧实绩页</span>
|
v-model="queryForm.coilId"
|
||||||
<el-tag type="success" size="mini">Plan + Seg + Quality</el-tag>
|
placeholder="热卷号 / 成品卷号"
|
||||||
</div>
|
clearable
|
||||||
|
size="small"
|
||||||
|
style="width: 220px"
|
||||||
|
@keyup.enter.native="handleSearch"
|
||||||
|
/>
|
||||||
|
<el-button size="small" type="primary" :loading="loading" @click="handleSearch">查询</el-button>
|
||||||
|
<el-button size="small" @click="handleReset">重置</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<el-form :inline="true" :model="queryForm" size="mini" class="query-form">
|
<el-row :gutter="12" class="main-row">
|
||||||
<el-form-item label="计划号">
|
<!-- 左侧计划列表 -->
|
||||||
<el-input v-model="queryForm.coilId" placeholder="输入 coilId" clearable style="width: 220px;" />
|
<el-col :span="9">
|
||||||
</el-form-item>
|
<div class="left-card">
|
||||||
<el-form-item>
|
<el-table
|
||||||
<el-button type="primary" icon="el-icon-search" :loading="loading" @click="handleSearch">查询</el-button>
|
ref="planTable"
|
||||||
<el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
|
:data="planRows"
|
||||||
</el-form-item>
|
size="mini"
|
||||||
</el-form>
|
highlight-current-row
|
||||||
|
:height="tableHeight"
|
||||||
|
@row-click="handlePlanRowClick"
|
||||||
|
>
|
||||||
|
<el-table-column prop="hot_coilid" label="热卷号" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="coilid" label="原料卷号" show-overflow-tooltip />
|
||||||
|
<el-table-column label="状态">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<span :class="['status-dot', statusClass(row.status)]" width="70px"/>
|
||||||
|
<span class="status-text">{{ row.status || '—' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="厚×宽">
|
||||||
|
<template slot-scope="{ row }">{{ row.entry_thick }}×{{ row.entry_width }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="exit_thick" label="出口厚" />
|
||||||
|
<el-table-column prop="entry_weight" label="重量(t)" />
|
||||||
|
</el-table>
|
||||||
|
|
||||||
<el-row :gutter="16">
|
<div class="pagination-bar">
|
||||||
<el-col :span="8">
|
<el-pagination
|
||||||
<el-card shadow="never" class="sub-card">
|
small
|
||||||
<div slot="header" class="sub-header">计划列表</div>
|
layout="total, prev, pager, next"
|
||||||
<el-table
|
:total="pagination.total"
|
||||||
:data="planRows"
|
:page-size="pagination.pageSize"
|
||||||
height="560"
|
:current-page="pagination.page"
|
||||||
size="mini"
|
@current-change="handlePageChange"
|
||||||
highlight-current-row
|
|
||||||
@row-click="handlePlanRowClick"
|
|
||||||
>
|
|
||||||
<el-table-column prop="COILID" label="COILID" min-width="140" />
|
|
||||||
<el-table-column prop="HOT_COILID" label="热卷号" min-width="140" />
|
|
||||||
<el-table-column prop="STATUS" label="状态" width="90" />
|
|
||||||
</el-table>
|
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
|
|
||||||
<el-col :span="16">
|
|
||||||
<el-card shadow="never" class="sub-card">
|
|
||||||
<div slot="header" class="sub-header">计划详情</div>
|
|
||||||
<el-empty v-if="!selectedPlan" description="请选择一条计划" />
|
|
||||||
<el-descriptions v-else :column="3" border size="mini">
|
|
||||||
<el-descriptions-item label="COILID">{{ selectedPlan.COILID }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="HOT_COILID">{{ selectedPlan.HOT_COILID }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="STATUS">{{ selectedPlan.STATUS }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="ENTRY_THICK">{{ selectedPlan.ENTRY_THICK }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="ENTRY_WIDTH">{{ selectedPlan.ENTRY_WIDTH }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="ENTRY_WEIGHT">{{ selectedPlan.ENTRY_WEIGHT }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="EXIT_THICK">{{ selectedPlan.EXIT_THICK }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="EXIT_WIDTH">{{ selectedPlan.EXIT_WIDTH }}</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="EXIT_LENGTH">{{ selectedPlan.EXIT_LENGTH }}</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<el-card shadow="never" class="sub-card" style="margin-top: 16px;">
|
|
||||||
<div slot="header" class="sub-header">SEG 实绩</div>
|
|
||||||
<el-alert
|
|
||||||
v-if="!segView"
|
|
||||||
title="请选择计划后自动加载对应 SEG"
|
|
||||||
type="info"
|
|
||||||
:closable="false"
|
|
||||||
show-icon
|
|
||||||
/>
|
/>
|
||||||
<template v-else>
|
</div>
|
||||||
<el-row :gutter="16">
|
</div>
|
||||||
<el-col :span="8">
|
</el-col>
|
||||||
<el-card shadow="never">
|
|
||||||
<div slot="header">SEGNO</div>
|
<!-- 右侧详情 + 图表 -->
|
||||||
<div class="seg-list">
|
<el-col :span="15">
|
||||||
<el-tag
|
<div v-if="!selectedPlan" class="empty-hint">
|
||||||
v-for="item in segView.segNo"
|
<el-empty description="选择左侧计划" :image-size="72" />
|
||||||
:key="item"
|
</div>
|
||||||
size="mini"
|
|
||||||
class="seg-tag"
|
<template v-else>
|
||||||
>
|
<div class="detail-grid">
|
||||||
{{ item }}
|
<div v-for="f in planFields" :key="f.key" class="detail-cell">
|
||||||
</el-tag>
|
<span class="cell-label">{{ f.label }}</span>
|
||||||
</div>
|
<span class="cell-value">{{ selectedPlan[f.key] != null ? selectedPlan[f.key] : '—' }}</span>
|
||||||
</el-card>
|
</div>
|
||||||
</el-col>
|
</div>
|
||||||
<el-col :span="16">
|
|
||||||
<el-card shadow="never">
|
<div class="perf-header">
|
||||||
<div slot="header">属性数组</div>
|
实轧实绩
|
||||||
<el-table :data="seriesTable" size="mini" height="240" border>
|
<span v-if="hasPerfData" class="perf-count">({{ perfSegCount }} 段)</span>
|
||||||
<el-table-column prop="key" label="属性" width="180" />
|
<el-tag v-if="perfLoading" size="mini" type="info" style="margin-left:8px">加载中…</el-tag>
|
||||||
<el-table-column prop="values" label="数组值" min-width="300">
|
</div>
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-tag v-for="(item, index) in scope.row.values" :key="index" size="mini" style="margin-right: 4px; margin-bottom: 4px;">
|
<div v-if="hasPerfData" class="charts-wrap">
|
||||||
{{ item }}
|
<div ref="chartSpeed" class="chart-box" />
|
||||||
</el-tag>
|
<div ref="chartMillSpeed" class="chart-box" />
|
||||||
</template>
|
<div ref="chartTension" class="chart-box" />
|
||||||
</el-table-column>
|
</div>
|
||||||
</el-table>
|
|
||||||
</el-card>
|
<el-empty v-else-if="!perfLoading" description="暂无实绩数据" :image-size="56" style="margin-top: 24px" />
|
||||||
</el-col>
|
</template>
|
||||||
</el-row>
|
</el-col>
|
||||||
</template>
|
</el-row>
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import createTimingFetch from '@/api/l2/timing'
|
import * as echarts from 'echarts'
|
||||||
|
import {
|
||||||
|
getTimingPlanList,
|
||||||
|
getTimingPlanCount,
|
||||||
|
getTimingPlanDetail,
|
||||||
|
getTimingSegByEncoilId
|
||||||
|
} from '@/api/l2/timing'
|
||||||
|
|
||||||
|
const PLAN_FIELDS = [
|
||||||
|
{ key: 'status', label: '状态' },
|
||||||
|
{ key: 'process_code', label: '工艺编码' },
|
||||||
|
{ key: 'entry_thick', label: '入口厚度(mm)' },
|
||||||
|
{ key: 'entry_width', label: '入口宽度(mm)' },
|
||||||
|
{ key: 'entry_weight', label: '入口重量(t)' },
|
||||||
|
{ key: 'exit_thick', label: '出口厚度(mm)' },
|
||||||
|
{ key: 'exit_width', label: '出口宽度(mm)' },
|
||||||
|
{ key: 'exit_length', label: '出口长度(m)' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const STATUS_CLASS = {
|
||||||
|
READY: 'status-ready',
|
||||||
|
ONLINE: 'status-online',
|
||||||
|
PRODUCING: 'status-producing',
|
||||||
|
PRODUCT: 'status-product'
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeLine(name, data) {
|
||||||
|
return { name, type: 'line', smooth: true, symbol: 'none', data }
|
||||||
|
}
|
||||||
|
|
||||||
|
function baseOption(title, xData, series, yName) {
|
||||||
|
return {
|
||||||
|
title: { text: title, textStyle: { fontSize: 12, fontWeight: 'normal' }, top: 4, left: 8 },
|
||||||
|
tooltip: { trigger: 'axis' },
|
||||||
|
legend: { top: 4, right: 8, textStyle: { fontSize: 11 } },
|
||||||
|
grid: { top: 36, bottom: 28, left: 8, right: 8, containLabel: true },
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: xData,
|
||||||
|
name: 'pos(m)',
|
||||||
|
nameTextStyle: { fontSize: 10 },
|
||||||
|
axisLabel: { fontSize: 10 }
|
||||||
|
},
|
||||||
|
yAxis: { type: 'value', name: yName, nameTextStyle: { fontSize: 10 }, axisLabel: { fontSize: 10 } },
|
||||||
|
series
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TimingAcidPage',
|
name: 'TimingAcidPage',
|
||||||
props: {
|
|
||||||
baseURL: { type: String, required: true }
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
fetchApi: null,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
|
perfLoading: false,
|
||||||
queryForm: { coilId: '' },
|
queryForm: { coilId: '' },
|
||||||
planRows: [],
|
planRows: [],
|
||||||
selectedPlan: null,
|
selectedPlan: null,
|
||||||
segView: null,
|
perfSeries: null,
|
||||||
seriesTable: []
|
perfSegCount: 0,
|
||||||
|
planFields: PLAN_FIELDS,
|
||||||
|
pagination: { page: 1, pageSize: 50, total: 0 },
|
||||||
|
tableHeight: 'calc(100vh - 210px)'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
hasPerfData() {
|
||||||
|
return this.perfSeries && this.perfSegCount > 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.fetchApi = createTimingFetch(this.baseURL)
|
// plain instance property — Vue 2 does NOT proxy underscore-prefixed names
|
||||||
|
this.chartInstances = []
|
||||||
|
this.resizeHandler = null
|
||||||
|
this.loadPlanCount()
|
||||||
this.loadPlanList()
|
this.loadPlanList()
|
||||||
},
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.disposeCharts()
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
statusClass(status) {
|
||||||
|
return STATUS_CLASS[status] || 'status-default'
|
||||||
|
},
|
||||||
|
async loadPlanCount() {
|
||||||
|
try {
|
||||||
|
const res = await getTimingPlanCount()
|
||||||
|
this.pagination.total = res?.data?.total ?? 0
|
||||||
|
} catch (_) {}
|
||||||
|
},
|
||||||
async loadPlanList() {
|
async loadPlanList() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
try {
|
try {
|
||||||
const res = await this.fetchApi.getPlanList()
|
const { page, pageSize } = this.pagination
|
||||||
const rows = res?.data?.rows || res?.rows || []
|
const res = await getTimingPlanList(page, pageSize)
|
||||||
this.planRows = rows
|
this.planRows = res?.data?.rows || []
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
handlePageChange(page) {
|
||||||
|
this.pagination.page = page
|
||||||
|
this.loadPlanList()
|
||||||
|
},
|
||||||
async handleSearch() {
|
async handleSearch() {
|
||||||
if (!this.queryForm.coilId) {
|
if (!this.queryForm.coilId) return this.loadPlanList()
|
||||||
return this.loadPlanList()
|
|
||||||
}
|
|
||||||
this.loading = true
|
this.loading = true
|
||||||
try {
|
try {
|
||||||
const res = await this.fetchApi.getPlanDetail(this.queryForm.coilId)
|
const res = await getTimingPlanDetail(this.queryForm.coilId)
|
||||||
const row = res?.data?.firstRow || res?.firstRow || null
|
const row = res?.data?.firstRow || null
|
||||||
this.selectedPlan = row
|
if (row) {
|
||||||
if (row && row.ENCOILID) {
|
this.selectedPlan = row
|
||||||
await this.loadSeg(row.ENCOILID)
|
await this.loadPerf(row)
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
@@ -153,25 +206,86 @@ export default {
|
|||||||
},
|
},
|
||||||
async handlePlanRowClick(row) {
|
async handlePlanRowClick(row) {
|
||||||
this.selectedPlan = row
|
this.selectedPlan = row
|
||||||
if (row?.COILID) {
|
this.perfSeries = null
|
||||||
const detail = await this.fetchApi.getPlanDetail(row.COILID)
|
this.perfSegCount = 0
|
||||||
const plan = detail?.data?.firstRow || detail?.firstRow || row
|
this.disposeCharts()
|
||||||
this.selectedPlan = plan
|
await this.loadPerf(row)
|
||||||
const encoilId = plan.ENCOILID || plan.ENCOILID || plan.COILID
|
},
|
||||||
if (encoilId) await this.loadSeg(encoilId)
|
async loadPerf(plan) {
|
||||||
|
const encoilId = plan.encoilid || plan.coilid
|
||||||
|
if (!encoilId) return
|
||||||
|
this.perfLoading = true
|
||||||
|
try {
|
||||||
|
const res = await getTimingSegByEncoilId(encoilId)
|
||||||
|
const series = res?.data?.series || null
|
||||||
|
const rows = res?.data?.rows || []
|
||||||
|
this.perfSegCount = rows.length
|
||||||
|
this.perfSeries = series
|
||||||
|
if (series && rows.length) {
|
||||||
|
await this.$nextTick()
|
||||||
|
this.renderCharts(series)
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
this.perfSeries = null
|
||||||
|
this.perfSegCount = 0
|
||||||
|
} finally {
|
||||||
|
this.perfLoading = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async loadSeg(encoilId) {
|
disposeCharts() {
|
||||||
const res = await this.fetchApi.getSegByEncoilId(encoilId)
|
if (this.resizeHandler) {
|
||||||
const view = res?.data || res
|
window.removeEventListener('resize', this.resizeHandler)
|
||||||
this.segView = view
|
this.resizeHandler = null
|
||||||
this.seriesTable = Object.entries(view?.series || {}).map(([key, values]) => ({ key, values }))
|
}
|
||||||
|
if (this.chartInstances && this.chartInstances.length) {
|
||||||
|
this.chartInstances.forEach(c => { if (c) c.dispose() })
|
||||||
|
this.chartInstances = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
renderCharts(series) {
|
||||||
|
this.disposeCharts()
|
||||||
|
const pick = key => (series[key] || []).map(v => v == null ? null : Number(v).toFixed(2))
|
||||||
|
const xData = (series.startpos || []).map(v => v == null ? '' : Number(v).toFixed(1))
|
||||||
|
|
||||||
|
const c1 = echarts.init(this.$refs.chartSpeed)
|
||||||
|
c1.setOption(baseOption(
|
||||||
|
'速度趋势 (m/min)', xData,
|
||||||
|
[
|
||||||
|
makeLine('轧制速度 plspeed', pick('plspeed')),
|
||||||
|
makeLine('剪切速度 trimspeed', pick('trimspeed'))
|
||||||
|
],
|
||||||
|
'm/min'
|
||||||
|
))
|
||||||
|
|
||||||
|
const c2 = echarts.init(this.$refs.chartMillSpeed)
|
||||||
|
c2.setOption(baseOption(
|
||||||
|
'轧机出口速度 (m/min)', xData,
|
||||||
|
[makeLine('millexitspeed', pick('millexitspeed'))],
|
||||||
|
'm/min'
|
||||||
|
))
|
||||||
|
|
||||||
|
const c3 = echarts.init(this.$refs.chartTension)
|
||||||
|
c3.setOption(baseOption(
|
||||||
|
'张力趋势 (N)', xData,
|
||||||
|
[
|
||||||
|
makeLine('出口张力 pltens', pick('pltens')),
|
||||||
|
makeLine('入口张力 enltens', pick('enltens')),
|
||||||
|
makeLine('cxltens', pick('cxltens'))
|
||||||
|
],
|
||||||
|
'N'
|
||||||
|
))
|
||||||
|
|
||||||
|
this.chartInstances = [c1, c2, c3]
|
||||||
|
this.resizeHandler = () => this.chartInstances.forEach(c => { if (c) c.resize() })
|
||||||
|
window.addEventListener('resize', this.resizeHandler)
|
||||||
},
|
},
|
||||||
handleReset() {
|
handleReset() {
|
||||||
this.queryForm.coilId = ''
|
this.queryForm.coilId = ''
|
||||||
this.selectedPlan = null
|
this.selectedPlan = null
|
||||||
this.segView = null
|
this.perfSeries = null
|
||||||
this.seriesTable = []
|
this.perfSegCount = 0
|
||||||
|
this.disposeCharts()
|
||||||
|
this.pagination.page = 1
|
||||||
this.loadPlanList()
|
this.loadPlanList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -179,11 +293,115 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.timing-page { padding: 16px; }
|
.acid-view {
|
||||||
.page-card { border-radius: 12px; }
|
padding: 12px 16px;
|
||||||
.card-header, .sub-header { display: flex; align-items: center; justify-content: space-between; font-weight: 600; }
|
}
|
||||||
.sub-card { border-radius: 10px; }
|
|
||||||
.query-form { margin-bottom: 12px; }
|
.filter-bar {
|
||||||
.seg-list { display: flex; flex-wrap: wrap; gap: 6px; }
|
display: flex;
|
||||||
.seg-tag { margin-right: 0; }
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-row {
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-card {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-hint {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-bar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding: 6px 8px;
|
||||||
|
border-top: 1px solid #ebeef5;
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
border-top: 1px solid #ebeef5;
|
||||||
|
border-left: 1px solid #ebeef5;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-cell {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 8px 10px;
|
||||||
|
border-right: 1px solid #ebeef5;
|
||||||
|
border-bottom: 1px solid #ebeef5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-label {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #909399;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-value {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #303133;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.perf-header {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #606266;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.perf-count {
|
||||||
|
font-weight: normal;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.charts-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: calc(100vh - 310px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 状态指示点 */
|
||||||
|
.status-dot {
|
||||||
|
display: inline-block;
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 4px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.status-text {
|
||||||
|
font-size: 8px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.status-ready { background: #909399; }
|
||||||
|
.status-online { background: #67c23a; }
|
||||||
|
.status-producing { background: #e6a23c; }
|
||||||
|
.status-product { background: #409eff; }
|
||||||
|
.status-default { background: #c0c4cc; }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -55,16 +55,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import createTimingFetch from '@/api/l2/timing'
|
import { getTimingRealtimeData } from '@/api/l2/timing'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TimingRealtimePage',
|
name: 'TimingRealtimePage',
|
||||||
props: {
|
|
||||||
baseURL: { type: String, required: true }
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
fetchApi: null,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
queryForm: { matId: '' },
|
queryForm: { matId: '' },
|
||||||
realtimeData: null,
|
realtimeData: null,
|
||||||
@@ -72,9 +68,6 @@ export default {
|
|||||||
shapeRows: []
|
shapeRows: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
|
||||||
this.fetchApi = createTimingFetch(this.baseURL)
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
async handleQuery() {
|
async handleQuery() {
|
||||||
if (!this.queryForm.matId) {
|
if (!this.queryForm.matId) {
|
||||||
@@ -83,10 +76,10 @@ export default {
|
|||||||
}
|
}
|
||||||
this.loading = true
|
this.loading = true
|
||||||
try {
|
try {
|
||||||
const res = await this.fetchApi.getRealtimeData(this.queryForm.matId)
|
const res = await getTimingRealtimeData(this.queryForm.matId)
|
||||||
this.realtimeData = res?.data || res
|
this.realtimeData = res && res.data ? res.data : res
|
||||||
this.gaugeRows = this.realtimeData?.gauge?.result || []
|
this.gaugeRows = this.realtimeData && this.realtimeData.gauge ? this.realtimeData.gauge.result || [] : []
|
||||||
this.shapeRows = this.realtimeData?.shape?.result || []
|
this.shapeRows = this.realtimeData && this.realtimeData.shape ? this.realtimeData.shape.result || [] : []
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,87 +1,80 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="spec-page">
|
||||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="88px">
|
<!-- 规程类型 tabs -->
|
||||||
<el-form-item label="规程编号" prop="specCode">
|
<div class="type-tab-bar">
|
||||||
<el-input v-model="queryParams.specCode" placeholder="规程编号" clearable @keyup.enter.native="handleQuery" />
|
<span
|
||||||
</el-form-item>
|
v-for="t in specTypeTab"
|
||||||
<el-form-item label="规程名称" prop="specName">
|
:key="t.value"
|
||||||
<el-input v-model="queryParams.specName" placeholder="规程名称" clearable @keyup.enter.native="handleQuery" />
|
:class="['type-tab', { active: activeSpecType === t.value }]"
|
||||||
</el-form-item>
|
@click="switchSpecType(t.value)"
|
||||||
<el-form-item label="规程类型" prop="specType">
|
>{{ t.label }}</span>
|
||||||
<el-select v-model="queryParams.specType" placeholder="全部" clearable>
|
</div>
|
||||||
<el-option v-for="item in specTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
||||||
</el-select>
|
<!-- 产线 tabs -->
|
||||||
</el-form-item>
|
<div class="line-tab-bar">
|
||||||
<el-form-item label="产线" prop="lineId">
|
<span
|
||||||
<el-select v-model="queryParams.lineId" placeholder="全部" clearable filterable>
|
:class="['line-tab', { active: activeLineId === '' }]"
|
||||||
<el-option
|
@click="switchLine('')"
|
||||||
v-for="line in lineOptions"
|
>全部</span>
|
||||||
:key="line.lineId"
|
<span
|
||||||
:label="formatLineOption(line)"
|
v-for="line in lineOptions"
|
||||||
:value="line.lineId"
|
:key="line.lineId"
|
||||||
|
:class="['line-tab', { active: activeLineId === line.lineId }]"
|
||||||
|
@click="switchLine(line.lineId)"
|
||||||
|
>{{ line.lineName }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 工具栏 -->
|
||||||
|
<div class="toolbar">
|
||||||
|
<div class="toolbar-left">
|
||||||
|
<el-button type="primary" size="mini" icon="el-icon-plus" @click="handleAdd">新增</el-button>
|
||||||
|
<el-button size="mini" icon="el-icon-edit" :disabled="single" @click="handleUpdate()">修改</el-button>
|
||||||
|
<el-button size="mini" icon="el-icon-delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="toolbar-right">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.specName"
|
||||||
|
size="small"
|
||||||
|
placeholder="规程名称"
|
||||||
|
clearable
|
||||||
|
style="width:180px; margin-right:8px"
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
<el-button size="mini" type="primary" @click="handleQuery">查询</el-button>
|
||||||
|
<el-button size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 列表 -->
|
||||||
|
<el-table
|
||||||
|
v-loading="loading"
|
||||||
|
:data="dataList"
|
||||||
|
size="small"
|
||||||
|
highlight-current-row
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" />
|
||||||
|
<el-table-column label="规程编号" prop="specCode" show-overflow-tooltip />
|
||||||
|
<el-table-column label="规程名称" prop="specName" show-overflow-tooltip />
|
||||||
|
<el-table-column label="产品类型" prop="productType" show-overflow-tooltip />
|
||||||
|
<el-table-column label="创建时间" prop="createTime" />
|
||||||
|
<el-table-column label="启用" align="center">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<el-switch
|
||||||
|
:value="row.isEnabled === 1"
|
||||||
|
active-color="#5F7BA0"
|
||||||
|
@change="toggleEnabled(row, $event)"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="产品类型" prop="productType">
|
|
||||||
<el-input v-model="queryParams.productType" placeholder="产品类型" clearable />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="是否启用" prop="isEnabled">
|
|
||||||
<el-select v-model="queryParams.isEnabled" placeholder="全部" clearable>
|
|
||||||
<el-option label="启用" :value="1" />
|
|
||||||
<el-option label="禁用" :value="0" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
|
||||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
|
|
||||||
<el-row :gutter="10" class="mb8">
|
|
||||||
<el-col :span="1.5">
|
|
||||||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">新增</el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="1.5">
|
|
||||||
<el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate">修改</el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="1.5">
|
|
||||||
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete">删除</el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="1.5">
|
|
||||||
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport">导出</el-button>
|
|
||||||
</el-col>
|
|
||||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<KLPTable v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
|
|
||||||
<el-table-column type="selection" width="55" align="center" />
|
|
||||||
<el-table-column label="规程编号" align="center" prop="specCode" min-width="120" show-overflow-tooltip />
|
|
||||||
<el-table-column label="规程名称" align="center" prop="specName" min-width="140" show-overflow-tooltip />
|
|
||||||
<el-table-column label="规程类型" align="center" prop="specType" width="100">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<span>{{ formatSpecType(scope.row.specType) }}</span>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="产线" align="center" min-width="160" show-overflow-tooltip>
|
<el-table-column label="操作" align="right">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="{ row }">
|
||||||
{{ getLineName(scope.row.lineId) }}
|
<el-button type="text" size="mini" @click="goVersionManage(row)">版本与方案</el-button>
|
||||||
|
<el-button type="text" size="mini" @click="handleUpdate(row)">修改</el-button>
|
||||||
|
<el-button type="text" size="mini" class="btn-danger" @click="handleDelete(row)">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="产品类型" align="center" prop="productType" min-width="100" show-overflow-tooltip />
|
</el-table>
|
||||||
<el-table-column label="是否启用" align="center" prop="isEnabled" width="90">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<dict-tag :options="dict.type.common_swicth" :value="scope.row.isEnabled" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="创建时间" align="center" prop="createTime" width="160" />
|
|
||||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="220">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-button size="mini" type="text" icon="el-icon-document" @click="goVersionManage(scope.row)">版本与方案</el-button>
|
|
||||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)">修改</el-button>
|
|
||||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</KLPTable>
|
|
||||||
|
|
||||||
<pagination
|
<pagination
|
||||||
v-show="total > 0"
|
v-show="total > 0"
|
||||||
@@ -91,45 +84,43 @@
|
|||||||
@pagination="getList"
|
@pagination="getList"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<el-dialog :title="title" :visible.sync="open" width="560px" append-to-body>
|
<!-- 新增/修改 -->
|
||||||
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
<el-dialog :title="dialogTitle" :visible.sync="open" width="520px" append-to-body @close="reset">
|
||||||
|
<el-form ref="form" :model="form" :rules="rules" label-width="88px" size="small">
|
||||||
<el-form-item label="规程编号" prop="specCode">
|
<el-form-item label="规程编号" prop="specCode">
|
||||||
<el-input v-model="form.specCode" placeholder="唯一编号" maxlength="64" show-word-limit />
|
<el-input v-model="form.specCode" placeholder="唯一编号" maxlength="64" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="规程名称" prop="specName">
|
<el-form-item label="规程名称" prop="specName">
|
||||||
<el-input v-model="form.specName" placeholder="规程名称" maxlength="200" show-word-limit />
|
<el-input v-model="form.specName" maxlength="200" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="规程类型" prop="specType">
|
<el-form-item label="规程类型" prop="specType">
|
||||||
<el-select v-model="form.specType" placeholder="请选择">
|
<el-select v-model="form.specType" style="width:100%">
|
||||||
<el-option v-for="item in specTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
<el-option v-for="t in specTypeOptions" :key="t.value" :label="t.label" :value="t.value" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="产线" prop="lineId">
|
<el-form-item label="产线" prop="lineId">
|
||||||
<el-select v-model="form.lineId" placeholder="请选择产线" filterable style="width: 100%">
|
<el-select v-model="form.lineId" filterable placeholder="请选择" style="width:100%">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="line in lineOptions"
|
v-for="line in lineOptions"
|
||||||
:key="line.lineId"
|
:key="line.lineId"
|
||||||
:label="formatLineOption(line)"
|
:label="line.lineCode ? line.lineName + '(' + line.lineCode + ')' : line.lineName"
|
||||||
:value="line.lineId"
|
:value="line.lineId"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="产品类型" prop="productType">
|
<el-form-item label="产品类型" prop="productType">
|
||||||
<el-input v-model="form.productType" placeholder="可选" maxlength="100" />
|
<el-input v-model="form.productType" maxlength="100" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="是否启用" prop="isEnabled">
|
<el-form-item label="是否启用" prop="isEnabled">
|
||||||
<el-radio-group v-model="form.isEnabled">
|
<el-switch v-model="form.isEnabled" :active-value="1" :inactive-value="0" />
|
||||||
<el-radio :label="1">启用</el-radio>
|
|
||||||
<el-radio :label="0">禁用</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="备注" prop="remark">
|
<el-form-item label="备注" prop="remark">
|
||||||
<el-input v-model="form.remark" type="textarea" placeholder="备注" maxlength="500" show-word-limit rows="2" />
|
<el-input v-model="form.remark" type="textarea" rows="2" maxlength="500" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer">
|
||||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
<el-button size="small" @click="open = false">取消</el-button>
|
||||||
<el-button @click="cancel">取 消</el-button>
|
<el-button size="small" type="primary" :loading="btnLoading" @click="submitForm">确定</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
@@ -139,184 +130,255 @@
|
|||||||
import { listProcessSpec, getProcessSpec, delProcessSpec, updateProcessSpec, addProcessSpec } from '@/api/wms/processSpec'
|
import { listProcessSpec, getProcessSpec, delProcessSpec, updateProcessSpec, addProcessSpec } from '@/api/wms/processSpec'
|
||||||
import { listProductionLine } from '@/api/wms/productionLine'
|
import { listProductionLine } from '@/api/wms/productionLine'
|
||||||
|
|
||||||
|
const SPEC_TYPES = [
|
||||||
|
{ label: '工艺规程', value: 'PROCESS' },
|
||||||
|
{ label: '标准', value: 'STANDARD' }
|
||||||
|
]
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProcessSpec',
|
name: 'ProcessSpec',
|
||||||
dicts: ['common_swicth'],
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
buttonLoading: false,
|
loading: false,
|
||||||
loading: true,
|
btnLoading: false,
|
||||||
|
total: 0,
|
||||||
|
dataList: [],
|
||||||
ids: [],
|
ids: [],
|
||||||
single: true,
|
single: true,
|
||||||
multiple: true,
|
multiple: true,
|
||||||
showSearch: true,
|
|
||||||
total: 0,
|
|
||||||
dataList: [],
|
|
||||||
title: '',
|
|
||||||
open: false,
|
open: false,
|
||||||
|
dialogTitle: '',
|
||||||
lineOptions: [],
|
lineOptions: [],
|
||||||
specTypeOptions: [
|
specTypeTab: [{ label: '全部', value: '' }, ...SPEC_TYPES],
|
||||||
{ label: '工艺规程', value: 'PROCESS' },
|
specTypeOptions: SPEC_TYPES,
|
||||||
{ label: '标准', value: 'STANDARD' }
|
activeSpecType: '',
|
||||||
],
|
activeLineId: '',
|
||||||
queryParams: {
|
queryParams: {
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
specCode: undefined,
|
|
||||||
specName: undefined,
|
specName: undefined,
|
||||||
specType: undefined,
|
specType: undefined,
|
||||||
lineId: undefined,
|
lineId: undefined
|
||||||
productType: undefined,
|
|
||||||
isEnabled: undefined
|
|
||||||
},
|
},
|
||||||
form: {},
|
form: {},
|
||||||
rules: {
|
rules: {
|
||||||
specCode: [{ required: true, message: '规程编号不能为空', trigger: 'blur' }],
|
specCode: [{ required: true, message: '规程编号不能为空', trigger: 'blur' }],
|
||||||
specName: [{ required: true, message: '规程名称不能为空', trigger: 'blur' }],
|
specName: [{ required: true, message: '规程名称不能为空', trigger: 'blur' }],
|
||||||
specType: [{ required: true, message: '规程类型不能为空', trigger: 'change' }],
|
specType: [{ required: true, message: '请选择规程类型', trigger: 'change' }],
|
||||||
lineId: [{ required: true, message: '产线不能为空', trigger: 'change' }]
|
lineId: [{ required: true, message: '请选择产线', trigger: 'change' }]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.loadLineOptions()
|
listProductionLine({ pageNum: 1, pageSize: 500 }).then(res => {
|
||||||
|
this.lineOptions = res.rows || []
|
||||||
|
})
|
||||||
this.getList()
|
this.getList()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
formatSpecType(value) {
|
|
||||||
const hit = this.specTypeOptions.find((x) => x.value === value)
|
|
||||||
return hit ? hit.label : value
|
|
||||||
},
|
|
||||||
formatLineOption(line) {
|
|
||||||
if (!line) {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
return line.lineCode ? `${line.lineName}(${line.lineCode})` : line.lineName
|
|
||||||
},
|
|
||||||
getLineName(lineId) {
|
|
||||||
if (lineId == null) {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
const hit = this.lineOptions.find((o) => o.lineId === lineId)
|
|
||||||
return hit ? this.formatLineOption(hit) : lineId
|
|
||||||
},
|
|
||||||
loadLineOptions() {
|
|
||||||
listProductionLine({ pageNum: 1, pageSize: 500 }).then((res) => {
|
|
||||||
this.lineOptions = res.rows || []
|
|
||||||
}).catch((err) => {
|
|
||||||
console.error('加载产线列表失败', err)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getList() {
|
getList() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
listProcessSpec(this.queryParams).then((response) => {
|
listProcessSpec(this.queryParams).then(res => {
|
||||||
this.dataList = response.rows
|
this.dataList = res.rows || []
|
||||||
this.total = response.total
|
this.total = res.total || 0
|
||||||
this.loading = false
|
}).finally(() => { this.loading = false })
|
||||||
}).catch((err) => {
|
|
||||||
console.error('加载规程列表失败', err)
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
cancel() {
|
switchSpecType(val) {
|
||||||
this.open = false
|
this.activeSpecType = val
|
||||||
this.reset()
|
this.queryParams.specType = val || undefined
|
||||||
|
this.queryParams.pageNum = 1
|
||||||
|
this.getList()
|
||||||
},
|
},
|
||||||
reset() {
|
switchLine(lineId) {
|
||||||
this.form = {
|
this.activeLineId = lineId
|
||||||
specId: undefined,
|
this.queryParams.lineId = lineId || undefined
|
||||||
specCode: undefined,
|
this.queryParams.pageNum = 1
|
||||||
specName: undefined,
|
this.getList()
|
||||||
specType: 'PROCESS',
|
|
||||||
lineId: undefined,
|
|
||||||
productType: undefined,
|
|
||||||
isEnabled: 1,
|
|
||||||
remark: undefined
|
|
||||||
}
|
|
||||||
this.resetForm('form')
|
|
||||||
},
|
},
|
||||||
handleQuery() {
|
handleQuery() {
|
||||||
this.queryParams.pageNum = 1
|
this.queryParams.pageNum = 1
|
||||||
this.getList()
|
this.getList()
|
||||||
},
|
},
|
||||||
resetQuery() {
|
resetQuery() {
|
||||||
this.resetForm('queryForm')
|
this.queryParams.specName = undefined
|
||||||
this.handleQuery()
|
this.handleQuery()
|
||||||
},
|
},
|
||||||
handleSelectionChange(selection) {
|
handleSelectionChange(sel) {
|
||||||
this.ids = selection.map((item) => item.specId)
|
this.ids = sel.map(r => r.specId)
|
||||||
this.single = selection.length !== 1
|
this.single = sel.length !== 1
|
||||||
this.multiple = !selection.length
|
this.multiple = !sel.length
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
this.form = { specId: undefined, specCode: undefined, specName: undefined, specType: 'PROCESS', lineId: undefined, productType: undefined, isEnabled: 1, remark: undefined }
|
||||||
|
this.$refs.form && this.$refs.form.clearValidate()
|
||||||
},
|
},
|
||||||
handleAdd() {
|
handleAdd() {
|
||||||
this.reset()
|
this.reset()
|
||||||
|
this.dialogTitle = '新增规程'
|
||||||
this.open = true
|
this.open = true
|
||||||
this.title = '添加规程'
|
|
||||||
},
|
},
|
||||||
handleUpdate(row) {
|
handleUpdate(row) {
|
||||||
this.loading = true
|
|
||||||
this.reset()
|
this.reset()
|
||||||
const specId = row.specId || this.ids
|
const specId = row ? row.specId : this.ids[0]
|
||||||
getProcessSpec(specId).then((response) => {
|
getProcessSpec(specId).then(res => {
|
||||||
this.loading = false
|
this.form = res.data || {}
|
||||||
this.form = response.data
|
this.dialogTitle = '修改规程'
|
||||||
this.open = true
|
this.open = true
|
||||||
this.title = '修改规程'
|
|
||||||
}).catch((err) => {
|
|
||||||
console.error('获取规程详情失败', err)
|
|
||||||
this.loading = false
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
submitForm() {
|
submitForm() {
|
||||||
this.$refs.form.validate((valid) => {
|
this.$refs.form.validate(valid => {
|
||||||
if (!valid) {
|
if (!valid) return
|
||||||
return
|
this.btnLoading = true
|
||||||
}
|
const req = this.form.specId ? updateProcessSpec(this.form) : addProcessSpec(this.form)
|
||||||
this.buttonLoading = true
|
|
||||||
const req = this.form.specId != null ? updateProcessSpec(this.form) : addProcessSpec(this.form)
|
|
||||||
req.then(() => {
|
req.then(() => {
|
||||||
this.$modal.msgSuccess(this.form.specId != null ? '修改成功' : '新增成功')
|
this.$modal.msgSuccess('保存成功')
|
||||||
this.open = false
|
this.open = false
|
||||||
this.getList()
|
this.getList()
|
||||||
}).catch((err) => {
|
}).finally(() => { this.btnLoading = false })
|
||||||
console.error('保存规程失败', err)
|
|
||||||
}).finally(() => {
|
|
||||||
this.buttonLoading = false
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
handleDelete(row) {
|
handleDelete(row) {
|
||||||
const specIds = row.specId || this.ids
|
const ids = row ? row.specId : this.ids
|
||||||
this.$modal.confirm('是否确认删除选中的规程数据?').then(() => {
|
this.$modal.confirm('确认删除所选规程?').then(() => {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
return delProcessSpec(specIds)
|
return delProcessSpec(ids)
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.getList()
|
|
||||||
this.$modal.msgSuccess('删除成功')
|
this.$modal.msgSuccess('删除成功')
|
||||||
}).catch(() => {}).finally(() => {
|
this.getList()
|
||||||
this.loading = false
|
}).catch(() => {}).finally(() => { this.loading = false })
|
||||||
})
|
|
||||||
},
|
},
|
||||||
handleExport() {
|
toggleEnabled(row, val) {
|
||||||
this.download('wms/processSpec/export', {
|
const updated = { ...row, isEnabled: val ? 1 : 0 }
|
||||||
...this.queryParams
|
updateProcessSpec(updated).then(() => {
|
||||||
}, `processSpec_${new Date().getTime()}.xlsx`)
|
row.isEnabled = updated.isEnabled
|
||||||
|
}).catch(() => {})
|
||||||
},
|
},
|
||||||
goVersionManage(row) {
|
goVersionManage(row) {
|
||||||
const specId = row.specId
|
|
||||||
if (specId == null || specId === '') {
|
|
||||||
this.$modal.msgWarning('无法获取规程ID,请刷新列表后重试')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 固定落在「…/processSpec/version」,避免列表为 …/processSpec/list 时拼成 …/list/version 导致路由不匹配、query 丢失
|
|
||||||
const pathCurrent = this.$route.path.replace(/\/$/, '')
|
const pathCurrent = this.$route.path.replace(/\/$/, '')
|
||||||
const m = pathCurrent.match(/^(.*\/processSpec)(?:\/.*)?$/)
|
const m = pathCurrent.match(/^(.*\/processSpec)(?:\/.*)?$/)
|
||||||
const base = m ? m[1] : pathCurrent
|
const base = m ? m[1] : pathCurrent
|
||||||
this.$router.push({
|
this.$router.push({ path: `${base}/version`, query: { specId: String(row.specId) } })
|
||||||
path: `${base}/version`,
|
|
||||||
query: { specId: String(specId) }
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.spec-page {
|
||||||
|
padding: 16px 20px;
|
||||||
|
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── 双色主题:默认=白底灰边,激活/主操作=深藏青 #5F7BA0 ── */
|
||||||
|
|
||||||
|
.type-tab-bar {
|
||||||
|
display: flex;
|
||||||
|
gap: 0;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
width: fit-content;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-tab {
|
||||||
|
padding: 5px 14px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #606266;
|
||||||
|
background: #fff;
|
||||||
|
border: none;
|
||||||
|
border-right: 1px solid #dcdfe6;
|
||||||
|
transition: color .15s, background .15s;
|
||||||
|
user-select: none;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-tab:last-child { border-right: none; }
|
||||||
|
.type-tab:hover { color: #5F7BA0; }
|
||||||
|
|
||||||
|
.type-tab.active {
|
||||||
|
color: #fff;
|
||||||
|
background: #5F7BA0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-tab-bar {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-tab {
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #606266;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
transition: color .15s, background .15s, border-color .15s;
|
||||||
|
user-select: none;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-tab:hover { color: #5F7BA0; border-color: #5F7BA0; }
|
||||||
|
|
||||||
|
.line-tab.active {
|
||||||
|
color: #fff;
|
||||||
|
background: #5F7BA0;
|
||||||
|
border-color: #5F7BA0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-left,
|
||||||
|
.toolbar-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table {
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* el-button:主操作类 → 深藏青;默认类 → 白底灰边 */
|
||||||
|
::v-deep .el-button--primary {
|
||||||
|
color: #fff !important;
|
||||||
|
background: #5F7BA0 !important;
|
||||||
|
border-color: #5F7BA0 !important;
|
||||||
|
}
|
||||||
|
::v-deep .el-button--primary:hover,
|
||||||
|
::v-deep .el-button--primary:focus {
|
||||||
|
background: #4d6a8e !important;
|
||||||
|
border-color: #4d6a8e !important;
|
||||||
|
}
|
||||||
|
::v-deep .el-button--primary:active { background: #4a6585 !important; border-color: #4a6585 !important; }
|
||||||
|
::v-deep .el-button--primary.is-disabled { opacity: .5; }
|
||||||
|
|
||||||
|
::v-deep .el-button:not(.el-button--primary):not(.el-button--text):not(.el-button--danger):not(.el-button--info) {
|
||||||
|
color: #606266 !important;
|
||||||
|
background: #fff !important;
|
||||||
|
border-color: #dcdfe6 !important;
|
||||||
|
}
|
||||||
|
::v-deep .el-button:not(.el-button--primary):not(.el-button--text):not(.el-button--danger):not(.el-button--info):hover {
|
||||||
|
color: #5F7BA0 !important;
|
||||||
|
border-color: #5F7BA0 !important;
|
||||||
|
}
|
||||||
|
::v-deep .el-button:not(.el-button--primary):not(.el-button--text):not(.el-button--danger):not(.el-button--info).is-disabled { opacity: .5; }
|
||||||
|
|
||||||
|
::v-deep .el-button--text { background: transparent !important; border-color: transparent !important; }
|
||||||
|
::v-deep .el-button--text.btn-danger { color: #f56c6c !important; }
|
||||||
|
|
||||||
|
.btn-danger { color: #f56c6c; }
|
||||||
|
</style>
|
||||||
|
|||||||
533
klp-ui/src/views/wms/processSpec/planSpec.vue
Normal file
533
klp-ui/src/views/wms/processSpec/planSpec.vue
Normal file
@@ -0,0 +1,533 @@
|
|||||||
|
<template>
|
||||||
|
<div class="plan-spec-page" v-loading="pageLoading">
|
||||||
|
<!-- 头部 -->
|
||||||
|
<div class="page-header">
|
||||||
|
<el-button type="text" icon="el-icon-arrow-left" @click="goBack">返回</el-button>
|
||||||
|
<span class="page-title">方案详情</span>
|
||||||
|
<span v-if="versionCode" class="version-badge">版本 {{ versionCode }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 可配置 / 不可配置 切换 -->
|
||||||
|
<div class="config-tabs">
|
||||||
|
<span
|
||||||
|
:class="['config-tab', { active: configMode === 'configurable' }]"
|
||||||
|
@click="configMode = 'configurable'"
|
||||||
|
>可配置</span>
|
||||||
|
<span
|
||||||
|
:class="['config-tab', { active: configMode === 'readonly' }]"
|
||||||
|
@click="configMode = 'readonly'"
|
||||||
|
>不可配置</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main-layout">
|
||||||
|
<!-- 左侧段分组 -->
|
||||||
|
<div class="left-tree">
|
||||||
|
<div
|
||||||
|
:class="['tree-item', { active: activeSegment === '' }]"
|
||||||
|
@click="activeSegment = ''"
|
||||||
|
>全部</div>
|
||||||
|
<div
|
||||||
|
v-for="seg in segmentOptions"
|
||||||
|
:key="seg.value"
|
||||||
|
:class="['tree-item', { active: activeSegment === seg.value }]"
|
||||||
|
@click="activeSegment = seg.value"
|
||||||
|
>
|
||||||
|
<span class="tree-arrow">›</span> {{ seg.label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧内容 -->
|
||||||
|
<div class="right-content">
|
||||||
|
<!-- 搜索栏 -->
|
||||||
|
<div class="search-bar">
|
||||||
|
<span class="search-label">点位名称:</span>
|
||||||
|
<el-input
|
||||||
|
v-model="filterName"
|
||||||
|
placeholder="请输入"
|
||||||
|
size="small"
|
||||||
|
style="width:200px"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="applyFilter"
|
||||||
|
/>
|
||||||
|
<el-button size="mini" type="primary" @click="applyFilter">查询</el-button>
|
||||||
|
<el-button size="mini" @click="resetFilter">重置</el-button>
|
||||||
|
<el-button
|
||||||
|
size="mini"
|
||||||
|
type="primary"
|
||||||
|
icon="el-icon-plus"
|
||||||
|
style="margin-left:auto"
|
||||||
|
@click="openPlanDialog()"
|
||||||
|
>新建方案点位</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 方案点位表 -->
|
||||||
|
<el-table
|
||||||
|
v-loading="planLoading"
|
||||||
|
:data="filteredPlans"
|
||||||
|
size="small"
|
||||||
|
highlight-current-row
|
||||||
|
@current-change="onPlanSelect"
|
||||||
|
>
|
||||||
|
<el-table-column label="序号" type="index" align="center" />
|
||||||
|
<el-table-column label="父级名称" show-overflow-tooltip>
|
||||||
|
<template slot-scope="{ row }">{{ segLabel(row.segmentType) }} › {{ row.segmentName || '—' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="点位名称" prop="pointName" show-overflow-tooltip />
|
||||||
|
<el-table-column label="实际值ID" prop="actualValueId" show-overflow-tooltip />
|
||||||
|
<el-table-column label="L1设定值ID" prop="l1SetValueId" show-overflow-tooltip />
|
||||||
|
<el-table-column label="设定值" prop="targetValue" align="center" />
|
||||||
|
<el-table-column label="下限" prop="lowerLimit" align="center" />
|
||||||
|
<el-table-column label="上限" prop="upperLimit" align="center" />
|
||||||
|
<el-table-column label="操作" align="right">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<el-button type="text" size="mini" @click.stop="openPlanDialog(row)">编辑</el-button>
|
||||||
|
<el-button type="text" size="mini" @click.stop="openParamDialog(row)">参数</el-button>
|
||||||
|
<el-button type="text" size="mini" class="btn-danger" @click.stop="removePlan(row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 方案参数面板 -->
|
||||||
|
<template v-if="selectedPlan">
|
||||||
|
<div class="param-header">
|
||||||
|
<span>{{ selectedPlan.pointName || selectedPlan.pointCode }} — 参数</span>
|
||||||
|
<el-button size="mini" type="primary" icon="el-icon-plus" @click="openParamDialog()">新建参数</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table v-loading="paramLoading" :data="paramList" size="small">
|
||||||
|
<el-table-column label="参数编码" prop="paramCode" show-overflow-tooltip />
|
||||||
|
<el-table-column label="参数名称" prop="paramName" show-overflow-tooltip />
|
||||||
|
<el-table-column label="设定值" prop="targetValue" align="center" />
|
||||||
|
<el-table-column label="下限" prop="lowerLimit" align="center" />
|
||||||
|
<el-table-column label="上限" prop="upperLimit" align="center" />
|
||||||
|
<el-table-column label="单位" prop="unit" align="center" />
|
||||||
|
<el-table-column label="操作" align="right">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<el-button type="text" size="mini" @click="openParamDialog(null, row)">编辑</el-button>
|
||||||
|
<el-button type="text" size="mini" class="btn-danger" @click="removeParam(row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 方案点位 dialog -->
|
||||||
|
<el-dialog :title="planTitle" :visible.sync="planOpen" width="520px" append-to-body @close="planForm = {}">
|
||||||
|
<el-form ref="planFormRef" :model="planForm" :rules="planRules" label-width="90px" size="small">
|
||||||
|
<el-form-item label="段类型" prop="segmentType">
|
||||||
|
<el-select v-model="planForm.segmentType" style="width:100%">
|
||||||
|
<el-option v-for="s in segmentOptions" :key="s.value" :label="s.label" :value="s.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="段名称" prop="segmentName">
|
||||||
|
<el-input v-model="planForm.segmentName" maxlength="100" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="点位名称" prop="pointName">
|
||||||
|
<el-input v-model="planForm.pointName" maxlength="200" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="点位编码" prop="pointCode">
|
||||||
|
<el-input v-model="planForm.pointCode" maxlength="64" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="实际值ID" prop="actualValueId">
|
||||||
|
<el-input v-model="planForm.actualValueId" maxlength="64" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="L1设定值ID" prop="l1SetValueId">
|
||||||
|
<el-input v-model="planForm.l1SetValueId" maxlength="64" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="设定值" prop="targetValue">
|
||||||
|
<el-input v-model="planForm.targetValue" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="下限" prop="lowerLimit">
|
||||||
|
<el-input v-model="planForm.lowerLimit" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="上限" prop="upperLimit">
|
||||||
|
<el-input v-model="planForm.upperLimit" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序">
|
||||||
|
<el-input-number v-model="planForm.sortOrder" :min="0" controls-position="right" style="width:100%" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注">
|
||||||
|
<el-input v-model="planForm.remark" type="textarea" rows="2" maxlength="500" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer">
|
||||||
|
<el-button size="small" @click="planOpen = false">取消</el-button>
|
||||||
|
<el-button size="small" type="primary" :loading="planSubmitLoading" @click="submitPlan">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 方案参数 dialog -->
|
||||||
|
<el-dialog :title="paramTitle" :visible.sync="paramOpen" width="480px" append-to-body @close="paramForm = {}">
|
||||||
|
<el-form ref="paramFormRef" :model="paramForm" :rules="paramRules" label-width="90px" size="small">
|
||||||
|
<el-form-item label="参数编码" prop="paramCode">
|
||||||
|
<el-input v-model="paramForm.paramCode" maxlength="64" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="参数名称" prop="paramName">
|
||||||
|
<el-input v-model="paramForm.paramName" maxlength="200" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="设定值">
|
||||||
|
<el-input v-model="paramForm.targetValue" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="下限">
|
||||||
|
<el-input v-model="paramForm.lowerLimit" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="上限">
|
||||||
|
<el-input v-model="paramForm.upperLimit" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="单位">
|
||||||
|
<el-input v-model="paramForm.unit" maxlength="32" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注">
|
||||||
|
<el-input v-model="paramForm.remark" type="textarea" rows="2" maxlength="500" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer">
|
||||||
|
<el-button size="small" @click="paramOpen = false">取消</el-button>
|
||||||
|
<el-button size="small" type="primary" :loading="paramSubmitLoading" @click="submitParam">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listProcessPlan, addProcessPlan, updateProcessPlan, delProcessPlan } from '@/api/wms/processPlan'
|
||||||
|
import { listProcessPlanParam, addProcessPlanParam, updateProcessPlanParam, delProcessPlanParam } from '@/api/wms/processPlanParam'
|
||||||
|
|
||||||
|
const SEGMENTS = [
|
||||||
|
{ label: '入口段', value: 'INLET' },
|
||||||
|
{ label: '工艺段', value: 'PROCESS' },
|
||||||
|
{ label: '出口段', value: 'OUTLET' }
|
||||||
|
]
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ProcessSpecPlanSpec',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
pageLoading: false,
|
||||||
|
versionId: undefined,
|
||||||
|
versionCode: '',
|
||||||
|
specId: undefined,
|
||||||
|
configMode: 'configurable',
|
||||||
|
activeSegment: '',
|
||||||
|
filterName: '',
|
||||||
|
appliedFilterName: '',
|
||||||
|
segmentOptions: SEGMENTS,
|
||||||
|
planList: [],
|
||||||
|
planLoading: false,
|
||||||
|
selectedPlan: null,
|
||||||
|
paramList: [],
|
||||||
|
paramLoading: false,
|
||||||
|
planOpen: false,
|
||||||
|
planTitle: '',
|
||||||
|
planSubmitLoading: false,
|
||||||
|
planForm: {},
|
||||||
|
planRules: {
|
||||||
|
segmentType: [{ required: true, message: '请选择段类型', trigger: 'change' }],
|
||||||
|
pointName: [{ required: true, message: '点位名称不能为空', trigger: 'blur' }],
|
||||||
|
pointCode: [{ required: true, message: '点位编码不能为空', trigger: 'blur' }]
|
||||||
|
},
|
||||||
|
paramOpen: false,
|
||||||
|
paramTitle: '',
|
||||||
|
paramSubmitLoading: false,
|
||||||
|
paramForm: {},
|
||||||
|
paramRules: {
|
||||||
|
paramCode: [{ required: true, message: '参数编码不能为空', trigger: 'blur' }],
|
||||||
|
paramName: [{ required: true, message: '参数名称不能为空', trigger: 'blur' }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
filteredPlans() {
|
||||||
|
return this.planList.filter(p => {
|
||||||
|
const segOk = !this.activeSegment || p.segmentType === this.activeSegment
|
||||||
|
const nameOk = !this.appliedFilterName || (p.pointName || '').includes(this.appliedFilterName)
|
||||||
|
return segOk && nameOk
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route: { immediate: true, handler() { this.syncFromRoute() } }
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
syncFromRoute() {
|
||||||
|
const q = this.$route.query
|
||||||
|
this.versionId = q.versionId || undefined
|
||||||
|
this.versionCode = q.versionCode || ''
|
||||||
|
this.specId = q.specId || undefined
|
||||||
|
if (this.versionId) this.loadPlans()
|
||||||
|
},
|
||||||
|
goBack() { this.$router.go(-1) },
|
||||||
|
segLabel(val) {
|
||||||
|
const hit = SEGMENTS.find(s => s.value === val)
|
||||||
|
return hit ? hit.label : val || ''
|
||||||
|
},
|
||||||
|
loadPlans() {
|
||||||
|
this.planLoading = true
|
||||||
|
this.selectedPlan = null
|
||||||
|
this.paramList = []
|
||||||
|
listProcessPlan({ versionId: this.versionId, pageNum: 1, pageSize: 500 }).then(res => {
|
||||||
|
this.planList = res.rows || []
|
||||||
|
}).catch(e => console.error(e)).finally(() => { this.planLoading = false })
|
||||||
|
},
|
||||||
|
loadParams(planId) {
|
||||||
|
this.paramLoading = true
|
||||||
|
listProcessPlanParam({ planId, pageNum: 1, pageSize: 500 }).then(res => {
|
||||||
|
this.paramList = res.rows || []
|
||||||
|
}).catch(e => console.error(e)).finally(() => { this.paramLoading = false })
|
||||||
|
},
|
||||||
|
onPlanSelect(row) {
|
||||||
|
if (!row) return
|
||||||
|
this.selectedPlan = row
|
||||||
|
this.loadParams(row.planId)
|
||||||
|
},
|
||||||
|
applyFilter() { this.appliedFilterName = this.filterName },
|
||||||
|
resetFilter() { this.filterName = ''; this.appliedFilterName = '' },
|
||||||
|
openPlanDialog(row) {
|
||||||
|
this.planForm = row
|
||||||
|
? { ...row }
|
||||||
|
: {
|
||||||
|
versionId: this.versionId,
|
||||||
|
segmentType: 'PROCESS',
|
||||||
|
segmentName: undefined,
|
||||||
|
pointName: undefined,
|
||||||
|
pointCode: undefined,
|
||||||
|
actualValueId: undefined,
|
||||||
|
l1SetValueId: undefined,
|
||||||
|
targetValue: undefined,
|
||||||
|
lowerLimit: undefined,
|
||||||
|
upperLimit: undefined,
|
||||||
|
sortOrder: 0,
|
||||||
|
remark: undefined
|
||||||
|
}
|
||||||
|
this.planTitle = row ? '编辑方案点位' : '新建方案点位'
|
||||||
|
this.planOpen = true
|
||||||
|
this.$nextTick(() => this.$refs.planFormRef && this.$refs.planFormRef.clearValidate())
|
||||||
|
},
|
||||||
|
submitPlan() {
|
||||||
|
this.$refs.planFormRef.validate(ok => {
|
||||||
|
if (!ok) return
|
||||||
|
this.planSubmitLoading = true
|
||||||
|
const req = this.planForm.planId ? updateProcessPlan(this.planForm) : addProcessPlan(this.planForm)
|
||||||
|
req.then(() => {
|
||||||
|
this.$modal.msgSuccess('保存成功')
|
||||||
|
this.planOpen = false
|
||||||
|
this.loadPlans()
|
||||||
|
}).catch(e => console.error(e)).finally(() => { this.planSubmitLoading = false })
|
||||||
|
})
|
||||||
|
},
|
||||||
|
removePlan(row) {
|
||||||
|
this.$modal.confirm('确认删除该方案点位?').then(() => {
|
||||||
|
return delProcessPlan(row.planId)
|
||||||
|
}).then(() => {
|
||||||
|
this.$modal.msgSuccess('删除成功')
|
||||||
|
if (this.selectedPlan && this.selectedPlan.planId === row.planId) {
|
||||||
|
this.selectedPlan = null
|
||||||
|
this.paramList = []
|
||||||
|
}
|
||||||
|
this.loadPlans()
|
||||||
|
}).catch(() => {})
|
||||||
|
},
|
||||||
|
openParamDialog(planRow, paramRow) {
|
||||||
|
const targetPlan = planRow || this.selectedPlan
|
||||||
|
if (!targetPlan) { this.$message.warning('请先选择一个方案点位'); return }
|
||||||
|
if (!this.selectedPlan || this.selectedPlan.planId !== targetPlan.planId) {
|
||||||
|
this.selectedPlan = targetPlan
|
||||||
|
this.loadParams(targetPlan.planId)
|
||||||
|
}
|
||||||
|
this.paramForm = paramRow
|
||||||
|
? { ...paramRow }
|
||||||
|
: {
|
||||||
|
planId: targetPlan.planId,
|
||||||
|
paramCode: undefined,
|
||||||
|
paramName: undefined,
|
||||||
|
targetValue: undefined,
|
||||||
|
lowerLimit: undefined,
|
||||||
|
upperLimit: undefined,
|
||||||
|
unit: undefined,
|
||||||
|
remark: undefined
|
||||||
|
}
|
||||||
|
this.paramTitle = paramRow ? '编辑方案参数' : '新建方案参数'
|
||||||
|
this.paramOpen = true
|
||||||
|
this.$nextTick(() => this.$refs.paramFormRef && this.$refs.paramFormRef.clearValidate())
|
||||||
|
},
|
||||||
|
submitParam() {
|
||||||
|
this.$refs.paramFormRef.validate(ok => {
|
||||||
|
if (!ok) return
|
||||||
|
this.paramSubmitLoading = true
|
||||||
|
const req = this.paramForm.paramId ? updateProcessPlanParam(this.paramForm) : addProcessPlanParam(this.paramForm)
|
||||||
|
req.then(() => {
|
||||||
|
this.$modal.msgSuccess('保存成功')
|
||||||
|
this.paramOpen = false
|
||||||
|
this.loadParams(this.selectedPlan.planId)
|
||||||
|
}).catch(e => console.error(e)).finally(() => { this.paramSubmitLoading = false })
|
||||||
|
})
|
||||||
|
},
|
||||||
|
removeParam(row) {
|
||||||
|
this.$modal.confirm('确认删除该参数?').then(() => {
|
||||||
|
return delProcessPlanParam(row.paramId)
|
||||||
|
}).then(() => {
|
||||||
|
this.$modal.msgSuccess('删除成功')
|
||||||
|
this.loadParams(this.selectedPlan.planId)
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.plan-spec-page {
|
||||||
|
padding: 16px 20px;
|
||||||
|
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-badge {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
background: #f0f2f5;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 0;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-tab {
|
||||||
|
padding: 5px 20px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #606266;
|
||||||
|
background: #fff;
|
||||||
|
border: none;
|
||||||
|
border-right: 1px solid #dcdfe6;
|
||||||
|
transition: color .15s, background .15s;
|
||||||
|
user-select: none;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-tab:last-child { border-right: none; }
|
||||||
|
.config-tab:hover { color: #5F7BA0; }
|
||||||
|
|
||||||
|
.config-tab.active {
|
||||||
|
color: #fff;
|
||||||
|
background: #5F7BA0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-layout {
|
||||||
|
display: flex;
|
||||||
|
gap: 0;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-tree {
|
||||||
|
width: 120px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-right: 1px solid #ebeef5;
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-item {
|
||||||
|
padding: 9px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #606266;
|
||||||
|
transition: all .15s;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-item:hover { background: #f5f7fa; }
|
||||||
|
|
||||||
|
.tree-item.active {
|
||||||
|
background: #5F7BA0;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-item.active .tree-arrow { color: rgba(255,255,255,.6); }
|
||||||
|
|
||||||
|
.tree-arrow {
|
||||||
|
margin-right: 4px;
|
||||||
|
color: #c0c4cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-label {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #606266;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 0 8px;
|
||||||
|
margin-top: 12px;
|
||||||
|
border-top: 1px solid #ebeef5;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table { border-radius: 0; }
|
||||||
|
|
||||||
|
::v-deep .el-button--primary {
|
||||||
|
color: #fff !important;
|
||||||
|
background: #5F7BA0 !important;
|
||||||
|
border-color: #5F7BA0 !important;
|
||||||
|
}
|
||||||
|
::v-deep .el-button--primary:hover,
|
||||||
|
::v-deep .el-button--primary:focus { background: #4d6a8e !important; border-color: #4d6a8e !important; }
|
||||||
|
::v-deep .el-button--primary:active { background: #4a6585 !important; border-color: #4a6585 !important; }
|
||||||
|
::v-deep .el-button--primary.is-disabled { opacity: .5; }
|
||||||
|
|
||||||
|
::v-deep .el-button:not(.el-button--primary):not(.el-button--text):not(.el-button--danger) {
|
||||||
|
color: #606266 !important;
|
||||||
|
background: #fff !important;
|
||||||
|
border-color: #dcdfe6 !important;
|
||||||
|
}
|
||||||
|
::v-deep .el-button:not(.el-button--primary):not(.el-button--text):not(.el-button--danger):hover {
|
||||||
|
color: #5F7BA0 !important;
|
||||||
|
border-color: #5F7BA0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-button--text { background: transparent !important; border-color: transparent !important; }
|
||||||
|
::v-deep .el-button--text.btn-danger { color: #f56c6c !important; }
|
||||||
|
|
||||||
|
.btn-danger { color: #f56c6c; }
|
||||||
|
</style>
|
||||||
@@ -1,192 +1,97 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container" v-loading="pageLoading">
|
<div class="ver-page" v-loading="pageLoading">
|
||||||
<el-page-header v-if="specInfo.specId" @back="goBack" :content="'规程版本与方案 — ' + (specInfo.specName || '') + '(' + (specInfo.specCode || '') + ')'" />
|
<!-- 头部 -->
|
||||||
<el-card v-else shadow="never" class="mb8">
|
<div class="page-header">
|
||||||
<div slot="header">请选择规程</div>
|
<el-button type="text" icon="el-icon-arrow-left" @click="goBack">返回</el-button>
|
||||||
<p class="text-muted mb8" style="color: #909399; font-size: 13px">从「规程管理」列表进入时会自动带上规程;从菜单直接进入时请先选择规程。</p>
|
<span class="page-title" v-if="specInfo.specName">
|
||||||
<el-form inline size="small" @submit.native.prevent>
|
{{ specInfo.specName }}
|
||||||
<el-form-item label="规程">
|
<span class="spec-code">{{ specInfo.specCode }}</span>
|
||||||
<el-select
|
</span>
|
||||||
v-model="specPickerId"
|
<el-button
|
||||||
filterable
|
v-if="specInfo.specId"
|
||||||
clearable
|
type="primary"
|
||||||
placeholder="请选择规程"
|
size="mini"
|
||||||
style="min-width: 280px"
|
icon="el-icon-plus"
|
||||||
:loading="specPickerLoading"
|
style="margin-left:auto"
|
||||||
>
|
@click="openVersionDialog()"
|
||||||
<el-option v-for="s in specPickerOptions" :key="s.specId" :label="(s.specCode || '') + ' — ' + (s.specName || '')" :value="String(s.specId)" />
|
>新建版本</el-button>
|
||||||
</el-select>
|
</div>
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
<!-- 无 specId 时选择规程 -->
|
||||||
<el-button type="primary" :disabled="!specPickerId" @click="applySpecPicker">进入维护</el-button>
|
<div v-if="!specInfo.specId" class="pick-card">
|
||||||
</el-form-item>
|
<p class="pick-hint">从「规程管理」列表进入会自动带上规程;也可在下方手动选择。</p>
|
||||||
</el-form>
|
<el-select
|
||||||
</el-card>
|
v-model="specPickerId"
|
||||||
|
filterable clearable
|
||||||
|
placeholder="选择规程"
|
||||||
|
style="width:300px; margin-right:8px"
|
||||||
|
:loading="specPickerLoading"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="s in specPickerOptions"
|
||||||
|
:key="s.specId"
|
||||||
|
:label="(s.specCode || '') + ' — ' + (s.specName || '')"
|
||||||
|
:value="String(s.specId)"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<el-button type="primary" size="small" :disabled="!specPickerId" @click="applySpecPicker">进入维护</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<template v-if="specInfo.specId">
|
<template v-if="specInfo.specId">
|
||||||
<el-card shadow="never" class="mb8">
|
<!-- 版本列表 -->
|
||||||
<div slot="header" class="clearfix">
|
<div class="section-title">规程版本</div>
|
||||||
<span>规程版本</span>
|
<el-table
|
||||||
<el-button style="float: right; padding: 3px 10px" type="primary" size="mini" icon="el-icon-plus" @click="openVersionDialog()">新建版本</el-button>
|
:data="versionList"
|
||||||
</div>
|
size="small"
|
||||||
<KLPTable
|
highlight-current-row
|
||||||
:data="versionList"
|
@row-click="onVersionRowClick"
|
||||||
highlight-current-row
|
>
|
||||||
@row-click="onVersionRowClick"
|
<el-table-column label="版本号" prop="versionCode" />
|
||||||
>
|
<el-table-column label="状态" prop="status" />
|
||||||
<el-table-column label="版本号" prop="versionCode" min-width="120" align="center" />
|
<el-table-column label="创建时间" prop="createTime" />
|
||||||
<el-table-column label="是否生效" prop="isActive" width="100" align="center">
|
<el-table-column label="生效" align="center">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="{ row }">
|
||||||
<el-tag v-if="scope.row.isActive === 1" type="success" size="mini">生效</el-tag>
|
<el-switch
|
||||||
<span v-else>否</span>
|
:value="row.isActive === 1"
|
||||||
</template>
|
active-color="#5F7BA0"
|
||||||
</el-table-column>
|
@click.native.stop
|
||||||
<el-table-column label="状态" prop="status" width="120" align="center" />
|
@change="handleActiveChange(row, $event)"
|
||||||
<el-table-column label="创建时间" prop="createTime" width="170" align="center" />
|
/>
|
||||||
<el-table-column label="操作" width="220" align="center" fixed="right">
|
</template>
|
||||||
<template slot-scope="scope">
|
</el-table-column>
|
||||||
<el-button type="text" size="mini" @click.stop="activateVersion(scope.row)">设为生效</el-button>
|
<el-table-column label="操作" align="right">
|
||||||
<el-button type="text" size="mini" @click.stop="openVersionDialog(scope.row)">编辑</el-button>
|
<template slot-scope="{ row }">
|
||||||
<el-button type="text" size="mini" @click.stop="removeVersion(scope.row)">删除</el-button>
|
<el-button type="text" size="mini" @click.stop="goPlanSpec(row)">方案点位</el-button>
|
||||||
</template>
|
<el-button type="text" size="mini" @click.stop="openVersionDialog(row)">编辑</el-button>
|
||||||
</el-table-column>
|
<el-button type="text" size="mini" class="btn-danger" @click.stop="removeVersion(row)">删除</el-button>
|
||||||
</KLPTable>
|
</template>
|
||||||
</el-card>
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
<el-card shadow="never" v-if="selectedVersion">
|
<el-empty v-if="!versionList.length && !pageLoading" description="暂无版本,请新建" style="padding:40px 0" />
|
||||||
<div slot="header" class="clearfix">
|
|
||||||
<span>方案点位(版本 {{ selectedVersion.versionCode }})</span>
|
|
||||||
<el-button style="float: right; padding: 3px 10px" type="primary" size="mini" icon="el-icon-plus" @click="openPlanDialog()">新建方案点位</el-button>
|
|
||||||
</div>
|
|
||||||
<KLPTable
|
|
||||||
v-loading="planLoading"
|
|
||||||
:data="planList"
|
|
||||||
highlight-current-row
|
|
||||||
@row-click="onPlanRowClick"
|
|
||||||
>
|
|
||||||
<el-table-column label="段类型" prop="segmentType" width="110" align="center" />
|
|
||||||
<el-table-column label="段名称" prop="segmentName" min-width="100" show-overflow-tooltip align="center" />
|
|
||||||
<el-table-column label="点位名称" prop="pointName" min-width="120" show-overflow-tooltip align="center" />
|
|
||||||
<el-table-column label="点位编码" prop="pointCode" min-width="120" align="center" />
|
|
||||||
<el-table-column label="排序" prop="sortOrder" width="80" align="center" />
|
|
||||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-button type="text" size="mini" @click.stop="openPlanDialog(scope.row)">编辑</el-button>
|
|
||||||
<el-button type="text" size="mini" @click.stop="removePlan(scope.row)">删除</el-button>
|
|
||||||
<el-button type="text" size="mini" @click.stop="onPlanRowClick(scope.row)">维护参数</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</KLPTable>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<el-card shadow="never" v-if="selectedVersion && selectedPlan" class="mb8">
|
|
||||||
<div slot="header" class="clearfix">
|
|
||||||
<span>方案参数({{ selectedPlan.pointName || selectedPlan.pointCode }})</span>
|
|
||||||
<el-button style="float: right; padding: 3px 10px" type="primary" size="mini" icon="el-icon-plus" @click="openParamDialog()">新建参数</el-button>
|
|
||||||
</div>
|
|
||||||
<KLPTable v-loading="paramLoading" :data="paramList">
|
|
||||||
<el-table-column label="参数编码" prop="paramCode" min-width="100" align="center" />
|
|
||||||
<el-table-column label="参数名称" prop="paramName" min-width="120" show-overflow-tooltip align="center" />
|
|
||||||
<el-table-column label="设定值" prop="targetValue" width="100" align="center" />
|
|
||||||
<el-table-column label="下限" prop="lowerLimit" width="90" align="center" />
|
|
||||||
<el-table-column label="上限" prop="upperLimit" width="90" align="center" />
|
|
||||||
<el-table-column label="单位" prop="unit" width="80" align="center" />
|
|
||||||
<el-table-column label="操作" width="140" align="center" fixed="right">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-button type="text" size="mini" @click="openParamDialog(scope.row)">编辑</el-button>
|
|
||||||
<el-button type="text" size="mini" @click="removeParam(scope.row)">删除</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</KLPTable>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<el-card v-if="specInfo.specId && !selectedVersion" shadow="never" class="mb8">
|
|
||||||
<el-empty description="请在上方选择一个规程版本以维护方案点位" />
|
|
||||||
</el-card>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 版本 -->
|
<!-- 新建/编辑版本 -->
|
||||||
<el-dialog :title="versionTitle" :visible.sync="versionOpen" width="520px" append-to-body @close="versionForm = {}">
|
<el-dialog :title="versionTitle" :visible.sync="versionOpen" width="480px" append-to-body @close="versionForm = {}">
|
||||||
<el-form ref="versionFormRef" :model="versionForm" :rules="versionRules" label-width="100px">
|
<el-form ref="versionFormRef" :model="versionForm" :rules="versionRules" label-width="88px" size="small">
|
||||||
<el-form-item label="版本号" prop="versionCode">
|
<el-form-item label="版本号" prop="versionCode">
|
||||||
<el-input v-model="versionForm.versionCode" maxlength="64" placeholder="如 V1.0" />
|
<el-input v-model="versionForm.versionCode" placeholder="如 V1.0" maxlength="64" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="状态" prop="status">
|
<el-form-item label="状态" prop="status">
|
||||||
<el-select v-model="versionForm.status" placeholder="请选择" style="width: 100%">
|
<el-select v-model="versionForm.status" style="width:100%">
|
||||||
<el-option v-for="s in statusOptions" :key="s" :label="s" :value="s" />
|
<el-option v-for="s in statusOptions" :key="s" :label="s" :value="s" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="保存后生效" prop="isActive">
|
<el-form-item label="保存后生效">
|
||||||
<el-switch v-model="versionForm.isActive" :active-value="1" :inactive-value="0" />
|
<el-switch v-model="versionForm.isActive" :active-value="1" :inactive-value="0" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="备注" prop="remark">
|
<el-form-item label="备注">
|
||||||
<el-input v-model="versionForm.remark" type="textarea" rows="2" maxlength="500" show-word-limit />
|
<el-input v-model="versionForm.remark" type="textarea" rows="2" maxlength="500" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer">
|
||||||
<el-button :loading="versionSubmitLoading" type="primary" @click="submitVersion">确 定</el-button>
|
<el-button size="small" @click="versionOpen = false">取消</el-button>
|
||||||
<el-button @click="versionOpen = false">取 消</el-button>
|
<el-button size="small" type="primary" :loading="versionSubmitLoading" @click="submitVersion">确定</el-button>
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<!-- 方案点位 -->
|
|
||||||
<el-dialog :title="planTitle" :visible.sync="planOpen" width="560px" append-to-body @close="planForm = {}">
|
|
||||||
<el-form ref="planFormRef" :model="planForm" :rules="planRules" label-width="100px">
|
|
||||||
<el-form-item label="段类型" prop="segmentType">
|
|
||||||
<el-select v-model="planForm.segmentType" placeholder="请选择" style="width: 100%">
|
|
||||||
<el-option v-for="s in segmentOptions" :key="s.value" :label="s.label" :value="s.value" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="段名称" prop="segmentName">
|
|
||||||
<el-input v-model="planForm.segmentName" maxlength="100" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="点位名称" prop="pointName">
|
|
||||||
<el-input v-model="planForm.pointName" maxlength="200" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="点位编码" prop="pointCode">
|
|
||||||
<el-input v-model="planForm.pointCode" maxlength="64" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="排序" prop="sortOrder">
|
|
||||||
<el-input-number v-model="planForm.sortOrder" :min="0" :max="999999" controls-position="right" style="width: 100%" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="备注" prop="remark">
|
|
||||||
<el-input v-model="planForm.remark" type="textarea" rows="2" maxlength="500" show-word-limit />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<div slot="footer" class="dialog-footer">
|
|
||||||
<el-button :loading="planSubmitLoading" type="primary" @click="submitPlan">确 定</el-button>
|
|
||||||
<el-button @click="planOpen = false">取 消</el-button>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<!-- 方案参数 -->
|
|
||||||
<el-dialog :title="paramTitle" :visible.sync="paramOpen" width="560px" append-to-body @close="paramForm = {}">
|
|
||||||
<el-form ref="paramFormRef" :model="paramForm" :rules="paramRules" label-width="100px">
|
|
||||||
<el-form-item label="参数编码" prop="paramCode">
|
|
||||||
<el-input v-model="paramForm.paramCode" maxlength="64" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="参数名称" prop="paramName">
|
|
||||||
<el-input v-model="paramForm.paramName" maxlength="200" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="设定值" prop="targetValue">
|
|
||||||
<el-input v-model="paramForm.targetValue" placeholder="数值" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="下限" prop="lowerLimit">
|
|
||||||
<el-input v-model="paramForm.lowerLimit" placeholder="数值" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="上限" prop="upperLimit">
|
|
||||||
<el-input v-model="paramForm.upperLimit" placeholder="数值" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="单位" prop="unit">
|
|
||||||
<el-input v-model="paramForm.unit" maxlength="32" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="备注" prop="remark">
|
|
||||||
<el-input v-model="paramForm.remark" type="textarea" rows="2" maxlength="500" show-word-limit />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<div slot="footer" class="dialog-footer">
|
|
||||||
<el-button :loading="paramSubmitLoading" type="primary" @click="submitParam">确 定</el-button>
|
|
||||||
<el-button @click="paramOpen = false">取 消</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
@@ -201,8 +106,6 @@ import {
|
|||||||
delProcessSpecVersion,
|
delProcessSpecVersion,
|
||||||
activateProcessSpecVersion
|
activateProcessSpecVersion
|
||||||
} from '@/api/wms/processSpecVersion'
|
} from '@/api/wms/processSpecVersion'
|
||||||
import { listProcessPlan, addProcessPlan, updateProcessPlan, delProcessPlan } from '@/api/wms/processPlan'
|
|
||||||
import { listProcessPlanParam, addProcessPlanParam, updateProcessPlanParam, delProcessPlanParam } from '@/api/wms/processPlanParam'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProcessSpecVersionManage',
|
name: 'ProcessSpecVersionManage',
|
||||||
@@ -212,18 +115,7 @@ export default {
|
|||||||
specId: undefined,
|
specId: undefined,
|
||||||
specInfo: {},
|
specInfo: {},
|
||||||
versionList: [],
|
versionList: [],
|
||||||
selectedVersion: null,
|
|
||||||
planList: [],
|
|
||||||
planLoading: false,
|
|
||||||
selectedPlan: null,
|
|
||||||
paramList: [],
|
|
||||||
paramLoading: false,
|
|
||||||
statusOptions: ['DRAFT', 'PUBLISHED', 'OBSOLETE'],
|
statusOptions: ['DRAFT', 'PUBLISHED', 'OBSOLETE'],
|
||||||
segmentOptions: [
|
|
||||||
{ label: '入口', value: 'INLET' },
|
|
||||||
{ label: '过程', value: 'PROCESS' },
|
|
||||||
{ label: '出口', value: 'OUTLET' }
|
|
||||||
],
|
|
||||||
versionOpen: false,
|
versionOpen: false,
|
||||||
versionTitle: '',
|
versionTitle: '',
|
||||||
versionSubmitLoading: false,
|
versionSubmitLoading: false,
|
||||||
@@ -232,180 +124,62 @@ export default {
|
|||||||
versionCode: [{ required: true, message: '版本号不能为空', trigger: 'blur' }],
|
versionCode: [{ required: true, message: '版本号不能为空', trigger: 'blur' }],
|
||||||
status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
|
status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
|
||||||
},
|
},
|
||||||
planOpen: false,
|
|
||||||
planTitle: '',
|
|
||||||
planSubmitLoading: false,
|
|
||||||
planForm: {},
|
|
||||||
planRules: {
|
|
||||||
segmentType: [{ required: true, message: '段类型不能为空', trigger: 'change' }],
|
|
||||||
pointName: [{ required: true, message: '点位名称不能为空', trigger: 'blur' }],
|
|
||||||
pointCode: [{ required: true, message: '点位编码不能为空', trigger: 'blur' }],
|
|
||||||
sortOrder: [{ required: true, message: '排序不能为空', trigger: 'blur' }]
|
|
||||||
},
|
|
||||||
specPickerId: '',
|
specPickerId: '',
|
||||||
specPickerOptions: [],
|
specPickerOptions: [],
|
||||||
specPickerLoading: false,
|
specPickerLoading: false
|
||||||
paramOpen: false,
|
|
||||||
paramTitle: '',
|
|
||||||
paramSubmitLoading: false,
|
|
||||||
paramForm: {},
|
|
||||||
paramRules: {
|
|
||||||
paramCode: [{ required: true, message: '参数编码不能为空', trigger: 'blur' }],
|
|
||||||
paramName: [{ required: true, message: '参数名称不能为空', trigger: 'blur' }]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'$route': {
|
$route: { immediate: true, handler() { this.syncFromRoute() } }
|
||||||
immediate: true,
|
|
||||||
handler() {
|
|
||||||
this.syncSpecIdFromRoute()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
syncSpecIdFromRoute() {
|
syncFromRoute() {
|
||||||
const raw = this.$route.query.specId
|
const raw = this.$route.query.specId
|
||||||
if (raw != null && raw !== '') {
|
this.specId = raw != null && raw !== '' ? String(raw) : undefined
|
||||||
this.specId = String(raw)
|
|
||||||
} else {
|
|
||||||
this.specId = undefined
|
|
||||||
}
|
|
||||||
this.initPage()
|
this.initPage()
|
||||||
},
|
},
|
||||||
loadSpecPickerOptions() {
|
goBack() { this.$router.go(-1) },
|
||||||
this.specPickerLoading = true
|
|
||||||
listProcessSpec({ pageNum: 1, pageSize: 500 })
|
|
||||||
.then((res) => {
|
|
||||||
this.specPickerOptions = res.rows || []
|
|
||||||
})
|
|
||||||
.catch((e) => console.error('加载规程列表失败', e))
|
|
||||||
.finally(() => {
|
|
||||||
this.specPickerLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
applySpecPicker() {
|
|
||||||
if (!this.specPickerId) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.$router.replace({
|
|
||||||
path: this.$route.path,
|
|
||||||
query: { ...this.$route.query, specId: this.specPickerId }
|
|
||||||
})
|
|
||||||
},
|
|
||||||
goBack() {
|
|
||||||
this.$router.go(-1)
|
|
||||||
},
|
|
||||||
initPage() {
|
initPage() {
|
||||||
if (!this.specId) {
|
if (!this.specId) {
|
||||||
this.specInfo = {}
|
this.specInfo = {}
|
||||||
if (this.specPickerOptions.length === 0) {
|
if (!this.specPickerOptions.length) this.loadSpecPicker()
|
||||||
this.loadSpecPickerOptions()
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.pageLoading = true
|
this.pageLoading = true
|
||||||
getProcessSpec(this.specId)
|
getProcessSpec(this.specId).then(res => {
|
||||||
.then((res) => {
|
this.specInfo = res.data || {}
|
||||||
this.specInfo = res.data || {}
|
return listProcessSpecVersion({ specId: this.specId, pageNum: 1, pageSize: 200 })
|
||||||
return listProcessSpecVersion({ specId: this.specId, pageNum: 1, pageSize: 200 })
|
}).then(res => {
|
||||||
})
|
this.versionList = res.rows || []
|
||||||
.then((res) => {
|
}).catch(e => console.error(e)).finally(() => { this.pageLoading = false })
|
||||||
this.versionList = res.rows || []
|
},
|
||||||
this.selectedVersion = null
|
loadSpecPicker() {
|
||||||
this.planList = []
|
this.specPickerLoading = true
|
||||||
this.selectedPlan = null
|
listProcessSpec({ pageNum: 1, pageSize: 500 }).then(res => {
|
||||||
this.paramList = []
|
this.specPickerOptions = res.rows || []
|
||||||
})
|
}).finally(() => { this.specPickerLoading = false })
|
||||||
.catch((e) => {
|
},
|
||||||
console.error('加载规程版本失败', e)
|
applySpecPicker() {
|
||||||
})
|
if (!this.specPickerId) return
|
||||||
.finally(() => {
|
this.$router.replace({ path: this.$route.path, query: { ...this.$route.query, specId: this.specPickerId } })
|
||||||
this.pageLoading = false
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
onVersionRowClick(row) {
|
onVersionRowClick(row) {
|
||||||
this.selectedVersion = row
|
this.goPlanSpec(row)
|
||||||
this.selectedPlan = null
|
|
||||||
this.paramList = []
|
|
||||||
this.loadPlans(row.versionId)
|
|
||||||
},
|
},
|
||||||
onPlanRowClick(row) {
|
goPlanSpec(row) {
|
||||||
this.selectedPlan = row
|
const pathCurrent = this.$route.path.replace(/\/$/, '')
|
||||||
this.loadParams(row.planId)
|
const m = pathCurrent.match(/^(.*\/processSpec)(?:\/.*)?$/)
|
||||||
},
|
const base = m ? m[1] : pathCurrent
|
||||||
loadPlans(versionId) {
|
this.$router.push({
|
||||||
if (!versionId) {
|
path: `${base}/planSpec`,
|
||||||
this.planList = []
|
query: { specId: this.specId, versionId: String(row.versionId), versionCode: row.versionCode }
|
||||||
this.selectedPlan = null
|
|
||||||
this.paramList = []
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.selectedPlan = null
|
|
||||||
this.paramList = []
|
|
||||||
this.planLoading = true
|
|
||||||
listProcessPlan({ versionId, pageNum: 1, pageSize: 500 })
|
|
||||||
.then((res) => {
|
|
||||||
this.planList = res.rows || []
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
console.error('加载方案点位失败', e)
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.planLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
loadParams(planId) {
|
|
||||||
if (!planId) {
|
|
||||||
this.paramList = []
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.paramLoading = true
|
|
||||||
listProcessPlanParam({ planId, pageNum: 1, pageSize: 500 })
|
|
||||||
.then((res) => {
|
|
||||||
this.paramList = res.rows || []
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
console.error('加载方案参数失败', e)
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.paramLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
openVersionDialog(row) {
|
|
||||||
this.versionForm = row
|
|
||||||
? { ...row }
|
|
||||||
: {
|
|
||||||
specId: this.specId,
|
|
||||||
versionCode: undefined,
|
|
||||||
status: 'DRAFT',
|
|
||||||
isActive: 0,
|
|
||||||
remark: undefined
|
|
||||||
}
|
|
||||||
this.versionTitle = row ? '编辑版本' : '新建版本'
|
|
||||||
this.versionOpen = true
|
|
||||||
this.$nextTick(() => this.$refs.versionFormRef && this.$refs.versionFormRef.clearValidate())
|
|
||||||
},
|
|
||||||
submitVersion() {
|
|
||||||
this.$refs.versionFormRef.validate((ok) => {
|
|
||||||
if (!ok) return
|
|
||||||
this.versionSubmitLoading = true
|
|
||||||
const req = this.versionForm.versionId
|
|
||||||
? updateProcessSpecVersion({ ...this.versionForm, specId: this.specId })
|
|
||||||
: addProcessSpecVersion({ ...this.versionForm, specId: this.specId })
|
|
||||||
req
|
|
||||||
.then(() => {
|
|
||||||
this.$modal.msgSuccess('保存成功')
|
|
||||||
this.versionOpen = false
|
|
||||||
this.initPage()
|
|
||||||
})
|
|
||||||
.catch((e) => console.error('保存版本失败', e))
|
|
||||||
.finally(() => {
|
|
||||||
this.versionSubmitLoading = false
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
activateVersion(row) {
|
handleActiveChange(row, val) {
|
||||||
|
if (!val) {
|
||||||
|
this.$message.info('请激活其他版本来替换当前生效版本')
|
||||||
|
return
|
||||||
|
}
|
||||||
this.$modal.confirm('确认将版本「' + row.versionCode + '」设为当前生效版本?').then(() => {
|
this.$modal.confirm('确认将版本「' + row.versionCode + '」设为当前生效版本?').then(() => {
|
||||||
return activateProcessSpecVersion(row.versionId)
|
return activateProcessSpecVersion(row.versionId)
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
@@ -413,129 +187,116 @@ export default {
|
|||||||
this.initPage()
|
this.initPage()
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
},
|
},
|
||||||
|
openVersionDialog(row) {
|
||||||
|
this.versionForm = row
|
||||||
|
? { ...row }
|
||||||
|
: { specId: this.specId, versionCode: undefined, status: 'DRAFT', isActive: 0, remark: undefined }
|
||||||
|
this.versionTitle = row ? '编辑版本' : '新建版本'
|
||||||
|
this.versionOpen = true
|
||||||
|
this.$nextTick(() => this.$refs.versionFormRef && this.$refs.versionFormRef.clearValidate())
|
||||||
|
},
|
||||||
|
submitVersion() {
|
||||||
|
this.$refs.versionFormRef.validate(ok => {
|
||||||
|
if (!ok) return
|
||||||
|
this.versionSubmitLoading = true
|
||||||
|
const req = this.versionForm.versionId
|
||||||
|
? updateProcessSpecVersion({ ...this.versionForm, specId: this.specId })
|
||||||
|
: addProcessSpecVersion({ ...this.versionForm, specId: this.specId })
|
||||||
|
req.then(() => {
|
||||||
|
this.$modal.msgSuccess('保存成功')
|
||||||
|
this.versionOpen = false
|
||||||
|
this.initPage()
|
||||||
|
}).catch(e => console.error(e)).finally(() => { this.versionSubmitLoading = false })
|
||||||
|
})
|
||||||
|
},
|
||||||
removeVersion(row) {
|
removeVersion(row) {
|
||||||
this.$modal.confirm('确认删除版本「' + row.versionCode + '」及其下方案点位?').then(() => {
|
this.$modal.confirm('确认删除版本「' + row.versionCode + '」?').then(() => {
|
||||||
return delProcessSpecVersion(row.versionId)
|
return delProcessSpecVersion(row.versionId)
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.$modal.msgSuccess('删除成功')
|
this.$modal.msgSuccess('删除成功')
|
||||||
this.selectedVersion = null
|
|
||||||
this.planList = []
|
|
||||||
this.selectedPlan = null
|
|
||||||
this.paramList = []
|
|
||||||
this.initPage()
|
this.initPage()
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
},
|
|
||||||
openPlanDialog(row) {
|
|
||||||
if (!this.selectedVersion) {
|
|
||||||
this.$modal.msgWarning('请先选择一个规程版本')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.planForm = row
|
|
||||||
? { ...row }
|
|
||||||
: {
|
|
||||||
versionId: this.selectedVersion.versionId,
|
|
||||||
segmentType: 'PROCESS',
|
|
||||||
segmentName: undefined,
|
|
||||||
pointName: undefined,
|
|
||||||
pointCode: undefined,
|
|
||||||
sortOrder: 0,
|
|
||||||
remark: undefined
|
|
||||||
}
|
|
||||||
this.planTitle = row ? '编辑方案点位' : '新建方案点位'
|
|
||||||
this.planOpen = true
|
|
||||||
this.$nextTick(() => this.$refs.planFormRef && this.$refs.planFormRef.clearValidate())
|
|
||||||
},
|
|
||||||
submitPlan() {
|
|
||||||
this.$refs.planFormRef.validate((ok) => {
|
|
||||||
if (!ok) return
|
|
||||||
this.planSubmitLoading = true
|
|
||||||
const req = this.planForm.planId ? updateProcessPlan(this.planForm) : addProcessPlan(this.planForm)
|
|
||||||
req
|
|
||||||
.then(() => {
|
|
||||||
this.$modal.msgSuccess('保存成功')
|
|
||||||
this.planOpen = false
|
|
||||||
this.loadPlans(this.selectedVersion.versionId)
|
|
||||||
})
|
|
||||||
.catch((e) => console.error('保存方案点位失败', e))
|
|
||||||
.finally(() => {
|
|
||||||
this.planSubmitLoading = false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
removePlan(row) {
|
|
||||||
this.$modal.confirm('确认删除该方案点位?').then(() => {
|
|
||||||
return delProcessPlan(row.planId)
|
|
||||||
}).then(() => {
|
|
||||||
this.$modal.msgSuccess('删除成功')
|
|
||||||
if (this.selectedPlan && this.selectedPlan.planId === row.planId) {
|
|
||||||
this.selectedPlan = null
|
|
||||||
this.paramList = []
|
|
||||||
}
|
|
||||||
this.loadPlans(this.selectedVersion.versionId)
|
|
||||||
}).catch(() => {})
|
|
||||||
},
|
|
||||||
openParamDialog(row) {
|
|
||||||
if (!this.selectedPlan) {
|
|
||||||
this.$modal.msgWarning('请先在上方方案点位表格中选中一行')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.paramForm = row
|
|
||||||
? { ...row }
|
|
||||||
: {
|
|
||||||
planId: this.selectedPlan.planId,
|
|
||||||
paramCode: undefined,
|
|
||||||
paramName: undefined,
|
|
||||||
targetValue: undefined,
|
|
||||||
lowerLimit: undefined,
|
|
||||||
upperLimit: undefined,
|
|
||||||
unit: undefined,
|
|
||||||
remark: undefined
|
|
||||||
}
|
|
||||||
this.paramTitle = row ? '编辑方案参数' : '新建方案参数'
|
|
||||||
this.paramOpen = true
|
|
||||||
this.$nextTick(() => this.$refs.paramFormRef && this.$refs.paramFormRef.clearValidate())
|
|
||||||
},
|
|
||||||
parseDecimal(val) {
|
|
||||||
if (val === undefined || val === null || val === '') {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
const n = Number(val)
|
|
||||||
return Number.isFinite(n) ? n : undefined
|
|
||||||
},
|
|
||||||
buildParamPayload() {
|
|
||||||
return {
|
|
||||||
...this.paramForm,
|
|
||||||
targetValue: this.parseDecimal(this.paramForm.targetValue),
|
|
||||||
lowerLimit: this.parseDecimal(this.paramForm.lowerLimit),
|
|
||||||
upperLimit: this.parseDecimal(this.paramForm.upperLimit)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
submitParam() {
|
|
||||||
this.$refs.paramFormRef.validate((ok) => {
|
|
||||||
if (!ok) return
|
|
||||||
this.paramSubmitLoading = true
|
|
||||||
const payload = this.buildParamPayload()
|
|
||||||
const req = payload.paramId ? updateProcessPlanParam(payload) : addProcessPlanParam(payload)
|
|
||||||
req
|
|
||||||
.then(() => {
|
|
||||||
this.$modal.msgSuccess('保存成功')
|
|
||||||
this.paramOpen = false
|
|
||||||
this.loadParams(this.selectedPlan.planId)
|
|
||||||
})
|
|
||||||
.catch((e) => console.error('保存方案参数失败', e))
|
|
||||||
.finally(() => {
|
|
||||||
this.paramSubmitLoading = false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
removeParam(row) {
|
|
||||||
this.$modal.confirm('确认删除该方案参数?').then(() => {
|
|
||||||
return delProcessPlanParam(row.paramId)
|
|
||||||
}).then(() => {
|
|
||||||
this.$modal.msgSuccess('删除成功')
|
|
||||||
this.loadParams(this.selectedPlan.planId)
|
|
||||||
}).catch(() => {})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.ver-page {
|
||||||
|
padding: 16px 20px;
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
border-bottom: 1px solid #ebeef5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spec-code {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: normal;
|
||||||
|
color: #909399;
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pick-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 24px;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pick-hint {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #909399;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #606266;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table {
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-button--primary {
|
||||||
|
color: #fff !important;
|
||||||
|
background: #5F7BA0 !important;
|
||||||
|
border-color: #5F7BA0 !important;
|
||||||
|
}
|
||||||
|
::v-deep .el-button--primary:hover,
|
||||||
|
::v-deep .el-button--primary:focus { background: #4d6a8e !important; border-color: #4d6a8e !important; }
|
||||||
|
::v-deep .el-button--primary:active { background: #4a6585 !important; border-color: #4a6585 !important; }
|
||||||
|
::v-deep .el-button--primary.is-disabled { opacity: .5; }
|
||||||
|
|
||||||
|
::v-deep .el-button:not(.el-button--primary):not(.el-button--text):not(.el-button--danger) {
|
||||||
|
color: #606266 !important;
|
||||||
|
background: #fff !important;
|
||||||
|
border-color: #dcdfe6 !important;
|
||||||
|
}
|
||||||
|
::v-deep .el-button:not(.el-button--primary):not(.el-button--text):not(.el-button--danger):hover {
|
||||||
|
color: #5F7BA0 !important;
|
||||||
|
border-color: #5F7BA0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-button--text { background: transparent !important; border-color: transparent !important; }
|
||||||
|
::v-deep .el-button--text.btn-danger { color: #f56c6c !important; }
|
||||||
|
|
||||||
|
.btn-danger { color: #f56c6c; }
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user