完成运维规则

master
lynn 7 months ago
parent 74d5a4acc2
commit 359f882f96

@ -0,0 +1,10 @@
import request from '@/utils/request';
// 绑定/更新物资与运维规则的关系
export function saveStocksItem(data) {
return request({
url: '/api/admin/stocks-item/save',
method: 'post',
data
});
}

@ -28,23 +28,19 @@
</div>
<!-- 新增规则 Modal -->
<Modal v-model="showModal" title="新增维护规则" @on-ok="handleSubmit" @on-cancel="handleCancel">
<Modal v-model="showModal" title="新增维护规则" :footer="null" @on-cancel="handleCancel">
<Form ref="form" :model="form" :rules="rules" :label-width="120">
<FormItem label="规则名称" prop="name">
<Input v-model="form.name" placeholder="请输入规则名称"></Input>
</FormItem>
<FormItem label="重复周期" prop="cycle">
<Select v-model="form.cycle">
<Option value="monthly">每月</Option>
<Option value="quarterly">每季度</Option>
<Option value="half">每半年</Option>
<Option value="yearly">每年</Option>
<Option v-for="item in cycleOptions" :key="item.value" :value="item.value">{{ item.label }}</Option>
</Select>
</FormItem>
<FormItem label="起始日期设定" prop="startSetting">
<Select v-model="form.startSetting">
<Option value="specific_date">从每年第一天开始运算</Option>
<Option value="first_association">首次关联物资时</Option>
<Option v-for="item in startStandardOptions" :key="item.value" :value="item.value">{{ item.label }}</Option>
</Select>
</FormItem>
<FormItem label="生成计划时机" prop="timing">
@ -60,14 +56,17 @@
</FormItem>
<FormItem label="未完成时下次计划" prop="nextPlan">
<Select v-model="form.nextPlan">
<Option value="skip">暂停生成直到上次计划已经执行完成</Option>
<Option value="force">强制生成忽略未完成状态</Option>
<Option v-for="item in nextPlanOptions" :key="item.value" :value="item.value">{{ item.label }}</Option>
</Select>
</FormItem>
<FormItem label="备注" prop="notes">
<Input v-model="form.notes" type="textarea" :rows="3" placeholder="请输入备注"></Input>
</FormItem>
</Form>
<div slot="footer" style="text-align: right;">
<Button @click="handleCancel"></Button>
<Button type="primary" @click="handleCustomSubmit" style="margin-left: 8px;">确定</Button>
</div>
</Modal>
<!-- 编辑规则 Modal -->
@ -78,16 +77,12 @@
</FormItem>
<FormItem label="重复周期" prop="cycle">
<Select v-model="editForm.cycle">
<Option value="monthly">每月</Option>
<Option value="quarterly">每季度</Option>
<Option value="half">每半年</Option>
<Option value="yearly">每年</Option>
<Option v-for="item in cycleOptions" :key="item.value" :value="item.value">{{ item.label }}</Option>
</Select>
</FormItem>
<FormItem label="起始日期设定" prop="startSetting">
<Select v-model="editForm.startSetting">
<Option value="specific_date">从每年第一天开始运算</Option>
<Option value="first_association">首次关联物资时</Option>
<Option v-for="item in startStandardOptions" :key="item.value" :value="item.value">{{ item.label }}</Option>
</Select>
</FormItem>
<FormItem label="生成计划时机" prop="timing">
@ -103,8 +98,7 @@
</FormItem>
<FormItem label="未完成时下次计划" prop="nextPlan">
<Select v-model="editForm.nextPlan">
<Option value="skip">暂停生成直到上次计划已经执行完成</Option>
<Option value="force">强制生成忽略未完成状态</Option>
<Option v-for="item in nextPlanOptions" :key="item.value" :value="item.value">{{ item.label }}</Option>
</Select>
</FormItem>
<FormItem label="备注" prop="notes">
@ -113,29 +107,83 @@
</Form>
</Modal>
<!-- 删除确认 Modal -->
<Modal v-model="showDeleteModal" title="删除确认" @on-ok="handleDeleteConfirm" @on-cancel="handleDeleteCancel">
<p>确定要删除该维护规则吗此操作不可恢复</p>
</Modal>
<!-- 关联物资 Modal -->
<Modal v-model="showAssociateModal" title="关联物资" @on-ok="handleAssociateSubmit" @on-cancel="handleAssociateCancel">
<Modal v-model="showAssociateModal" title="关联物资" @on-ok="handleAssociateSubmit" @on-cancel="handleAssociateCancel" width="1000">
<div class="associate-content">
<div class="search-box">
<Input v-model="associateSearch" placeholder="搜索物资" style="width: 200px; margin-bottom: 16px;"></Input>
<div class="associate-toolbar">
<Input v-model="associateSearch" placeholder="搜索物资名称..." style="width: 250px; margin-right: 10px;" clearable></Input>
<Select v-model="select.storehouses_id" @on-change="getWarehouseNames" style="width: 120px; margin-right: 10px;" clearable placeholder="仓库类型">
<Option v-for="item in warehouseTypes" :key="item.id" :value="item.id">{{ item.name }}</Option>
</Select>
<Select v-model="select.area" @on-change="getWarehouseNames" style="width: 120px; margin-right: 10px;" clearable placeholder="所在区域">
<Option v-for="item in warehouseAreas" :key="item.id" :value="item.id">{{ item.value }}</Option>
</Select>
<Select v-model="associateWarehouseName" style="width: 120px; margin-right: 10px;" clearable placeholder="仓库名称">
<Option v-for="warehouse in warehouseNames" :key="warehouse.value" :value="warehouse.value">{{ warehouse.label }}</Option>
</Select>
<Button type="primary" @click="searchMaterialsPost"></Button>
<Button style="margin-left: 8px;" @click="resetAssociateSearch"></Button>
</div>
<Table :columns="associateColumns" :data="associateList" :height="400">
<template slot-scope="{ row }" slot="selection">
<Checkbox v-model="row.selected"></Checkbox>
<Table :columns="associateColumns" :data="associateList" :height="400" style="margin-top: 15px;">
<template slot-scope="{ row }" slot="action">
<div style="display: flex; gap: 8px; justify-content: center;">
<Button
v-if="row.equipment_maintain_config && row.equipment_maintain_config.id === currentRule.id"
type="error"
size="small"
style="border-radius: 6px;"
@click="toggleAssociation(row)"
:title="row.equipment_maintain_config ? `已关联规则: ${row.equipment_maintain_config.name}` : ''"
>
解除绑定
</Button>
<Button
v-else-if="row.equipment_maintain_config"
type="warning"
size="small"
style="border-radius: 6px;"
@click="toggleAssociation(row)"
:title="row.equipment_maintain_config ? `已关联规则: ${row.equipment_maintain_config.name}` : ''"
>
重新绑定
</Button>
<Button
v-else
type="primary"
size="small"
style="border-radius: 6px;"
@click="toggleAssociation(row)"
>
绑定
</Button>
</div>
</template>
</Table>
<div class="pagination-container">
<el-pagination
@size-change="handleAssociatePageSizeChange"
@current-change="handleAssociatePageChange"
:current-page="associateCurrentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="associatePageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="associateTotal"
/>
</div>
</div>
</Modal>
</div>
</template>
<script>
import { Button, Modal, Form, FormItem, Input, Select, Option, InputNumber, Table, Checkbox } from 'view-design';
import { Button, Modal, Form, FormItem, Input, Select, Option, InputNumber, Table, Checkbox, Page } from 'view-design';
import { getStorehouseTypeList } from '@/api/system/storehouseType';
import { getparameteritem } from "@/api/system/dictionary.js";
import { index } from "@/api/system/baseForm.js";
import { saveMaintainConfig, getMaintainConfigList, deleteMaintainConfig } from '@/api/maintenance/maintenance.js';
import { saveStocksItem } from '@/api/stocks-item.js';
import request from '@/utils/request';
import qs from 'qs';
export default {
components: {
@ -148,29 +196,47 @@ export default {
Option,
InputNumber,
Table,
Checkbox
Checkbox,
Page
},
data() {
return {
select: {
page: 1,
page_size: 10,
page_size: 999,
keyword: '',
table_name: 'materialstorages',
area: '',
storehouses_id: ''
},
total: 0,
list: [],
cycleOptions: [
{ value: '1', label: '每月' },
{ value: '3', label: '每季度' },
{ value: '6', label: '每半年' },
{ value: '12', label: '每年' }
],
startStandardOptions: [
{ value: '1', label: '从每年第一天开始运算' },
{ value: '2', label: '首次关联物资时' }
],
nextPlanOptions: [
{ value: '1', label: '暂停生成(直到上次计划已经执行完成)' },
{ value: '2', label: '强制生成(忽略未完成状态)' }
],
table: [
{ label: '序号', type: 'index', width: 60 },
{ label: '规则名称', prop: 'name', minWidth: 160 },
{ label: '重复周期', prop: 'cycle', minWidth: 120 },
{ label: '起始日期设定', prop: 'startSetting', minWidth: 160 },
{ label: '生成计划时机', prop: 'timing', minWidth: 120 },
{ label: '已用于物资数', prop: 'materialCount', minWidth: 120 },
{ label: '未完成时下次计划', prop: 'nextPlan', minWidth: 160 }
{ label: '重复周期', prop: 'month_frequency', minWidth: 120, formatter: row => this.cycleOptions.find(opt => opt.value == row.month_frequency)?.label || row.month_frequency },
{ label: '起始日期设定', prop: 'start_standard', minWidth: 160, formatter: row => this.startStandardOptions.find(opt => opt.value == row.start_standard)?.label || row.start_standard },
{ label: '生成计划时机(天)', prop: 'advance_days', minWidth: 120 },
{ label: '已用于物资数', prop: 'stocks_items_count', minWidth: 120 },
{ label: '未完成时下次计划', prop: 'next_plan_type', minWidth: 160, formatter: row => this.nextPlanOptions.find(opt => opt.value == row.next_plan_type)?.label || row.next_plan_type },
],
tableHeight: 550,
showModal: false,
showEditModal: false,
showDeleteModal: false,
showAssociateModal: false,
form: {
name: '',
@ -199,47 +265,59 @@ export default {
nextPlan: [{ required: true, message: '请选择未完成时下次计划', trigger: 'change' }]
},
associateSearch: '',
associateWarehouseName: '',
associatePageSize: 100,
associateCurrentPage: 1,
associateTotal: 0,
associateColumns: [
{
type: 'selection',
width: 60,
align: 'center'
},
{
title: '物资名称',
key: 'name',
key: 'zichanmingcheng',
minWidth: 120
},
{
title: '物资编号',
key: 'code',
title: '物资代码',
key: 'wuzibianma',
minWidth: 120
},
{
title: '仓库',
key: 'warehouse',
title: '规格型号',
key: 'guigexinghao',
minWidth: 120
}
],
associateList: [
},
{
name: '3号水泵',
code: 'SB001',
warehouse: '外塘河仓库',
selected: false
title: '仓库名称',
key: 'material_info',
minWidth: 120,
render: (h, params) => {
return h('span', params.row.material_info?.suozaicangku || '');
}
},
{
name: '柴油发电机',
code: 'FD001',
warehouse: '胥口仓库',
selected: false
title: '操作',
slot: 'action',
width: 200,
fixed: 'right',
align: 'center',
headerAlign: 'center',
className: 'table-col-action'
}
],
currentRule: null
associateList: [],
currentRule: null,
ruleAssociations: new Map(),
warehouseTypes: [],
warehouseAreas: [],
warehouseNames: [],
allWarehouses: [],
currentWarehouseNames: []
}
},
created() {
this.getList()
this.getList();
this.getWarehouseTypes();
this.getWarehouseAreas();
this.getWarehouseNames();
},
mounted() {
this.calcTableHeight();
@ -248,39 +326,29 @@ export default {
beforeDestroy() {
window.removeEventListener('resize', this.calcTableHeight);
},
watch: {
associateWarehouseArea(val) {
if (val) {
this.getWarehouseNames(val);
} else {
this.warehouseNames = [];
}
}
},
methods: {
async getList() {
try {
// TODO: API
this.list = [
{
name: '水泵月度检查',
cycle: '每月',
startSetting: '每年第一天开始运算',
timing: '提前 7 天',
materialCount: 5,
nextPlan: '暂停生成'
},
{
name: '发电机季度保养',
cycle: '每季度',
startSetting: '首次关联物资时',
timing: '提前 14 天',
materialCount: 2,
nextPlan: '强制生成'
},
{
name: '挡板年度检修',
cycle: '每年',
startSetting: '每年第一天开始运算',
timing: '提前 30 天',
materialCount: 12,
nextPlan: '暂停生成'
}
];
this.total = this.list.length;
const res = await getMaintainConfigList({
page: this.select.page,
page_size: this.select.page_size,
//
});
if (res && res.data) {
this.list = res.data;
this.total = res.total;
}
} catch (e) {
this.$message.error('获取规则失败');
this.$message.error('获取运维规则列表失败');
}
},
pageChange(e) {
@ -298,15 +366,20 @@ export default {
this.tableHeight = Math.max(windowHeight - header - 100, minTableHeight);
},
showAddRuleModal() {
this.form = {
name: '',
cycle: '',
startSetting: '',
timingUnit: 'days',
timingValue: 7,
timingPeriod: 'days',
nextPlan: '',
notes: ''
};
this.showModal = true;
},
handleSubmit() {
this.$refs.form.validate((valid) => {
if (valid) {
// TODO:
this.$message.success('保存成功');
this.showModal = false;
this.getList();
this.$nextTick(() => {
if (this.$refs.form) {
this.$refs.form.resetFields();
}
});
},
@ -318,13 +391,13 @@ export default {
this.currentRule = row;
this.editForm = {
name: row.name,
cycle: row.cycle,
startSetting: row.startSetting,
cycle: String(row.month_frequency),
startSetting: String(row.start_standard),
timingUnit: 'days',
timingValue: 7,
timingValue: row.advance_days,
timingPeriod: 'days',
nextPlan: row.nextPlan,
notes: row.notes || ''
nextPlan: String(row.next_plan_type),
notes: row.remark || ''
};
this.showEditModal = true;
},
@ -344,29 +417,295 @@ export default {
},
handleDelete(row) {
this.currentRule = row;
this.showDeleteModal = true;
},
handleDeleteConfirm() {
// TODO:
this.$message.success('删除成功');
this.showDeleteModal = false;
this.getList();
},
handleDeleteCancel() {
this.showDeleteModal = false;
this.$confirm('确认要删除该维护规则吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
try {
await deleteMaintainConfig({ id: this.currentRule.id });
this.$message.success('删除成功');
this.getList();
} catch (e) {
this.$message.error('删除失败');
}
});
},
handleAssociate(row) {
this.currentRule = row;
this.showAssociateModal = true;
//
this.associateSearch = '';
this.select.storehouses_id = '';
this.select.area = '';
this.associateWarehouseName = '';
this.associateCurrentPage = 1;
//
this.showAssociateModal = false;
this.$nextTick(() => {
this.showAssociateModal = true;
this.loadAssociations();
this.searchMaterialsPost();
});
},
loadAssociations() {
// Load saved associations from localStorage
const saved = localStorage.getItem('ruleAssociations');
if (saved) {
const parsed = JSON.parse(saved);
Object.entries(parsed).forEach(([ruleId, materials]) => {
this.ruleAssociations.set(ruleId, new Set(materials));
});
}
},
saveAssociations() {
const serialized = {};
this.ruleAssociations.forEach((materials, ruleId) => {
serialized[ruleId] = Array.from(materials);
});
localStorage.setItem('ruleAssociations', JSON.stringify(serialized));
},
async searchMaterialsPost() {
try {
const data = {
page_size: this.associatePageSize,
page: this.associateCurrentPage,
'show_relation[0]': 'equipmentMaintainConfig',
'show_relation[1]': 'materialInfo.materialstorage',
'filter[0][key]': 'xianwuzimingcheng',
'filter[0][op]': 'like',
'filter[0][value]': this.associateSearch ? this.associateSearch : '',
'filter[1][key]': 'guanliancangku',
'filter[1][op]': 'eq',
'filter[1][value]': this.associateWarehouseName ? this.associateWarehouseName : '',
'filter[2][key]': 'quyu_id',
'filter[2][op]': 'eq',
'filter[2][value]': this.select.area ? this.select.area : '',
'filter[3][key]': 'wuzileixing',
'filter[3][op]': 'eq',
'filter[3][value]': '一类一码'
};
const res = await request({
url: '/api/admin/stocks-item/index',
method: 'post',
data: qs.stringify(data),
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
if (res && res.data) {
this.associateList = res.data.map(material => ({
...material,
selected: this.ruleAssociations.get(this.currentRule.id)?.has(material.id) || false
}));
this.associateTotal = res.total;
}
} catch (e) {
console.error('获取防汛物资列表失败:', e);
this.$message.error('获取防汛物资列表失败');
}
},
async toggleAssociation(row) {
if (!this.currentRule) return;
if (row.equipment_maintain_config?.id === this.currentRule.id) {
//
try {
await saveStocksItem({
id: row.id,
equipment_maintain_config_id: null
});
this.$message.success(`已解除绑定物资"${row.material_info.suozaicangku}"`);
this.searchMaterialsPost();
} catch (e) {
this.$message.error('解除绑定失败');
}
} else {
//
try {
await saveStocksItem({
id: row.id,
equipment_maintain_config_id: this.currentRule.id
});
this.$message.success(`已成功绑定物资"${row.material_info.suozaicangku}"`);
this.searchMaterialsPost();
} catch (e) {
this.$message.error('绑定失败');
}
}
},
updateMaterialCount() {
const count = this.ruleAssociations.get(this.currentRule.id)?.size || 0;
const index = this.list.findIndex(item => item.id === this.currentRule.id);
if (index !== -1) {
this.$set(this.list[index], 'materialCount', count);
}
},
handleAssociatePageChange(page) {
this.associateCurrentPage = page;
this.searchMaterialsPost();
},
handleAssociateSubmit() {
// TODO:
this.$message.success('关联成功');
this.showAssociateModal = false;
this.getList();
},
handleAssociateCancel() {
this.showAssociateModal = false;
},
handleAssociatePageSizeChange(size) {
this.associatePageSize = size;
this.associateCurrentPage = 1;
this.searchMaterialsPost();
},
resetAssociateSearch() {
this.associateSearch = '';
this.select.storehouses_id = '';
this.select.area = '';
this.associateCurrentPage = 1;
this.searchMaterialsPost();
},
async getWarehouseTypes() {
try {
const res = await getStorehouseTypeList({
page: 1,
page_size: 999
});
if (res && res.data) {
this.warehouseTypes = res.data;
}
} catch (e) {
console.error('获取仓库类型列表失败:', e);
this.$message.error('获取仓库类型列表失败');
}
},
async getWarehouseAreas() {
try {
const res = await getparameteritem("area");
if (res && res.detail) {
this.warehouseAreas = res.detail;
}
} catch (e) {
console.error('获取仓库区域列表失败:', e);
this.$message.error('获取仓库区域列表失败');
}
},
async getWarehouseNames() {
try {
const res = await index({
page_size: this.select.page_size,
page: this.select.page,
table_name: this.select.table_name,
filter: [{
key: 'cangkumingcheng',
op: 'like',
value: this.select.keyword ? this.select.keyword : ''
}, {
key: 'quyu_id',
op: 'eq',
value: this.select.area ? this.select.area : ''
}, {
key: 'storehouses_id',
op: 'eq',
value: this.select.storehouses_id ? this.select.storehouses_id : ''
}],
});
if (res && res.data) {
this.warehouseNames = res.data.map(warehouse => ({
value: warehouse.id,
label: warehouse.cangkumingcheng
}));
}
} catch (e) {
console.error('获取仓库名称列表失败:', e);
this.$message.error('获取仓库名称列表失败');
}
},
clearArea(e) {
this.select.area = e || '';
this.getWarehouseNames();
},
clearType(e) {
this.select.storehouses_id = e || '';
this.getWarehouseNames();
},
async getAllWarehouses() {
try {
const formData = new FormData();
formData.append('page', 1);
formData.append('page_size', 999);
formData.append('table_name', 'materialstorages');
const res = await index(formData);
if (res && res.data) {
this.allWarehouses = res.data;
this.filterWarehouses();
}
} catch (e) {
console.error('获取仓库列表失败:', e);
this.$message.error('获取仓库列表失败');
}
},
filterWarehouses() {
let filtered = [...this.allWarehouses];
//
if (this.select.storehouses_id) {
filtered = filtered.filter(warehouse =>
warehouse.storehouses_id === this.select.storehouses_id
);
}
//
if (this.select.area) {
filtered = filtered.filter(warehouse =>
warehouse.quyu_id === this.select.area
);
}
//
this.warehouseNames = filtered.map(warehouse => ({
value: warehouse.id,
label: warehouse.cangkumingcheng
}));
},
clearArea(e) {
this.select.area = e || '';
this.filterWarehouses();
},
clearType(e) {
this.select.storehouses_id = e || '';
this.filterWarehouses();
},
async handleCustomSubmit() {
this.$refs.form.validate(async (valid) => {
if (valid) {
const data = {
name: this.form.name,
month_frequency: this.form.cycle,
start_standard: this.form.startSetting,
advance_days: this.form.timingValue,
next_plan_type: this.form.nextPlan,
remark: this.form.notes,
equipment_maintain_config_stocks_items: this.selectedMaterialIds?.map(id => [ { stocks_item_id: id } ]) || []
};
try {
await saveMaintainConfig(data);
this.$message.success('保存成功');
this.showModal = false;
this.getList();
} catch (e) {
this.$message.error('保存失败');
}
} else {
this.$message.error('请填写所有必填项');
}
});
}
}
}
@ -407,4 +746,17 @@ export default {
align-items: center;
gap: 8px;
}
.associate-toolbar {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 15px;
}
.pagination-container {
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 15px;
}
</style>

Loading…
Cancel
Save