Merge remote-tracking branch 'origin/0.8.X' into 0.8.X
This commit is contained in:
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"java.configuration.updateBuildConfiguration": "interactive"
|
||||||
|
}
|
||||||
@@ -11,7 +11,6 @@ import com.klp.common.core.page.TableDataInfo;
|
|||||||
import com.klp.common.enums.BusinessType;
|
import com.klp.common.enums.BusinessType;
|
||||||
import com.klp.common.utils.poi.ExcelUtil;
|
import com.klp.common.utils.poi.ExcelUtil;
|
||||||
import com.klp.system.service.ISysDictDataService;
|
import com.klp.system.service.ISysDictDataService;
|
||||||
import com.klp.system.service.ISysDictTypeService;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
@@ -32,7 +31,6 @@ import java.util.List;
|
|||||||
public class SysDictDataController extends BaseController {
|
public class SysDictDataController extends BaseController {
|
||||||
|
|
||||||
private final ISysDictDataService dictDataService;
|
private final ISysDictDataService dictDataService;
|
||||||
private final ISysDictTypeService dictTypeService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询字典数据列表
|
* 查询字典数据列表
|
||||||
@@ -72,7 +70,7 @@ public class SysDictDataController extends BaseController {
|
|||||||
*/
|
*/
|
||||||
@GetMapping(value = "/type/{dictType}")
|
@GetMapping(value = "/type/{dictType}")
|
||||||
public R<List<SysDictData>> dictType(@PathVariable("dictType") String dictType) {
|
public R<List<SysDictData>> dictType(@PathVariable("dictType") String dictType) {
|
||||||
List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);
|
List<SysDictData> data = dictDataService.selectDictDataByTypeRealtime(dictType);
|
||||||
if (ObjectUtil.isNull(data)) {
|
if (ObjectUtil.isNull(data)) {
|
||||||
data = new ArrayList<>();
|
data = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,14 @@ public class SysDictTypeController extends BaseController {
|
|||||||
ExcelUtil.exportExcel(list, "字典类型", SysDictType.class, response);
|
ExcelUtil.exportExcel(list, "字典类型", SysDictType.class, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按字典类型编码精确查询(避免 /list 条件对 dict_type 使用 LIKE 时下划线通配问题;供页内齿轮等使用)
|
||||||
|
*/
|
||||||
|
@GetMapping("/byType/{dictType}")
|
||||||
|
public R<SysDictType> getByDictType(@PathVariable String dictType) {
|
||||||
|
return R.ok(dictTypeService.selectDictTypeByType(dictType));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询字典类型详细
|
* 查询字典类型详细
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -41,6 +41,11 @@ public interface ISysDictDataService {
|
|||||||
*/
|
*/
|
||||||
SysDictData selectDictDataById(Long dictCode);
|
SysDictData selectDictDataById(Long dictCode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按字典类型查询字典数据(直查库)。用于 HTTP `/dict/data/type/{type}`,避免 Spring Cache 在无界面操作灌数后仍返回空。
|
||||||
|
*/
|
||||||
|
List<SysDictData> selectDictDataByTypeRealtime(String dictType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量删除字典数据信息
|
* 批量删除字典数据信息
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import org.springframework.cache.annotation.CachePut;
|
import org.springframework.cache.annotation.CachePut;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,6 +82,18 @@ public class SysDictDataServiceImpl implements ISysDictDataService {
|
|||||||
return baseMapper.selectById(dictCode);
|
return baseMapper.selectById(dictCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按类型查字典数据(直查数据库,不走 {@link com.klp.system.service.impl.SysDictTypeServiceImpl#selectDictDataByType} 的 Spring Cache)。
|
||||||
|
* SQL 脚本直插或外部改表后,仍需立即在「按类型」HTTP 接口中可见,避免长期使用空缓存。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<SysDictData> selectDictDataByTypeRealtime(String dictType) {
|
||||||
|
if (StringUtils.isBlank(dictType)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return baseMapper.selectDictDataByType(dictType);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量删除字典数据信息
|
* 批量删除字典数据信息
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
|
|||||||
@Cacheable(cacheNames = CacheNames.SYS_DICT, key = "#dictType")
|
@Cacheable(cacheNames = CacheNames.SYS_DICT, key = "#dictType")
|
||||||
@Override
|
@Override
|
||||||
public SysDictType selectDictTypeByType(String dictType) {
|
public SysDictType selectDictTypeByType(String dictType) {
|
||||||
return baseMapper.selectById(new LambdaQueryWrapper<SysDictType>().eq(SysDictType::getDictType, dictType));
|
return baseMapper.selectOne(new LambdaQueryWrapper<SysDictType>().eq(SysDictType::getDictType, dictType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -58,3 +58,11 @@ export function optionselect() {
|
|||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 按字典类型编码精确查询 sys_dict_type(路径需编码) */
|
||||||
|
export function getDictTypeByCode(dictType) {
|
||||||
|
return request({
|
||||||
|
url: '/system/dict/type/byType/' + encodeURIComponent(dictType),
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<div style="display: flex; align-items: center;" v-loading="loading">
|
<div style="display: flex; align-items: center;" v-loading="loading">
|
||||||
<!-- 下拉选择器:绑定计算属性做双向绑定,保留原有样式+清空功能 -->
|
<!-- 下拉选择器:绑定计算属性做双向绑定,保留原有样式+清空功能 -->
|
||||||
<el-select
|
<el-select
|
||||||
|
v-if="!toolbarOnly"
|
||||||
v-model="innerValue"
|
v-model="innerValue"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
clearable filterable
|
clearable filterable
|
||||||
@@ -20,13 +21,13 @@
|
|||||||
<div
|
<div
|
||||||
v-if="editable"
|
v-if="editable"
|
||||||
@click="openDictDialog"
|
@click="openDictDialog"
|
||||||
style="cursor: pointer; min-height: 24px; min-width: 24px; margin-top: 4px; display: flex; align-items: center; justify-content: center; border: 1px solid #828991; margin-left: 8px; border-radius: 2px;"
|
:style="toolbarOnly ? 'cursor: pointer; min-height: 24px; min-width: 24px; margin-top: 4px; display: flex; align-items: center; justify-content: center; border: 1px solid #828991; border-radius: 2px;' : 'cursor: pointer; min-height: 24px; min-width: 24px; margin-top: 4px; display: flex; align-items: center; justify-content: center; border: 1px solid #828991; margin-left: 8px; border-radius: 2px;'"
|
||||||
>
|
>
|
||||||
<i class="el-icon-setting"></i>
|
<i class="el-icon-setting"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="refresh"
|
v-if="refresh && !toolbarOnly"
|
||||||
@click="handleRefresh"
|
@click="handleRefresh"
|
||||||
style="cursor: pointer; min-height: 24px; min-width: 24px; margin-top: 4px; display: flex; align-items: center; justify-content: center; border: 1px solid #828991; margin-left: 8px; border-radius: 2px;"
|
style="cursor: pointer; min-height: 24px; min-width: 24px; margin-top: 4px; display: flex; align-items: center; justify-content: center; border: 1px solid #828991; margin-left: 8px; border-radius: 2px;"
|
||||||
>
|
>
|
||||||
@@ -37,7 +38,7 @@
|
|||||||
<el-dialog
|
<el-dialog
|
||||||
v-if="editable"
|
v-if="editable"
|
||||||
:visible.sync="open"
|
:visible.sync="open"
|
||||||
title="字典数据配置"
|
:title="panelTitle || '字典数据配置'"
|
||||||
width="600px"
|
width="600px"
|
||||||
append-to-body
|
append-to-body
|
||||||
>
|
>
|
||||||
@@ -145,14 +146,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { addData, updateData, delData, listData } from '@/api/system/dict/data'
|
import { addData, updateData, delData, getDicts } from '@/api/system/dict/data'
|
||||||
import { listType } from '@/api/system/dict/type'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DictSelectEdit',
|
name: 'DictSelectEdit',
|
||||||
props: {
|
props: {
|
||||||
dictType: { type: String, default: '' },
|
dictType: { type: String, default: '' },
|
||||||
editable: { type: Boolean, default: true },
|
editable: { type: Boolean, default: true },
|
||||||
|
/** 仅展示字典配置入口(齿轮)与弹窗,不渲染下拉框 — 用于列表页 Tab 旁内联维护字典 */
|
||||||
|
toolbarOnly: { type: Boolean, default: false },
|
||||||
|
/** 字典配置弹窗标题 */
|
||||||
|
panelTitle: { type: String, default: '' },
|
||||||
kisv: { type: Boolean, default: true },
|
kisv: { type: Boolean, default: true },
|
||||||
value: { type: String, default: '' },
|
value: { type: String, default: '' },
|
||||||
placeholder: { type: String, default: '请选择' },
|
placeholder: { type: String, default: '请选择' },
|
||||||
@@ -163,18 +167,17 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
dictOptions: [],
|
dictOptions: [],
|
||||||
dictId: '',
|
|
||||||
loading: false,
|
loading: false,
|
||||||
btnLoading: false,
|
btnLoading: false,
|
||||||
delBtnLoading: '',
|
delBtnLoading: '',
|
||||||
open: false,
|
open: false,
|
||||||
editRowId: '', // 控制当前编辑行,为空则所有单元格都是文本状态
|
editRowId: '', // 控制当前编辑行,为空则所有单元格都是文本状态
|
||||||
form: {
|
form: {
|
||||||
dictId: '',
|
|
||||||
dictLabel: '',
|
dictLabel: '',
|
||||||
dictValue: '',
|
dictValue: '',
|
||||||
dictType: '',
|
dictType: '',
|
||||||
sort: 0
|
dictSort: 0,
|
||||||
|
status: '0'
|
||||||
},
|
},
|
||||||
dictRules: {
|
dictRules: {
|
||||||
dictLabel: [{ required: true, message: '请输入字典标签', trigger: 'blur' }],
|
dictLabel: [{ required: true, message: '请输入字典标签', trigger: 'blur' }],
|
||||||
@@ -214,16 +217,16 @@ export default {
|
|||||||
watch: {
|
watch: {
|
||||||
dictType: {
|
dictType: {
|
||||||
async handler(newVal) {
|
async handler(newVal) {
|
||||||
if (newVal) {
|
if (!newVal) return
|
||||||
this.loading = true
|
// toolbarOnly:仅打开弹窗时加载,避免无谓请求;下拉模式按 dict_type 加载选项
|
||||||
try {
|
if (this.toolbarOnly) return
|
||||||
const dictId = await this.getDictId(newVal)
|
this.loading = true
|
||||||
await this.getDictOptions(dictId)
|
try {
|
||||||
} catch (err) {
|
await this.loadDictRows()
|
||||||
console.error('加载字典失败:', err)
|
} catch (err) {
|
||||||
} finally {
|
console.error('加载字典失败:', err)
|
||||||
this.loading = false
|
} finally {
|
||||||
}
|
this.loading = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
immediate: true
|
immediate: true
|
||||||
@@ -236,14 +239,11 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async getDictId(type) {
|
/** 读字典数据行:优先 getDicts(/dict/data/type/{type}),无权限点与系统字典页 list 不一致问题 */
|
||||||
const res = await listType({ dictType: type })
|
async loadDictRows() {
|
||||||
if (res.rows?.length !== 1) {
|
const res = await getDicts(this.dictType)
|
||||||
this.$message.error('字典类型异常,未查询到对应配置')
|
this.dictOptions = res.data || []
|
||||||
return Promise.reject('字典类型异常')
|
return this.dictOptions
|
||||||
}
|
|
||||||
this.dictId = res.rows[0].dictId
|
|
||||||
return this.dictId
|
|
||||||
},
|
},
|
||||||
disabledFormat(item) {
|
disabledFormat(item) {
|
||||||
if (this.disables) {
|
if (this.disables) {
|
||||||
@@ -256,27 +256,36 @@ export default {
|
|||||||
async handleRefresh() {
|
async handleRefresh() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
try {
|
try {
|
||||||
await this.getDictOptions(this.dictId)
|
await this.loadDictRows()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('刷新字典失败:', err)
|
console.error('刷新字典失败:', err)
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async getDictOptions(dictId) {
|
async openDictDialog() {
|
||||||
const res = await listData({ dictType: this.dictType, pageSize: 1000 })
|
|
||||||
this.dictOptions = res.rows || []
|
|
||||||
return this.dictOptions
|
|
||||||
},
|
|
||||||
openDictDialog() {
|
|
||||||
this.open = true
|
this.open = true
|
||||||
this.resetDictForm()
|
this.editRowId = ''
|
||||||
this.form.dictId = this.dictId
|
this.loading = true
|
||||||
this.form.dictType = this.dictType
|
try {
|
||||||
this.editRowId = '' // 打开弹窗重置编辑状态
|
await this.loadDictRows()
|
||||||
|
this.resetDictForm()
|
||||||
|
} catch (err) {
|
||||||
|
this.dictOptions = []
|
||||||
|
console.error('打开字典配置失败:', err)
|
||||||
|
this.$message.error('字典数据加载失败,请检查字典类型是否正确或稍后重试')
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
resetDictForm() {
|
resetDictForm() {
|
||||||
this.form = { dictId: this.dictId, dictLabel: '', dictValue: '', dictType: this.dictType, sort: 0 }
|
this.form = {
|
||||||
|
dictLabel: '',
|
||||||
|
dictValue: '',
|
||||||
|
dictType: this.dictType,
|
||||||
|
dictSort: 0,
|
||||||
|
status: '0'
|
||||||
|
}
|
||||||
this.$refs.dictFormRef && this.$refs.dictFormRef.clearValidate()
|
this.$refs.dictFormRef && this.$refs.dictFormRef.clearValidate()
|
||||||
},
|
},
|
||||||
handleKisvTableSync(row) {
|
handleKisvTableSync(row) {
|
||||||
@@ -290,7 +299,8 @@ export default {
|
|||||||
try {
|
try {
|
||||||
await addData(this.form)
|
await addData(this.form)
|
||||||
this.$message.success('字典项新增成功!')
|
this.$message.success('字典项新增成功!')
|
||||||
await this.getDictOptions(this.dictId)
|
await this.loadDictRows()
|
||||||
|
this.$emit('dict-updated')
|
||||||
this.resetDictForm()
|
this.resetDictForm()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.$message.error('新增失败,请稍后重试')
|
this.$message.error('新增失败,请稍后重试')
|
||||||
@@ -309,18 +319,19 @@ export default {
|
|||||||
async handleSaveRow(row) {
|
async handleSaveRow(row) {
|
||||||
if (!row.dictLabel || !row.dictValue) {
|
if (!row.dictLabel || !row.dictValue) {
|
||||||
this.$message.warning('字典标签和字典值不能为空!')
|
this.$message.warning('字典标签和字典值不能为空!')
|
||||||
await this.getDictOptions(this.dictId)
|
await this.loadDictRows()
|
||||||
this.editRowId = '' // 校验失败,也必须还原单元格
|
this.editRowId = '' // 校验失败,也必须还原单元格
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
row.sort = 0
|
if (row.dictSort == null) row.dictSort = 0
|
||||||
this.loading = true
|
this.loading = true
|
||||||
try {
|
try {
|
||||||
await updateData(row)
|
await updateData(row)
|
||||||
this.$message.success('字典项修改成功!')
|
this.$message.success('字典项修改成功!')
|
||||||
|
this.$emit('dict-updated')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.$message.error('修改失败,请稍后重试')
|
this.$message.error('修改失败,请稍后重试')
|
||||||
await this.getDictOptions(this.dictId)
|
await this.loadDictRows()
|
||||||
console.error(err)
|
console.error(err)
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
@@ -339,7 +350,8 @@ export default {
|
|||||||
try {
|
try {
|
||||||
await delData(row.dictCode)
|
await delData(row.dictCode)
|
||||||
this.$message.success('删除成功!')
|
this.$message.success('删除成功!')
|
||||||
await this.getDictOptions(this.dictId)
|
await this.loadDictRows()
|
||||||
|
this.$emit('dict-updated')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.$message.error('删除失败,请稍后重试')
|
this.$message.error('删除失败,请稍后重试')
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ export default {
|
|||||||
// 查询参数
|
// 查询参数
|
||||||
queryParams: {
|
queryParams: {
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 6,
|
pageSize: 10,
|
||||||
planDate: undefined,
|
planDate: undefined,
|
||||||
lineId: undefined,
|
lineId: undefined,
|
||||||
lineName: undefined,
|
lineName: undefined,
|
||||||
|
|||||||
483
klp-ui/src/views/micro/pages/acid/components/Performance.vue
Normal file
483
klp-ui/src/views/micro/pages/acid/components/Performance.vue
Normal file
@@ -0,0 +1,483 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container" style="width: 100%; height: 100%;">
|
||||||
|
<div class="filter-bar">
|
||||||
|
<el-input
|
||||||
|
v-model="queryForm.coilId"
|
||||||
|
placeholder="热卷号 / 成品卷号"
|
||||||
|
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-row :gutter="12" class="main-row">
|
||||||
|
<!-- 左侧计划列表 -->
|
||||||
|
<el-col :span="9">
|
||||||
|
<div class="left-card">
|
||||||
|
<el-table
|
||||||
|
ref="planTable"
|
||||||
|
:data="planRows"
|
||||||
|
size="mini"
|
||||||
|
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)]" />
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<div class="pagination-bar">
|
||||||
|
<el-pagination
|
||||||
|
small
|
||||||
|
layout="total, prev, pager, next"
|
||||||
|
:total="pagination.total"
|
||||||
|
:page-size="pagination.pageSize"
|
||||||
|
:current-page="pagination.page"
|
||||||
|
@current-change="handlePageChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<!-- 右侧详情 + 图表 -->
|
||||||
|
<el-col :span="15">
|
||||||
|
<div v-if="!selectedPlan" class="empty-hint">
|
||||||
|
<el-empty description="选择左侧计划" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<div class="detail-grid">
|
||||||
|
<div v-for="f in planFields" :key="f.key" class="detail-cell">
|
||||||
|
<span class="cell-label">{{ f.label }}</span>
|
||||||
|
<span class="cell-value">{{ selectedPlan[f.key] != null ? selectedPlan[f.key] : '—' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="perf-header">
|
||||||
|
实轧实绩
|
||||||
|
<span v-if="hasPerfData" class="perf-count">({{ perfSegCount }} 段)</span>
|
||||||
|
<el-tag v-if="perfLoading" size="mini" type="info" style="margin-left:8px">加载中…</el-tag>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="hasPerfData" class="charts-wrap">
|
||||||
|
<div ref="chartSpeed" class="chart-box" />
|
||||||
|
<div ref="chartMillSpeed" class="chart-box" />
|
||||||
|
<div ref="chartTension" class="chart-box" />
|
||||||
|
<div ref="chartPorSpeed" class="chart-box" />
|
||||||
|
<div ref="chartPorTens" class="chart-box" />
|
||||||
|
<div ref="chartTrim" class="chart-box" />
|
||||||
|
<div ref="chartTemp" class="chart-box" />
|
||||||
|
<div ref="chartMesh" class="chart-box" />
|
||||||
|
<div ref="chartElong" class="chart-box" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-empty v-else-if="!perfLoading" description="暂无实绩数据" :image-size="56" style="margin-top: 24px" />
|
||||||
|
</template>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
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 {
|
||||||
|
name: 'TimingAcidPage',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
perfLoading: false,
|
||||||
|
queryForm: { coilId: '' },
|
||||||
|
planRows: [],
|
||||||
|
selectedPlan: null,
|
||||||
|
perfSeries: null,
|
||||||
|
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() {
|
||||||
|
// plain instance property — Vue 2 does NOT proxy underscore-prefixed names
|
||||||
|
this.chartInstances = []
|
||||||
|
this.resizeHandler = null
|
||||||
|
this.loadPlanCount()
|
||||||
|
this.loadPlanList()
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.disposeCharts()
|
||||||
|
},
|
||||||
|
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() {
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
const { page, pageSize } = this.pagination
|
||||||
|
const res = await getTimingPlanList(page, pageSize)
|
||||||
|
this.planRows = res?.data?.rows || []
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handlePageChange(page) {
|
||||||
|
this.pagination.page = page
|
||||||
|
this.loadPlanList()
|
||||||
|
},
|
||||||
|
async handleSearch() {
|
||||||
|
if (!this.queryForm.coilId) return this.loadPlanList()
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
const res = await getTimingPlanDetail(this.queryForm.coilId)
|
||||||
|
const row = res?.data?.firstRow || null
|
||||||
|
if (row) {
|
||||||
|
this.selectedPlan = row
|
||||||
|
await this.loadPerf(row)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async handlePlanRowClick(row) {
|
||||||
|
this.selectedPlan = row
|
||||||
|
this.perfSeries = null
|
||||||
|
this.perfSegCount = 0
|
||||||
|
this.disposeCharts()
|
||||||
|
await this.loadPerf(row)
|
||||||
|
},
|
||||||
|
async loadPerf(plan) {
|
||||||
|
const encoilId = plan.encoilid || plan.coilid
|
||||||
|
if (!encoilId) return
|
||||||
|
this.perfLoading = true
|
||||||
|
try {
|
||||||
|
const res = await getTimingSegByEncoilId(encoilId)
|
||||||
|
const keys = Object.keys(res?.data?.series || {})
|
||||||
|
console.log(keys)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
},
|
||||||
|
disposeCharts() {
|
||||||
|
if (this.resizeHandler) {
|
||||||
|
window.removeEventListener('resize', this.resizeHandler)
|
||||||
|
this.resizeHandler = null
|
||||||
|
}
|
||||||
|
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('入口速度 millentryspeed', pick('millentryspeed')),
|
||||||
|
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'
|
||||||
|
))
|
||||||
|
|
||||||
|
const c4 = echarts.init(this.$refs.chartPorSpeed)
|
||||||
|
c4.setOption(baseOption(
|
||||||
|
'开卷机速度 (m/min)', xData,
|
||||||
|
[
|
||||||
|
makeLine('开卷速度 porspeed', pick('porspeed')),
|
||||||
|
makeLine('最大 porspeedmax', pick('porspeedmax')),
|
||||||
|
makeLine('最小 porspeedmin', pick('porspeedmin'))
|
||||||
|
],
|
||||||
|
'm/min'
|
||||||
|
))
|
||||||
|
|
||||||
|
const c5 = echarts.init(this.$refs.chartPorTens)
|
||||||
|
c5.setOption(baseOption(
|
||||||
|
'开卷机张力 (N)', xData,
|
||||||
|
[
|
||||||
|
makeLine('开卷张力 portens', pick('portens')),
|
||||||
|
makeLine('最大 portensmax', pick('portensmax')),
|
||||||
|
makeLine('最小 portensmin', pick('portensmin'))
|
||||||
|
],
|
||||||
|
'N'
|
||||||
|
))
|
||||||
|
|
||||||
|
const c6 = echarts.init(this.$refs.chartTrim)
|
||||||
|
c6.setOption(baseOption(
|
||||||
|
'剪切参数', xData,
|
||||||
|
[
|
||||||
|
makeLine('剪切张力 trimtens', pick('trimtens')),
|
||||||
|
makeLine('剪切宽度 trimwidth', pick('trimwidth')),
|
||||||
|
makeLine('trtens', pick('trtens'))
|
||||||
|
],
|
||||||
|
''
|
||||||
|
))
|
||||||
|
|
||||||
|
const c7 = echarts.init(this.$refs.chartTemp)
|
||||||
|
c7.setOption(baseOption(
|
||||||
|
'温度趋势 (℃)', xData,
|
||||||
|
[
|
||||||
|
makeLine('1号测温 tk1temp', pick('tk1temp')),
|
||||||
|
makeLine('2号测温 tk2temp', pick('tk2temp')),
|
||||||
|
makeLine('3号测温 tk3temp', pick('tk3temp')),
|
||||||
|
makeLine('4号测温 tk4temp', pick('tk4temp')),
|
||||||
|
makeLine('漂洗温度 rinsetemp', pick('rinsetemp'))
|
||||||
|
],
|
||||||
|
'℃'
|
||||||
|
))
|
||||||
|
|
||||||
|
const c8 = echarts.init(this.$refs.chartMesh)
|
||||||
|
c8.setOption(baseOption(
|
||||||
|
'轧辊参数', xData,
|
||||||
|
[
|
||||||
|
makeLine('轧辊1 tlmesh1', pick('tlmesh1')),
|
||||||
|
makeLine('轧辊2 tlmesh2', pick('tlmesh2')),
|
||||||
|
makeLine('轧辊3 tlmesh3', pick('tlmesh3'))
|
||||||
|
],
|
||||||
|
''
|
||||||
|
))
|
||||||
|
|
||||||
|
const c9 = echarts.init(this.$refs.chartElong)
|
||||||
|
c9.setOption(baseOption(
|
||||||
|
'延伸率', xData,
|
||||||
|
[
|
||||||
|
makeLine('延伸率 tlelong', pick('tlelong')),
|
||||||
|
makeLine('总张力 tltens', pick('tltens')),
|
||||||
|
makeLine('teltens', pick('teltens'))
|
||||||
|
],
|
||||||
|
''
|
||||||
|
))
|
||||||
|
|
||||||
|
this.chartInstances = [c1, c2, c3, c4, c5, c6, c7, c8, c9]
|
||||||
|
this.resizeHandler = () => this.chartInstances.forEach(c => { if (c) c.resize() })
|
||||||
|
window.addEventListener('resize', this.resizeHandler)
|
||||||
|
},
|
||||||
|
handleReset() {
|
||||||
|
this.queryForm.coilId = ''
|
||||||
|
this.selectedPlan = null
|
||||||
|
this.perfSeries = null
|
||||||
|
this.perfSegCount = 0
|
||||||
|
this.disposeCharts()
|
||||||
|
this.pagination.page = 1
|
||||||
|
this.loadPlanList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.filter-bar {
|
||||||
|
display: flex;
|
||||||
|
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>
|
||||||
@@ -16,18 +16,31 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="钢卷判级" name="gradeCoil">
|
<el-tab-pane label="次品钢卷" name="defect">
|
||||||
<div class="tab-content" v-if="activeTab === 'gradeCoil'">
|
<div class="tab-content" v-if="activeTab === 'defect'">
|
||||||
<BasePage
|
<BasePage
|
||||||
:qrcode="false"
|
:qrcode="false"
|
||||||
:querys="{
|
:querys="{
|
||||||
dataType: 0,
|
dataType: 1,
|
||||||
status: 1,
|
OnlyScrap: true,
|
||||||
|
}"
|
||||||
|
:hideType="false"
|
||||||
|
:useWarehouseIds="useWarehouseIds"
|
||||||
|
:warehouseOptions="warehouseOptions"
|
||||||
|
:showMaterialType="showMaterialType"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="O卷" name="oil">
|
||||||
|
<div class="tab-content" v-if="activeTab === 'oil'">
|
||||||
|
<BasePage
|
||||||
|
:qrcode="false"
|
||||||
|
:querys="{
|
||||||
|
dataType: 1,
|
||||||
|
status: 0,
|
||||||
|
qualityStatus: 'O'
|
||||||
}"
|
}"
|
||||||
:hideType="false"
|
:hideType="false"
|
||||||
:showControl="false"
|
|
||||||
:showGrade="true"
|
|
||||||
:showStatus="false"
|
|
||||||
:useWarehouseIds="useWarehouseIds"
|
:useWarehouseIds="useWarehouseIds"
|
||||||
:warehouseOptions="warehouseOptions"
|
:warehouseOptions="warehouseOptions"
|
||||||
:showMaterialType="showMaterialType"
|
:showMaterialType="showMaterialType"
|
||||||
|
|||||||
103
klp-ui/src/views/micro/pages/acid/components/RealTime.vue
Normal file
103
klp-ui/src/views/micro/pages/acid/components/RealTime.vue
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
<template>
|
||||||
|
<div class="timing-page realtime-page">
|
||||||
|
<el-card shadow="never" class="page-card">
|
||||||
|
<div slot="header" class="card-header">
|
||||||
|
<span>实时跟踪页</span>
|
||||||
|
<el-tag type="warning" size="mini">Gauge + Shape</el-tag>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-form :inline="true" :model="queryForm" size="mini" class="query-form">
|
||||||
|
<el-form-item label="MATID">
|
||||||
|
<el-input v-model="queryForm.matId" placeholder="输入 MATID" clearable style="width: 240px;" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-refresh" :loading="loading" @click="handleQuery">获取实时数据</el-button>
|
||||||
|
<el-button icon="el-icon-delete" @click="handleReset">清空</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-empty v-if="!realtimeData" description="请输入 MATID 后查询实时数据" />
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-card shadow="never" class="sub-card">
|
||||||
|
<div slot="header">Gauge 厚度数据</div>
|
||||||
|
<el-table :data="gaugeRows" size="mini" height="520" border>
|
||||||
|
<el-table-column prop="XTIME" label="XTIME" width="120" />
|
||||||
|
<el-table-column prop="XLOCATION" label="XLOCATION" width="100" />
|
||||||
|
<el-table-column prop="THICK0" label="THICK0" width="90" />
|
||||||
|
<el-table-column prop="THICK1" label="THICK1" width="90" />
|
||||||
|
<el-table-column prop="THICK4" label="THICK4" width="90" />
|
||||||
|
<el-table-column prop="THICK5" label="THICK5" width="90" />
|
||||||
|
<el-table-column prop="EXIT_SPEED" label="EXIT_SPEED" width="100" />
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-card shadow="never" class="sub-card">
|
||||||
|
<div slot="header">Shape 板形数据</div>
|
||||||
|
<el-table :data="shapeRows" size="mini" height="520" border>
|
||||||
|
<el-table-column prop="XTIME" label="XTIME" width="120" />
|
||||||
|
<el-table-column prop="XLOCATION" label="XLOCATION" width="100" />
|
||||||
|
<el-table-column prop="HIGHZONEID" label="HIGHZONEID" width="100" />
|
||||||
|
<el-table-column prop="LOWZONEID" label="LOWZONEID" width="100" />
|
||||||
|
<el-table-column prop="EXIT_SPEED" label="EXIT_SPEED" width="100" />
|
||||||
|
<el-table-column prop="ROLLFORCE" label="ROLLFORCE" width="100" />
|
||||||
|
<el-table-column prop="ABSDEVIATION" label="ABSDEVIATION" width="110" />
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getTimingRealtimeData } from '@/api/l2/timing'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TimingRealtimePage',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
queryForm: { matId: '' },
|
||||||
|
realtimeData: null,
|
||||||
|
gaugeRows: [],
|
||||||
|
shapeRows: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async handleQuery() {
|
||||||
|
if (!this.queryForm.matId) {
|
||||||
|
this.$message.warning('请输入 MATID')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
const res = await getTimingRealtimeData(this.queryForm.matId)
|
||||||
|
this.realtimeData = res && res.data ? res.data : res
|
||||||
|
this.gaugeRows = this.realtimeData && this.realtimeData.gauge ? this.realtimeData.gauge.result || [] : []
|
||||||
|
this.shapeRows = this.realtimeData && this.realtimeData.shape ? this.realtimeData.shape.result || [] : []
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleReset() {
|
||||||
|
this.queryForm.matId = ''
|
||||||
|
this.realtimeData = null
|
||||||
|
this.gaugeRows = []
|
||||||
|
this.shapeRows = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.timing-page { padding: 16px; }
|
||||||
|
.page-card { border-radius: 12px; }
|
||||||
|
.card-header { display: flex; align-items: center; justify-content: space-between; font-weight: 600; }
|
||||||
|
.query-form { margin-bottom: 12px; }
|
||||||
|
.sub-card { border-radius: 10px; }
|
||||||
|
</style>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item index="processing">
|
<el-menu-item index="processing">
|
||||||
<i class="el-icon-s-operation"></i>
|
<i class="el-icon-s-operation"></i>
|
||||||
<span slot="title">加工</span>
|
<span slot="title">WIP</span>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item index="report">
|
<el-menu-item index="report">
|
||||||
<i class="el-icon-document"></i>
|
<i class="el-icon-document"></i>
|
||||||
@@ -26,9 +26,17 @@
|
|||||||
<i class="el-icon-warning-outline"></i>
|
<i class="el-icon-warning-outline"></i>
|
||||||
<span slot="title">品质</span>
|
<span slot="title">品质</span>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
|
<el-menu-item index="performance">
|
||||||
|
<i class="el-icon-coin"></i>
|
||||||
|
<span slot="title">实绩</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<!-- <el-menu-item index="realTime">
|
||||||
|
<i class="el-icon-time"></i>
|
||||||
|
<span slot="title">实时</span>
|
||||||
|
</el-menu-item> -->
|
||||||
</el-menu>
|
</el-menu>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div style="flex: 1;">
|
||||||
<component :is="currentComponent" />
|
<component :is="currentComponent" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,6 +48,8 @@ import Processing from './components/Processing.vue';
|
|||||||
import Report from './components/Report.vue';
|
import Report from './components/Report.vue';
|
||||||
import Shipping from './components/Shipping.vue';
|
import Shipping from './components/Shipping.vue';
|
||||||
import Quality from './components/Quality.vue';
|
import Quality from './components/Quality.vue';
|
||||||
|
import Performance from './components/Performance.vue';
|
||||||
|
import RealTime from './components/RealTime.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AcidSystem',
|
name: 'AcidSystem',
|
||||||
@@ -49,6 +59,8 @@ export default {
|
|||||||
Report,
|
Report,
|
||||||
Shipping,
|
Shipping,
|
||||||
Quality,
|
Quality,
|
||||||
|
Performance,
|
||||||
|
RealTime
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -63,6 +75,8 @@ export default {
|
|||||||
report: 'Report',
|
report: 'Report',
|
||||||
shipping: 'Shipping',
|
shipping: 'Shipping',
|
||||||
quality: 'Quality',
|
quality: 'Quality',
|
||||||
|
performance: 'Performance',
|
||||||
|
realTime: 'RealTime',
|
||||||
};
|
};
|
||||||
return componentMap[this.activeMenu];
|
return componentMap[this.activeMenu];
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -293,10 +293,6 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.acid-view {
|
|
||||||
padding: 12px 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-bar {
|
.filter-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -62,33 +62,33 @@
|
|||||||
{value: '1988150380649967617', label: '镀锌分条成品'},
|
{value: '1988150380649967617', label: '镀锌分条成品'},
|
||||||
{value: '1988151027466170370', label: '拉矫分条成品'},
|
{value: '1988151027466170370', label: '拉矫分条成品'},
|
||||||
],
|
],
|
||||||
'酸轧修复工序': [
|
// '酸轧修复工序': [
|
||||||
{value: '1988150044862377986', label: '酸连轧原料库'},
|
// {value: '1988150044862377986', label: '酸连轧原料库'},
|
||||||
{value: '1988150099140866050', label: '酸连轧成品库'},
|
// {value: '1988150099140866050', label: '酸连轧成品库'},
|
||||||
{value: '1988150263284953089', label: '镀锌原料库'},
|
// {value: '1988150263284953089', label: '镀锌原料库'},
|
||||||
{value: '1988150545175736322', label: '脱脂原料库'},
|
// {value: '1988150545175736322', label: '脱脂原料库'},
|
||||||
],
|
// ],
|
||||||
'镀锌修复工序': [
|
// '镀锌修复工序': [
|
||||||
{value: '1988150263284953089', label: '镀锌原料库'},
|
// {value: '1988150263284953089', label: '镀锌原料库'},
|
||||||
{value: '1988150323162836993', label: '镀锌成品库'},
|
// {value: '1988150323162836993', label: '镀锌成品库'},
|
||||||
{value: '1988150487185289217', label: '镀锌纵剪分条原料库'},
|
// {value: '1988150487185289217', label: '镀锌纵剪分条原料库'},
|
||||||
],
|
// ],
|
||||||
'脱脂修复工序': [
|
// '脱脂修复工序': [
|
||||||
{value: '1988150545175736322', label: '脱脂原料库'},
|
// {value: '1988150545175736322', label: '脱脂原料库'},
|
||||||
{value: '1988150586938421250', label: '脱脂成品库'},
|
// {value: '1988150586938421250', label: '脱脂成品库'},
|
||||||
],
|
// ],
|
||||||
'拉矫修复工序': [
|
// '拉矫修复工序': [
|
||||||
{value: '1988150854442741762', label: '拉矫原料库'},
|
// {value: '1988150854442741762', label: '拉矫原料库'},
|
||||||
{value: '1988150915591499777', label: '拉矫成品库'},
|
// {value: '1988150915591499777', label: '拉矫成品库'},
|
||||||
],
|
// ],
|
||||||
'双机架修复工序': [
|
// '双机架修复工序': [
|
||||||
{value: '1992873386047643650', label: '双机架原料库'},
|
// {value: '1992873386047643650', label: '双机架原料库'},
|
||||||
{value: '1992873437713080322', label: '双机架成品库'},
|
// {value: '1992873437713080322', label: '双机架成品库'},
|
||||||
],
|
// ],
|
||||||
'镀铬修复工序': [
|
// '镀铬修复工序': [
|
||||||
{value: '1988151076996706306', label: '镀铬原料库'},
|
// {value: '1988151076996706306', label: '镀铬原料库'},
|
||||||
{value: '1988151132361519105', label: '镀铬成品库'},
|
// {value: '1988151132361519105', label: '镀铬成品库'},
|
||||||
],
|
// ],
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -741,6 +741,12 @@
|
|||||||
<div ref="chartSpeed" class="chart-box" />
|
<div ref="chartSpeed" class="chart-box" />
|
||||||
<div ref="chartMillSpeed" class="chart-box" />
|
<div ref="chartMillSpeed" class="chart-box" />
|
||||||
<div ref="chartTension" class="chart-box" />
|
<div ref="chartTension" class="chart-box" />
|
||||||
|
<div ref="chartPorSpeed" class="chart-box" />
|
||||||
|
<div ref="chartPorTens" class="chart-box" />
|
||||||
|
<div ref="chartTrim" class="chart-box" />
|
||||||
|
<div ref="chartTemp" class="chart-box" />
|
||||||
|
<div ref="chartMesh" class="chart-box" />
|
||||||
|
<div ref="chartElong" class="chart-box" />
|
||||||
</div>
|
</div>
|
||||||
<el-empty v-else-if="!perfLoading" description="暂无生产工艺数据" :image-size="56" style="margin-top: 24px" />
|
<el-empty v-else-if="!perfLoading" description="暂无生产工艺数据" :image-size="56" style="margin-top: 24px" />
|
||||||
</div>
|
</div>
|
||||||
@@ -1223,8 +1229,11 @@ export default {
|
|||||||
|
|
||||||
const c2 = echarts.init(this.$refs.chartMillSpeed);
|
const c2 = echarts.init(this.$refs.chartMillSpeed);
|
||||||
c2.setOption(this.baseOption(
|
c2.setOption(this.baseOption(
|
||||||
'轧机出口速度 (m/min)', xData,
|
'轧机速度 (m/min)', xData,
|
||||||
[this.makeLine('millexitspeed', pick('millexitspeed'))],
|
[
|
||||||
|
this.makeLine('入口速度 millentryspeed', pick('millentryspeed')),
|
||||||
|
this.makeLine('出口速度 millexitspeed', pick('millexitspeed'))
|
||||||
|
],
|
||||||
'm/min'
|
'm/min'
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -1239,7 +1248,88 @@ export default {
|
|||||||
'N'
|
'N'
|
||||||
));
|
));
|
||||||
|
|
||||||
this.chartInstances = [c1, c2, c3];
|
let c4, c5, c6, c7, c8, c9;
|
||||||
|
if (this.$refs.chartPorSpeed) {
|
||||||
|
c4 = echarts.init(this.$refs.chartPorSpeed);
|
||||||
|
c4.setOption(this.baseOption(
|
||||||
|
'开卷机速度 (m/min)', xData,
|
||||||
|
[
|
||||||
|
this.makeLine('开卷速度 porspeed', pick('porspeed')),
|
||||||
|
this.makeLine('最大 porspeedmax', pick('porspeedmax')),
|
||||||
|
this.makeLine('最小 porspeedmin', pick('porspeedmin'))
|
||||||
|
],
|
||||||
|
'm/min'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.$refs.chartPorTens) {
|
||||||
|
c5 = echarts.init(this.$refs.chartPorTens);
|
||||||
|
c5.setOption(this.baseOption(
|
||||||
|
'开卷机张力 (N)', xData,
|
||||||
|
[
|
||||||
|
this.makeLine('开卷张力 portens', pick('portens')),
|
||||||
|
this.makeLine('最大 portensmax', pick('portensmax')),
|
||||||
|
this.makeLine('最小 portensmin', pick('portensmin'))
|
||||||
|
],
|
||||||
|
'N'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.$refs.chartTrim) {
|
||||||
|
c6 = echarts.init(this.$refs.chartTrim);
|
||||||
|
c6.setOption(this.baseOption(
|
||||||
|
'剪切参数', xData,
|
||||||
|
[
|
||||||
|
this.makeLine('剪切张力 trimtens', pick('trimtens')),
|
||||||
|
this.makeLine('剪切宽度 trimwidth', pick('trimwidth')),
|
||||||
|
this.makeLine('trtens', pick('trtens'))
|
||||||
|
],
|
||||||
|
''
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.$refs.chartTemp) {
|
||||||
|
c7 = echarts.init(this.$refs.chartTemp);
|
||||||
|
c7.setOption(this.baseOption(
|
||||||
|
'温度趋势 (℃)', xData,
|
||||||
|
[
|
||||||
|
this.makeLine('1号测温 tk1temp', pick('tk1temp')),
|
||||||
|
this.makeLine('2号测温 tk2temp', pick('tk2temp')),
|
||||||
|
this.makeLine('3号测温 tk3temp', pick('tk3temp')),
|
||||||
|
this.makeLine('4号测温 tk4temp', pick('tk4temp')),
|
||||||
|
this.makeLine('漂洗温度 rinsetemp', pick('rinsetemp'))
|
||||||
|
],
|
||||||
|
'℃'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.$refs.chartMesh) {
|
||||||
|
c8 = echarts.init(this.$refs.chartMesh);
|
||||||
|
c8.setOption(this.baseOption(
|
||||||
|
'网纹辊参数', xData,
|
||||||
|
[
|
||||||
|
this.makeLine('网纹辊1 tlmesh1', pick('tlmesh1')),
|
||||||
|
this.makeLine('网纹辊2 tlmesh2', pick('tlmesh2')),
|
||||||
|
this.makeLine('网纹辊3 tlmesh3', pick('tlmesh3'))
|
||||||
|
],
|
||||||
|
''
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.$refs.chartElong) {
|
||||||
|
c9 = echarts.init(this.$refs.chartElong);
|
||||||
|
c9.setOption(this.baseOption(
|
||||||
|
'延伸率', xData,
|
||||||
|
[
|
||||||
|
this.makeLine('延伸率 tlelong', pick('tlelong')),
|
||||||
|
this.makeLine('总张力 tltens', pick('tltens')),
|
||||||
|
this.makeLine('teltens', pick('teltens'))
|
||||||
|
],
|
||||||
|
''
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.chartInstances = [c1, c2, c3, c4, c5, c6, c7, c8, c9].filter(c => c);
|
||||||
this.resizeHandler = () => this.chartInstances.forEach(c => { if (c) c.resize(); });
|
this.resizeHandler = () => this.chartInstances.forEach(c => { if (c) c.resize(); });
|
||||||
window.addEventListener('resize', this.resizeHandler);
|
window.addEventListener('resize', this.resizeHandler);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -27,6 +27,12 @@
|
|||||||
<el-col :span="16" class="detail-value">{{ step.time }}</el-col>
|
<el-col :span="16" class="detail-value">{{ step.time }}</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 新增步骤专属显示卷号 -->
|
||||||
|
<el-row class="detail-row" v-if="step.action === '创建'">
|
||||||
|
<el-col :span="8" class="detail-label">卷号:</el-col>
|
||||||
|
<el-col :span="16" class="detail-value">{{ step.original.current_coil_no }}</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
<!-- 旧钢卷关键信息(操作前)- 新增优化:展示核心字段,hover弹窗更多信息 -->
|
<!-- 旧钢卷关键信息(操作前)- 新增优化:展示核心字段,hover弹窗更多信息 -->
|
||||||
<el-row class="detail-row" v-if="step.oldCoilInfoList && step.oldCoilInfoList.length">
|
<el-row class="detail-row" v-if="step.oldCoilInfoList && step.oldCoilInfoList.length">
|
||||||
<el-col :span="8" class="detail-label">操作前钢卷:</el-col>
|
<el-col :span="8" class="detail-label">操作前钢卷:</el-col>
|
||||||
|
|||||||
@@ -1,27 +1,49 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="spec-page">
|
<div class="spec-page">
|
||||||
<!-- 规程类型 tabs -->
|
<!-- 规程类型 -->
|
||||||
<div class="type-tab-bar">
|
<div class="dict-toolbar-row">
|
||||||
<span
|
<div class="type-tab-bar">
|
||||||
v-for="t in specTypeTab"
|
<span
|
||||||
:key="t.value"
|
v-for="t in specTypeTab"
|
||||||
:class="['type-tab', { active: activeSpecType === t.value }]"
|
:key="'stype-' + (t.value === '' ? 'all' : t.value)"
|
||||||
@click="switchSpecType(t.value)"
|
:class="['type-tab', { active: activeSpecType === t.value }]"
|
||||||
>{{ t.label }}</span>
|
@click="switchSpecType(t.value)"
|
||||||
|
>{{ t.label }}</span>
|
||||||
|
</div>
|
||||||
|
<dict-select
|
||||||
|
toolbar-only
|
||||||
|
:kisv="false"
|
||||||
|
:editable="true"
|
||||||
|
:refresh="false"
|
||||||
|
:dict-type="DICT_SPEC_TYPE"
|
||||||
|
panel-title="规程工艺类型字典"
|
||||||
|
@dict-updated="loadSpecTypeDict"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 产线 tabs -->
|
<!-- 产线 -->
|
||||||
<div class="line-tab-bar">
|
<div class="dict-toolbar-row line-row">
|
||||||
<span
|
<div class="line-tab-bar">
|
||||||
:class="['line-tab', { active: activeLineId === '' }]"
|
<span
|
||||||
@click="switchLine('')"
|
:class="['line-tab', { active: activeLineId === '' }]"
|
||||||
>全部</span>
|
@click="switchLine('')"
|
||||||
<span
|
>全部</span>
|
||||||
v-for="line in lineOptions"
|
<span
|
||||||
:key="line.lineId"
|
v-for="line in lineOptions"
|
||||||
:class="['line-tab', { active: activeLineId === line.lineId }]"
|
:key="'ln-' + line.lineId"
|
||||||
@click="switchLine(line.lineId)"
|
:class="['line-tab', { active: lineTabActive(line) }]"
|
||||||
>{{ line.lineName }}</span>
|
@click="switchLine(line.lineId)"
|
||||||
|
>{{ line.lineName }}</span>
|
||||||
|
</div>
|
||||||
|
<dict-select
|
||||||
|
toolbar-only
|
||||||
|
:kisv="false"
|
||||||
|
:editable="true"
|
||||||
|
:refresh="false"
|
||||||
|
:dict-type="DICT_LINE"
|
||||||
|
panel-title="规程产线筛选项(字典值为产线 line_id)"
|
||||||
|
@dict-updated="loadLineOptions"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 工具栏 -->
|
<!-- 工具栏 -->
|
||||||
@@ -95,15 +117,20 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="规程类型" prop="specType">
|
<el-form-item label="规程类型" prop="specType">
|
||||||
<el-select v-model="form.specType" style="width:100%">
|
<el-select v-model="form.specType" style="width:100%">
|
||||||
<el-option v-for="t in specTypeOptions" :key="t.value" :label="t.label" :value="t.value" />
|
<el-option
|
||||||
|
v-for="t in specTypeOptionsForForm"
|
||||||
|
:key="t.dictValue"
|
||||||
|
:label="t.dictLabel"
|
||||||
|
:value="t.dictValue"
|
||||||
|
/>
|
||||||
</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" filterable placeholder="请选择" 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 lineOptionsForForm"
|
||||||
:key="line.lineId"
|
:key="line.lineId"
|
||||||
:label="line.lineCode ? line.lineName + '(' + line.lineCode + ')' : line.lineName"
|
:label="lineOptLabel(line)"
|
||||||
:value="line.lineId"
|
:value="line.lineId"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
@@ -127,18 +154,22 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { getDicts } from '@/api/system/dict/data'
|
||||||
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 = [
|
const DICT_SPEC_TYPE = 'wms_process_spec_type'
|
||||||
{ label: '工艺规程', value: 'PROCESS' },
|
const DICT_LINE = 'wms_process_spec_line'
|
||||||
{ label: '标准', value: 'STANDARD' }
|
|
||||||
|
const DEFAULT_SPEC_TYPES = [
|
||||||
]
|
]
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProcessSpec',
|
name: 'ProcessSpec',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
DICT_SPEC_TYPE,
|
||||||
|
DICT_LINE,
|
||||||
loading: false,
|
loading: false,
|
||||||
btnLoading: false,
|
btnLoading: false,
|
||||||
total: 0,
|
total: 0,
|
||||||
@@ -148,9 +179,8 @@ export default {
|
|||||||
multiple: true,
|
multiple: true,
|
||||||
open: false,
|
open: false,
|
||||||
dialogTitle: '',
|
dialogTitle: '',
|
||||||
|
specTypeRows: [],
|
||||||
lineOptions: [],
|
lineOptions: [],
|
||||||
specTypeTab: [{ label: '全部', value: '' }, ...SPEC_TYPES],
|
|
||||||
specTypeOptions: SPEC_TYPES,
|
|
||||||
activeSpecType: '',
|
activeSpecType: '',
|
||||||
activeLineId: '',
|
activeLineId: '',
|
||||||
queryParams: {
|
queryParams: {
|
||||||
@@ -169,13 +199,159 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
specTypeTab() {
|
||||||
|
const rows = this.mergeSpecTypeRowsWithDefaults()
|
||||||
|
const sorted = [...rows].sort((a, b) => (Number(a.dictSort) || 0) - (Number(b.dictSort) || 0))
|
||||||
|
return [{ label: '全部', value: '' }, ...sorted.map(r => ({ label: r.dictLabel, value: r.dictValue }))]
|
||||||
|
},
|
||||||
|
specTypeOptionsForForm() {
|
||||||
|
const rows = this.mergeSpecTypeRowsWithDefaults()
|
||||||
|
return [...rows].sort((a, b) => (Number(a.dictSort) || 0) - (Number(b.dictSort) || 0))
|
||||||
|
},
|
||||||
|
lineOptionsForForm() {
|
||||||
|
return this.lineOptions
|
||||||
|
}
|
||||||
|
},
|
||||||
created() {
|
created() {
|
||||||
listProductionLine({ pageNum: 1, pageSize: 500 }).then(res => {
|
Promise.all([this.loadSpecTypeDict(), this.loadLineOptions()]).finally(() => {
|
||||||
this.lineOptions = res.rows || []
|
this.getList()
|
||||||
})
|
})
|
||||||
this.getList()
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/**
|
||||||
|
* 工艺类型:默认 PROCESS/STANDARD 与字典合并。若仅有字典中存在的新项,仍可保留两行基础兜底;同 dict_value 以字典为准(可改名、调 sort)。
|
||||||
|
*/
|
||||||
|
mergeSpecTypeRowsWithDefaults() {
|
||||||
|
const byVal = new Map()
|
||||||
|
DEFAULT_SPEC_TYPES.forEach(row => {
|
||||||
|
byVal.set(row.dictValue, { ...row })
|
||||||
|
})
|
||||||
|
for (const row of this.specTypeRows || []) {
|
||||||
|
if (!row || row.dictValue === undefined || row.dictValue === null || row.dictValue === '') continue
|
||||||
|
const v = String(row.dictValue)
|
||||||
|
const prev = byVal.get(v)
|
||||||
|
const sort = row.dictSort != null && row.dictSort !== ''
|
||||||
|
? Number(row.dictSort)
|
||||||
|
: (prev && prev.dictSort != null ? prev.dictSort : 999)
|
||||||
|
const label = (row.dictLabel != null && String(row.dictLabel).trim() !== '')
|
||||||
|
? row.dictLabel
|
||||||
|
: (prev && prev.dictLabel)
|
||||||
|
byVal.set(v, {
|
||||||
|
dictLabel: label,
|
||||||
|
dictValue: v,
|
||||||
|
dictSort: sort
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return Array.from(byVal.values())
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 产线 ID 与字典值:用数字字符串,避免超过 Number.MAX_SAFE_INTEGER 时精度丢失(雪花 id)。
|
||||||
|
*/
|
||||||
|
normalizeLineIdString(value) {
|
||||||
|
if (value === undefined || value === null || value === '') return ''
|
||||||
|
const s = String(value).trim()
|
||||||
|
return /^\d+$/.test(s) ? s : ''
|
||||||
|
},
|
||||||
|
/** Tab 高亮:雪花 id 一律按字符串比较 */
|
||||||
|
lineTabActive(line) {
|
||||||
|
if (this.activeLineId === '' || this.activeLineId === undefined) return false
|
||||||
|
return String(this.activeLineId) === String(line.lineId)
|
||||||
|
},
|
||||||
|
parseDictRows(res) {
|
||||||
|
const rows = (res.data || []).filter(d => d.status === '0' || d.status === undefined)
|
||||||
|
rows.sort((a, b) => (Number(a.dictSort) || 0) - (Number(b.dictSort) || 0))
|
||||||
|
return rows
|
||||||
|
},
|
||||||
|
async loadSpecTypeDict() {
|
||||||
|
try {
|
||||||
|
const res = await getDicts(DICT_SPEC_TYPE)
|
||||||
|
this.specTypeRows = this.parseDictRows(res)
|
||||||
|
} catch (err) {
|
||||||
|
console.error('规程工艺类型字典加载失败', err)
|
||||||
|
this.specTypeRows = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 产线 Tab:产线主表 ∪ 字典中合法数字 line_id(字典独有也会显示 Tab)。
|
||||||
|
* line_id 全程用数字字符串,避免超过 Number.MAX_SAFE_INTEGER 时精度丢失;字典值须为数字 line_id。
|
||||||
|
*/
|
||||||
|
async loadLineOptions() {
|
||||||
|
const previousOptions = Array.isArray(this.lineOptions) && this.lineOptions.length
|
||||||
|
? this.lineOptions.map(o => ({ ...o }))
|
||||||
|
: []
|
||||||
|
|
||||||
|
let dictRows = []
|
||||||
|
try {
|
||||||
|
const res = await getDicts(DICT_LINE)
|
||||||
|
dictRows = this.parseDictRows(res)
|
||||||
|
} catch (err) {
|
||||||
|
console.error('规程产线字典加载失败', err)
|
||||||
|
}
|
||||||
|
|
||||||
|
const dictMeta = new Map()
|
||||||
|
for (const d of dictRows) {
|
||||||
|
const idStr = this.normalizeLineIdString(d.dictValue)
|
||||||
|
if (!idStr) continue
|
||||||
|
const sort = Number(d.dictSort) || 0
|
||||||
|
const label = (d.dictLabel != null && String(d.dictLabel).trim() !== '')
|
||||||
|
? String(d.dictLabel).trim()
|
||||||
|
: idStr
|
||||||
|
dictMeta.set(idStr, { label, sort })
|
||||||
|
}
|
||||||
|
|
||||||
|
let tableLines = []
|
||||||
|
// try {
|
||||||
|
// const res = await listProductionLine({ pageNum: 1, pageSize: 500 })
|
||||||
|
// tableLines = (res.rows || []).map(p => {
|
||||||
|
// const idStr = this.normalizeLineIdString(p.lineId) || (p.lineId != null ? String(p.lineId).trim() : '')
|
||||||
|
// return {
|
||||||
|
// lineId: idStr,
|
||||||
|
// lineName: p.lineName,
|
||||||
|
// lineCode: p.lineCode
|
||||||
|
// }
|
||||||
|
// }).filter(p => p.lineId)
|
||||||
|
// } catch (e2) {
|
||||||
|
// console.error('产线列表加载失败', e2)
|
||||||
|
// }
|
||||||
|
|
||||||
|
const tableIdSet = new Set(tableLines.map(l => String(l.lineId)))
|
||||||
|
const next = []
|
||||||
|
|
||||||
|
for (const line of tableLines) {
|
||||||
|
const idStr = String(line.lineId)
|
||||||
|
const dm = dictMeta.get(idStr)
|
||||||
|
next.push({
|
||||||
|
lineId: idStr,
|
||||||
|
lineName: dm ? dm.label : line.lineName,
|
||||||
|
lineCode: line.lineCode
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const dictOnly = []
|
||||||
|
for (const [idStr, meta] of dictMeta) {
|
||||||
|
if (!tableIdSet.has(idStr)) {
|
||||||
|
dictOnly.push({ lineId: idStr, lineName: meta.label, lineCode: undefined, _sort: meta.sort })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dictOnly.sort((a, b) => a._sort - b._sort)
|
||||||
|
dictOnly.forEach(d => {
|
||||||
|
const { _sort, ...rest } = d
|
||||||
|
next.push(rest)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (next.length === 0 && previousOptions.length > 0) {
|
||||||
|
console.warn('[规程产线] 本次未解析出有效筛选项(可能字典值非数字或产线接口异常),保留上一版 Tab')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.lineOptions = next
|
||||||
|
},
|
||||||
|
lineOptLabel(line) {
|
||||||
|
if (line.lineCode) {
|
||||||
|
return `${line.lineName}(${line.lineCode})`
|
||||||
|
}
|
||||||
|
return line.lineName
|
||||||
|
},
|
||||||
getList() {
|
getList() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
listProcessSpec(this.queryParams).then(res => {
|
listProcessSpec(this.queryParams).then(res => {
|
||||||
@@ -190,8 +366,13 @@ export default {
|
|||||||
this.getList()
|
this.getList()
|
||||||
},
|
},
|
||||||
switchLine(lineId) {
|
switchLine(lineId) {
|
||||||
this.activeLineId = lineId
|
if (lineId === '' || lineId === undefined || lineId === null) {
|
||||||
this.queryParams.lineId = lineId || undefined
|
this.activeLineId = ''
|
||||||
|
} else {
|
||||||
|
const s = this.normalizeLineIdString(lineId)
|
||||||
|
this.activeLineId = s || String(lineId).trim()
|
||||||
|
}
|
||||||
|
this.queryParams.lineId = this.activeLineId === '' ? undefined : this.activeLineId
|
||||||
this.queryParams.pageNum = 1
|
this.queryParams.pageNum = 1
|
||||||
this.getList()
|
this.getList()
|
||||||
},
|
},
|
||||||
@@ -208,8 +389,21 @@ export default {
|
|||||||
this.single = sel.length !== 1
|
this.single = sel.length !== 1
|
||||||
this.multiple = !sel.length
|
this.multiple = !sel.length
|
||||||
},
|
},
|
||||||
|
defaultSpecType() {
|
||||||
|
const first = this.specTypeOptionsForForm[0]
|
||||||
|
return first ? first.dictValue : 'PROCESS'
|
||||||
|
},
|
||||||
reset() {
|
reset() {
|
||||||
this.form = { specId: undefined, specCode: undefined, specName: undefined, specType: 'PROCESS', lineId: undefined, productType: undefined, isEnabled: 1, remark: undefined }
|
this.form = {
|
||||||
|
specId: undefined,
|
||||||
|
specCode: undefined,
|
||||||
|
specName: undefined,
|
||||||
|
specType: this.defaultSpecType(),
|
||||||
|
lineId: undefined,
|
||||||
|
productType: undefined,
|
||||||
|
isEnabled: 1,
|
||||||
|
remark: undefined
|
||||||
|
}
|
||||||
this.$refs.form && this.$refs.form.clearValidate()
|
this.$refs.form && this.$refs.form.clearValidate()
|
||||||
},
|
},
|
||||||
handleAdd() {
|
handleAdd() {
|
||||||
@@ -222,6 +416,10 @@ export default {
|
|||||||
const specId = row ? row.specId : this.ids[0]
|
const specId = row ? row.specId : this.ids[0]
|
||||||
getProcessSpec(specId).then(res => {
|
getProcessSpec(specId).then(res => {
|
||||||
this.form = res.data || {}
|
this.form = res.data || {}
|
||||||
|
if (this.form.lineId != null && this.form.lineId !== '') {
|
||||||
|
const sid = this.normalizeLineIdString(this.form.lineId)
|
||||||
|
this.form.lineId = sid || String(this.form.lineId).trim()
|
||||||
|
}
|
||||||
this.dialogTitle = '修改规程'
|
this.dialogTitle = '修改规程'
|
||||||
this.open = true
|
this.open = true
|
||||||
})
|
})
|
||||||
@@ -273,11 +471,23 @@ export default {
|
|||||||
|
|
||||||
/* ── 双色主题:默认=白底灰边,激活/主操作=深藏青 #5F7BA0 ── */
|
/* ── 双色主题:默认=白底灰边,激活/主操作=深藏青 #5F7BA0 ── */
|
||||||
|
|
||||||
|
.dict-toolbar-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px 10px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dict-toolbar-row.line-row {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.type-tab-bar {
|
.type-tab-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex: 0 1 auto;
|
||||||
|
flex-wrap: wrap;
|
||||||
gap: 0;
|
gap: 0;
|
||||||
margin-bottom: 10px;
|
|
||||||
width: fit-content;
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid #dcdfe6;
|
border: 1px solid #dcdfe6;
|
||||||
@@ -307,9 +517,11 @@ export default {
|
|||||||
.line-tab-bar {
|
.line-tab-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
flex: 0 1 auto;
|
||||||
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
margin-bottom: 12px;
|
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.line-tab {
|
.line-tab {
|
||||||
@@ -381,4 +593,9 @@ export default {
|
|||||||
::v-deep .el-button--text.btn-danger { color: #f56c6c !important; }
|
::v-deep .el-button--text.btn-danger { color: #f56c6c !important; }
|
||||||
|
|
||||||
.btn-danger { color: #f56c6c; }
|
.btn-danger { color: #f56c6c; }
|
||||||
|
|
||||||
|
/* 与 Tab 同一行时,将齿轮框配色贴近规程主题 */
|
||||||
|
::v-deep .dict-toolbar-row .el-icon-setting {
|
||||||
|
color: #5F7BA0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -23,14 +23,14 @@
|
|||||||
<!-- 左侧段分组 -->
|
<!-- 左侧段分组 -->
|
||||||
<div class="left-tree">
|
<div class="left-tree">
|
||||||
<div
|
<div
|
||||||
:class="['tree-item', { active: activeSegment === '' }]"
|
:class="['tree-item', { active: activeSegmentType === '' }]"
|
||||||
@click="activeSegment = ''"
|
@click="selectSegmentType('')"
|
||||||
>全部</div>
|
>全部</div>
|
||||||
<div
|
<div
|
||||||
v-for="seg in segmentOptions"
|
v-for="seg in segmentTypeTabOptions"
|
||||||
:key="seg.value"
|
:key="String(seg.value)"
|
||||||
:class="['tree-item', { active: activeSegment === seg.value }]"
|
:class="['tree-item', { active: String(activeSegmentType) === String(seg.value) }]"
|
||||||
@click="activeSegment = seg.value"
|
@click="selectSegmentType(seg.value)"
|
||||||
>
|
>
|
||||||
<span class="tree-arrow">›</span> {{ seg.label }}
|
<span class="tree-arrow">›</span> {{ seg.label }}
|
||||||
</div>
|
</div>
|
||||||
@@ -40,6 +40,24 @@
|
|||||||
<div class="right-content">
|
<div class="right-content">
|
||||||
<!-- 搜索栏 -->
|
<!-- 搜索栏 -->
|
||||||
<div class="search-bar">
|
<div class="search-bar">
|
||||||
|
<template v-if="activeSegmentType !== '' && activeSegmentType !== undefined && activeSegmentType !== null">
|
||||||
|
<span class="search-label">段名称:</span>
|
||||||
|
<el-select
|
||||||
|
v-model="activeSegmentName"
|
||||||
|
placeholder="请选择"
|
||||||
|
size="small"
|
||||||
|
clearable
|
||||||
|
style="width:180px"
|
||||||
|
@change="onSegmentNameFilterChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="opt in segmentNameFilterOptions"
|
||||||
|
:key="String(opt.value) + '-' + opt.label"
|
||||||
|
:label="opt.label"
|
||||||
|
:value="opt.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
<span class="search-label">点位名称:</span>
|
<span class="search-label">点位名称:</span>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="filterName"
|
v-model="filterName"
|
||||||
@@ -116,7 +134,7 @@
|
|||||||
<el-form ref="planFormRef" :model="planForm" :rules="planRules" label-width="90px" size="small">
|
<el-form ref="planFormRef" :model="planForm" :rules="planRules" label-width="90px" size="small">
|
||||||
<el-form-item label="段类型" prop="segmentType">
|
<el-form-item label="段类型" prop="segmentType">
|
||||||
<el-select v-model="planForm.segmentType" style="width:100%">
|
<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-option v-for="s in segmentFormOptionsForDialog" :key="String(s.value)" :label="s.label" :value="s.value" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="段名称" prop="segmentName">
|
<el-form-item label="段名称" prop="segmentName">
|
||||||
@@ -193,7 +211,8 @@
|
|||||||
import { listProcessPlan, addProcessPlan, updateProcessPlan, delProcessPlan } from '@/api/wms/processPlan'
|
import { listProcessPlan, addProcessPlan, updateProcessPlan, delProcessPlan } from '@/api/wms/processPlan'
|
||||||
import { listProcessPlanParam, addProcessPlanParam, updateProcessPlanParam, delProcessPlanParam } from '@/api/wms/processPlanParam'
|
import { listProcessPlanParam, addProcessPlanParam, updateProcessPlanParam, delProcessPlanParam } from '@/api/wms/processPlanParam'
|
||||||
|
|
||||||
const SEGMENTS = [
|
/** 表单内可选段类型(新建/编辑仍支持全部枚举) */
|
||||||
|
const SEGMENT_FORM_OPTIONS = [
|
||||||
{ label: '入口段', value: 'INLET' },
|
{ label: '入口段', value: 'INLET' },
|
||||||
{ label: '工艺段', value: 'PROCESS' },
|
{ label: '工艺段', value: 'PROCESS' },
|
||||||
{ label: '出口段', value: 'OUTLET' }
|
{ label: '出口段', value: 'OUTLET' }
|
||||||
@@ -208,10 +227,12 @@ export default {
|
|||||||
versionCode: '',
|
versionCode: '',
|
||||||
specId: undefined,
|
specId: undefined,
|
||||||
configMode: 'configurable',
|
configMode: 'configurable',
|
||||||
activeSegment: '',
|
/** 左侧:段类型;空=全部 */
|
||||||
|
activeSegmentType: '',
|
||||||
|
/** 工具栏下拉:当前段类型下的段名称;空字符串=该段类型下全部名称;__EMPTY__=未命名 */
|
||||||
|
activeSegmentName: '',
|
||||||
filterName: '',
|
filterName: '',
|
||||||
appliedFilterName: '',
|
appliedFilterName: '',
|
||||||
segmentOptions: SEGMENTS,
|
|
||||||
planList: [],
|
planList: [],
|
||||||
planLoading: false,
|
planLoading: false,
|
||||||
selectedPlan: null,
|
selectedPlan: null,
|
||||||
@@ -237,16 +258,125 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
/**
|
||||||
|
* 左侧 Tab:仅从点位数据汇总「段类型」,展示用语义名称(入口段/工艺段/出口段)
|
||||||
|
*/
|
||||||
|
segmentTypeTabOptions() {
|
||||||
|
const orderIdx = {}
|
||||||
|
SEGMENT_FORM_OPTIONS.forEach((s, i) => { orderIdx[String(s.value)] = i })
|
||||||
|
const seen = new Set()
|
||||||
|
const list = []
|
||||||
|
for (const plan of this.planList) {
|
||||||
|
const t = plan.segmentType
|
||||||
|
if (t === undefined || t === null || String(t).trim() === '') continue
|
||||||
|
const key = String(t)
|
||||||
|
if (seen.has(key)) continue
|
||||||
|
seen.add(key)
|
||||||
|
list.push({
|
||||||
|
value: plan.segmentType,
|
||||||
|
label: this.segmentTypeDisplayLabel(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
list.sort((a, b) => {
|
||||||
|
const ia = orderIdx[String(a.value)]
|
||||||
|
const ib = orderIdx[String(b.value)]
|
||||||
|
const aKnown = ia !== undefined
|
||||||
|
const bKnown = ib !== undefined
|
||||||
|
if (aKnown && bKnown) return ia - ib
|
||||||
|
if (aKnown) return -1
|
||||||
|
if (bKnown) return 1
|
||||||
|
return String(a.label).localeCompare(String(b.label), 'zh-CN')
|
||||||
|
})
|
||||||
|
return list
|
||||||
|
},
|
||||||
|
/** 当前选中段类型下,出现于数据中的段名称下拉项 */
|
||||||
|
segmentNameFilterOptions() {
|
||||||
|
const t = this.activeSegmentType
|
||||||
|
if (t === '' || t === undefined || t === null) return [{ value: '', label: '全部' }]
|
||||||
|
const names = new Set()
|
||||||
|
let hasEmpty = false
|
||||||
|
for (const p of this.planList) {
|
||||||
|
if (String(p.segmentType) !== String(t)) continue
|
||||||
|
const sn = p.segmentName != null ? String(p.segmentName).trim() : ''
|
||||||
|
if (!sn) hasEmpty = true
|
||||||
|
else names.add(sn)
|
||||||
|
}
|
||||||
|
const sorted = [...names].sort((a, b) => a.localeCompare(b, 'zh-CN'))
|
||||||
|
const opts = [{ value: '', label: '全部' }]
|
||||||
|
if (hasEmpty) opts.push({ value: '__EMPTY__', label: '(未命名)' })
|
||||||
|
sorted.forEach(n => opts.push({ value: n, label: n }))
|
||||||
|
return opts
|
||||||
|
},
|
||||||
|
/** 新建/编辑下拉:标准三项 + 当前方案中已出现的其它段类型 */
|
||||||
|
segmentFormOptionsForDialog() {
|
||||||
|
const seen = new Set(SEGMENT_FORM_OPTIONS.map(s => String(s.value)))
|
||||||
|
const extra = []
|
||||||
|
for (const plan of this.planList) {
|
||||||
|
const v = plan.segmentType
|
||||||
|
if (v === undefined || v === null || String(v).trim() === '') continue
|
||||||
|
const key = String(v)
|
||||||
|
if (seen.has(key)) continue
|
||||||
|
seen.add(key)
|
||||||
|
const name = plan.segmentName != null ? String(plan.segmentName).trim() : ''
|
||||||
|
extra.push({
|
||||||
|
value: plan.segmentType,
|
||||||
|
label: name || this.segmentTypeDisplayLabel(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
extra.sort((a, b) => String(a.label).localeCompare(String(b.label), 'zh-CN'))
|
||||||
|
return [...SEGMENT_FORM_OPTIONS, ...extra]
|
||||||
|
},
|
||||||
filteredPlans() {
|
filteredPlans() {
|
||||||
|
const type = this.activeSegmentType
|
||||||
|
const sub = this.activeSegmentName
|
||||||
return this.planList.filter(p => {
|
return this.planList.filter(p => {
|
||||||
const segOk = !this.activeSegment || p.segmentType === this.activeSegment
|
const typeOk =
|
||||||
|
type === '' || type === undefined || type === null
|
||||||
|
? true
|
||||||
|
: String(p.segmentType) === String(type)
|
||||||
|
let nameOkSeg = true
|
||||||
|
if (typeOk && type !== '' && type !== undefined && type !== null) {
|
||||||
|
if (sub === '' || sub === undefined || sub === null) {
|
||||||
|
nameOkSeg = true
|
||||||
|
} else if (sub === '__EMPTY__') {
|
||||||
|
const sn = p.segmentName != null ? String(p.segmentName).trim() : ''
|
||||||
|
nameOkSeg = !sn
|
||||||
|
} else {
|
||||||
|
const sn = p.segmentName != null ? String(p.segmentName).trim() : ''
|
||||||
|
nameOkSeg = sn === String(sub).trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
const nameOk = !this.appliedFilterName || (p.pointName || '').includes(this.appliedFilterName)
|
const nameOk = !this.appliedFilterName || (p.pointName || '').includes(this.appliedFilterName)
|
||||||
return segOk && nameOk
|
return typeOk && nameOkSeg && nameOk
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
$route: { immediate: true, handler() { this.syncFromRoute() } }
|
$route: { immediate: true, handler() { this.syncFromRoute() } },
|
||||||
|
segmentTypeTabOptions: {
|
||||||
|
handler() {
|
||||||
|
const a = this.activeSegmentType
|
||||||
|
if (a === '' || a === undefined || a === null) return
|
||||||
|
const still = this.segmentTypeTabOptions.some(s => String(s.value) === String(a))
|
||||||
|
if (!still) {
|
||||||
|
this.activeSegmentType = ''
|
||||||
|
this.activeSegmentName = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
planList: {
|
||||||
|
handler() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.activeSegmentType === '' || this.activeSegmentType === undefined || this.activeSegmentType === null) return
|
||||||
|
const allowed = this.segmentNameFilterOptions.map(o => o.value)
|
||||||
|
const cur = this.activeSegmentName
|
||||||
|
if (cur !== '' && cur !== undefined && cur !== null && !allowed.includes(cur)) {
|
||||||
|
this.activeSegmentName = ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
syncFromRoute() {
|
syncFromRoute() {
|
||||||
@@ -257,9 +387,22 @@ export default {
|
|||||||
if (this.versionId) this.loadPlans()
|
if (this.versionId) this.loadPlans()
|
||||||
},
|
},
|
||||||
goBack() { this.$router.go(-1) },
|
goBack() { this.$router.go(-1) },
|
||||||
|
selectSegmentType(val) {
|
||||||
|
this.activeSegmentType = val === undefined || val === null ? '' : val
|
||||||
|
this.activeSegmentName = ''
|
||||||
|
},
|
||||||
|
onSegmentNameFilterChange() {
|
||||||
|
const allowed = this.segmentNameFilterOptions.map(o => o.value)
|
||||||
|
if (!allowed.includes(this.activeSegmentName)) {
|
||||||
|
this.activeSegmentName = ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
segmentTypeDisplayLabel(segmentType) {
|
||||||
|
const hit = SEGMENT_FORM_OPTIONS.find(s => String(s.value) === String(segmentType))
|
||||||
|
return hit ? hit.label : (segmentType != null ? String(segmentType) : '')
|
||||||
|
},
|
||||||
segLabel(val) {
|
segLabel(val) {
|
||||||
const hit = SEGMENTS.find(s => s.value === val)
|
return this.segmentTypeDisplayLabel(val)
|
||||||
return hit ? hit.label : val || ''
|
|
||||||
},
|
},
|
||||||
loadPlans() {
|
loadPlans() {
|
||||||
this.planLoading = true
|
this.planLoading = true
|
||||||
@@ -281,7 +424,11 @@ export default {
|
|||||||
this.loadParams(row.planId)
|
this.loadParams(row.planId)
|
||||||
},
|
},
|
||||||
applyFilter() { this.appliedFilterName = this.filterName },
|
applyFilter() { this.appliedFilterName = this.filterName },
|
||||||
resetFilter() { this.filterName = ''; this.appliedFilterName = '' },
|
resetFilter() {
|
||||||
|
this.filterName = ''
|
||||||
|
this.appliedFilterName = ''
|
||||||
|
this.activeSegmentName = ''
|
||||||
|
},
|
||||||
openPlanDialog(row) {
|
openPlanDialog(row) {
|
||||||
this.planForm = row
|
this.planForm = row
|
||||||
? { ...row }
|
? { ...row }
|
||||||
|
|||||||
28
script/sql/mysql/patch_wms_process_spec_dict.sql
Normal file
28
script/sql/mysql/patch_wms_process_spec_dict.sql
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-- -------------------------------------------------------------------------------------
|
||||||
|
-- 规程管理页面字典:工艺类型(wms_process_spec_type)、产线筛选项(wms_process_spec_line)
|
||||||
|
-- 执行前提:已有 sys_dict_type / sys_dict_data 表。
|
||||||
|
--
|
||||||
|
-- 产线字典(dict.value):须为数字型的产线 ID(与 wms_production_line.line_id / wms_process_spec.line_id 一致),不可用字母;
|
||||||
|
-- 字典标签为该产线在规程页 Tab/下拉的展示文案。前端以产线主表列表为全集,字典仅覆盖同 line_id 的显示名(见 processSpec/loadLineOptions)。
|
||||||
|
-- -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
START TRANSACTION;
|
||||||
|
|
||||||
|
INSERT INTO sys_dict_type (dict_id, dict_name, dict_type, status, create_time, remark)
|
||||||
|
VALUES
|
||||||
|
(2036400000000000001, N'规程工艺类型', 'wms_process_spec_type', '0', NOW(), N'规程页筛选用:dict_value = spec_type'),
|
||||||
|
(2036400000000000002, N'规程产线筛选项', 'wms_process_spec_line', '0', NOW(), N'规程页产线筛选:dict_value = line_id')
|
||||||
|
ON DUPLICATE KEY UPDATE dict_name = VALUES(dict_name), remark = VALUES(remark), status = '0';
|
||||||
|
|
||||||
|
INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, status, create_time)
|
||||||
|
VALUES
|
||||||
|
(2036400000000100001, 10, N'工艺规程', 'PROCESS', 'wms_process_spec_type', '0', NOW()),
|
||||||
|
(2036400000000100002, 20, N'标准', 'STANDARD', 'wms_process_spec_type', '0', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE dict_label = VALUES(dict_label), dict_sort = VALUES(dict_sort), dict_value = VALUES(dict_value), status = '0';
|
||||||
|
|
||||||
|
-- 产线筛选项暂不批量插入(避免 dict_code 与库内既有数据冲突)。
|
||||||
|
-- 若需字典内可编辑的产线与页面一致:在「规程管理」页产线行齿轮中新增;或按产线主表自行插入,dict_value = CAST(line_id AS CHAR)。
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- 执行完成后建议在「系统管理 - 字典管理」中刷新缓存,或使用接口 /system/dict/type/refreshCache
|
||||||
Reference in New Issue
Block a user