feat(wms): 增加版本管理功能和操作按钮

在规程主表的操作列中新增“版本与方案”按钮,点击后可跳转至版本管理页面。更新了操作列的宽度以适应新按钮。同时,在后端服务中添加了对规程版本存在性的校验,确保在删除规程时不会影响相关版本数据。
This commit is contained in:
王文昊
2026-04-20 19:14:50 +08:00
parent 62b594026b
commit f501994da6
22 changed files with 1357 additions and 2 deletions

View File

@@ -0,0 +1,39 @@
import request from '@/utils/request'
export function listProcessPlan(query) {
return request({
url: '/wms/processPlan/list',
method: 'get',
params: query
})
}
export function getProcessPlan(planId) {
return request({
url: '/wms/processPlan/' + planId,
method: 'get'
})
}
export function addProcessPlan(data) {
return request({
url: '/wms/processPlan',
method: 'post',
data: data
})
}
export function updateProcessPlan(data) {
return request({
url: '/wms/processPlan',
method: 'put',
data: data
})
}
export function delProcessPlan(planId) {
return request({
url: '/wms/processPlan/' + planId,
method: 'delete'
})
}

View File

@@ -0,0 +1,46 @@
import request from '@/utils/request'
export function listProcessSpecVersion(query) {
return request({
url: '/wms/processSpecVersion/list',
method: 'get',
params: query
})
}
export function getProcessSpecVersion(versionId) {
return request({
url: '/wms/processSpecVersion/' + versionId,
method: 'get'
})
}
export function addProcessSpecVersion(data) {
return request({
url: '/wms/processSpecVersion',
method: 'post',
data: data
})
}
export function updateProcessSpecVersion(data) {
return request({
url: '/wms/processSpecVersion',
method: 'put',
data: data
})
}
export function activateProcessSpecVersion(versionId) {
return request({
url: '/wms/processSpecVersion/activate/' + versionId,
method: 'put'
})
}
export function delProcessSpecVersion(versionId) {
return request({
url: '/wms/processSpecVersion/' + versionId,
method: 'delete'
})
}

View File

@@ -74,8 +74,9 @@
</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="140">
<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>
@@ -300,6 +301,21 @@ export default {
this.download('wms/processSpec/export', {
...this.queryParams
}, `processSpec_${new Date().getTime()}.xlsx`)
},
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 m = pathCurrent.match(/^(.*\/processSpec)(?:\/.*)?$/)
const base = m ? m[1] : pathCurrent
this.$router.push({
path: `${base}/version`,
query: { specId: String(specId) }
})
}
}
}

View File

@@ -0,0 +1,374 @@
<template>
<div class="app-container" 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 slot="header">请选择规程</div>
<p class="text-muted mb8" style="color: #909399; font-size: 13px">规程管理列表进入时会自动带上规程从菜单直接进入时请先选择规程</p>
<el-form inline size="small" @submit.native.prevent>
<el-form-item label="规程">
<el-select
v-model="specPickerId"
filterable
clearable
placeholder="请选择规程"
style="min-width: 280px"
:loading="specPickerLoading"
>
<el-option v-for="s in specPickerOptions" :key="s.specId" :label="(s.specCode || '') + ' — ' + (s.specName || '')" :value="String(s.specId)" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" :disabled="!specPickerId" @click="applySpecPicker">进入维护</el-button>
</el-form-item>
</el-form>
</el-card>
<template v-if="specInfo.specId">
<el-card shadow="never" class="mb8">
<div slot="header" class="clearfix">
<span>规程版本</span>
<el-button style="float: right; padding: 3px 10px" type="primary" size="mini" icon="el-icon-plus" @click="openVersionDialog()">新建版本</el-button>
</div>
<KLPTable
:data="versionList"
highlight-current-row
@row-click="onVersionRowClick"
>
<el-table-column label="版本号" prop="versionCode" min-width="120" align="center" />
<el-table-column label="是否生效" prop="isActive" width="100" align="center">
<template slot-scope="scope">
<el-tag v-if="scope.row.isActive === 1" type="success" size="mini">生效</el-tag>
<span v-else></span>
</template>
</el-table-column>
<el-table-column label="状态" prop="status" width="120" align="center" />
<el-table-column label="创建时间" prop="createTime" width="170" align="center" />
<el-table-column label="操作" width="220" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="text" size="mini" @click.stop="activateVersion(scope.row)">设为生效</el-button>
<el-button type="text" size="mini" @click.stop="openVersionDialog(scope.row)">编辑</el-button>
<el-button type="text" size="mini" @click.stop="removeVersion(scope.row)">删除</el-button>
</template>
</el-table-column>
</KLPTable>
</el-card>
<el-card shadow="never" v-if="selectedVersion">
<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">
<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="140" align="center" fixed="right">
<template slot-scope="scope">
<el-button type="text" size="mini" @click="openPlanDialog(scope.row)">编辑</el-button>
<el-button type="text" size="mini" @click="removePlan(scope.row)">删除</el-button>
</template>
</el-table-column>
</KLPTable>
</el-card>
<el-card shadow="never" v-else class="mb8">
<el-empty description="请在上方选择一个规程版本以维护方案点位" />
</el-card>
</template>
<!-- 版本 -->
<el-dialog :title="versionTitle" :visible.sync="versionOpen" width="520px" append-to-body @close="versionForm = {}">
<el-form ref="versionFormRef" :model="versionForm" :rules="versionRules" label-width="100px">
<el-form-item label="版本号" prop="versionCode">
<el-input v-model="versionForm.versionCode" maxlength="64" placeholder="如 V1.0" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="versionForm.status" placeholder="请选择" style="width: 100%">
<el-option v-for="s in statusOptions" :key="s" :label="s" :value="s" />
</el-select>
</el-form-item>
<el-form-item label="保存后生效" prop="isActive">
<el-switch v-model="versionForm.isActive" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="versionForm.remark" type="textarea" rows="2" maxlength="500" show-word-limit />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button :loading="versionSubmitLoading" type="primary" @click="submitVersion"> </el-button>
<el-button @click="versionOpen = false"> </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>
</div>
</template>
<script>
import { getProcessSpec, listProcessSpec } from '@/api/wms/processSpec'
import {
listProcessSpecVersion,
addProcessSpecVersion,
updateProcessSpecVersion,
delProcessSpecVersion,
activateProcessSpecVersion
} from '@/api/wms/processSpecVersion'
import { listProcessPlan, addProcessPlan, updateProcessPlan, delProcessPlan } from '@/api/wms/processPlan'
export default {
name: 'ProcessSpecVersionManage',
data() {
return {
pageLoading: false,
specId: undefined,
specInfo: {},
versionList: [],
selectedVersion: null,
planList: [],
planLoading: false,
statusOptions: ['DRAFT', 'PUBLISHED', 'OBSOLETE'],
segmentOptions: [
{ label: '入口', value: 'INLET' },
{ label: '过程', value: 'PROCESS' },
{ label: '出口', value: 'OUTLET' }
],
versionOpen: false,
versionTitle: '',
versionSubmitLoading: false,
versionForm: {},
versionRules: {
versionCode: [{ required: true, message: '版本号不能为空', trigger: 'blur' }],
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: '',
specPickerOptions: [],
specPickerLoading: false
}
},
watch: {
'$route': {
immediate: true,
handler() {
this.syncSpecIdFromRoute()
}
}
},
methods: {
syncSpecIdFromRoute() {
const raw = this.$route.query.specId
if (raw != null && raw !== '') {
this.specId = String(raw)
} else {
this.specId = undefined
}
this.initPage()
},
loadSpecPickerOptions() {
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() {
if (!this.specId) {
this.specInfo = {}
if (this.specPickerOptions.length === 0) {
this.loadSpecPickerOptions()
}
return
}
this.pageLoading = true
getProcessSpec(this.specId)
.then((res) => {
this.specInfo = res.data || {}
return listProcessSpecVersion({ specId: this.specId, pageNum: 1, pageSize: 200 })
})
.then((res) => {
this.versionList = res.rows || []
this.selectedVersion = null
this.planList = []
})
.catch((e) => {
console.error('加载规程版本失败', e)
})
.finally(() => {
this.pageLoading = false
})
},
onVersionRowClick(row) {
this.selectedVersion = row
this.loadPlans(row.versionId)
},
loadPlans(versionId) {
if (!versionId) {
this.planList = []
return
}
this.planLoading = true
listProcessPlan({ versionId, pageNum: 1, pageSize: 500 })
.then((res) => {
this.planList = res.rows || []
})
.catch((e) => {
console.error('加载方案点位失败', e)
})
.finally(() => {
this.planLoading = 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) {
this.$modal.confirm('确认将版本「' + row.versionCode + '」设为当前生效版本?').then(() => {
return activateProcessSpecVersion(row.versionId)
}).then(() => {
this.$modal.msgSuccess('已生效')
this.initPage()
}).catch(() => {})
},
removeVersion(row) {
this.$modal.confirm('确认删除版本「' + row.versionCode + '」及其下方案点位?').then(() => {
return delProcessSpecVersion(row.versionId)
}).then(() => {
this.$modal.msgSuccess('删除成功')
this.selectedVersion = null
this.planList = []
this.initPage()
}).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('删除成功')
this.loadPlans(this.selectedVersion.versionId)
}).catch(() => {})
}
}
}
</script>