|
|
@@ -0,0 +1,529 @@
|
|
|
+<!-- 新增/编辑弹窗 -->
|
|
|
+<template>
|
|
|
+ <ele-modal
|
|
|
+ width="1000px"
|
|
|
+ :visible="visible"
|
|
|
+ :append-to-body="true"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ custom-class="ele-dialog-form inventory-rules-dialog"
|
|
|
+ :title="dialogTitle"
|
|
|
+ @update:visible="updateVisible"
|
|
|
+ >
|
|
|
+ <el-form
|
|
|
+ ref="form"
|
|
|
+ :model="form"
|
|
|
+ :rules="rules"
|
|
|
+ label-width="120px"
|
|
|
+ label-position="right"
|
|
|
+ >
|
|
|
+ <!-- 基本信息 -->
|
|
|
+ <div class="basic-details-title">
|
|
|
+ <span class="border-span">基本信息</span>
|
|
|
+ </div>
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="盘点规则编码:" prop="code">
|
|
|
+ <el-input
|
|
|
+ v-model="form.code"
|
|
|
+ placeholder="系统自动生成,PDGZ+年月日+3位数字"
|
|
|
+ :disabled="true"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="盘点规则名称:" prop="name">
|
|
|
+ <el-input
|
|
|
+ clearable
|
|
|
+ v-model="form.name"
|
|
|
+ placeholder="请输入盘点规则名称"
|
|
|
+ :disabled="isDetail"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="盘点类型:" prop="type">
|
|
|
+ <el-select
|
|
|
+ v-model="form.type"
|
|
|
+ placeholder="请选择"
|
|
|
+ style="width: 100%"
|
|
|
+ :disabled="isDetail"
|
|
|
+ @change="handleTypeChange"
|
|
|
+ >
|
|
|
+ <el-option label="库内盘点" :value="1" />
|
|
|
+ <el-option label="库外盘点" :value="2" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="盘点分类:" prop="category">
|
|
|
+ <el-select
|
|
|
+ v-model="form.category"
|
|
|
+ placeholder="请选择"
|
|
|
+ style="width: 100%"
|
|
|
+ :disabled="isDetail"
|
|
|
+ >
|
|
|
+ <el-option label="产品" :value="1" />
|
|
|
+ <el-option label="物料" :value="2" />
|
|
|
+ <el-option label="生产设备" :value="3" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="盘点模式:" prop="mode">
|
|
|
+ <el-select
|
|
|
+ v-model="form.mode"
|
|
|
+ placeholder="请选择"
|
|
|
+ style="width: 100%"
|
|
|
+ :disabled="isDetail"
|
|
|
+ >
|
|
|
+ <el-option label="明盘" :value="1" />
|
|
|
+ <el-option label="盲盘" :value="2" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="频率类型:" prop="frequencyType">
|
|
|
+ <el-select
|
|
|
+ v-model="form.frequencyType"
|
|
|
+ placeholder="请选择"
|
|
|
+ style="width: 100%"
|
|
|
+ :disabled="isDetail"
|
|
|
+ >
|
|
|
+ <el-option label="一次性" :value="1" />
|
|
|
+ <el-option label="周期性" :value="2" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8" v-if="form.frequencyType !== 1">
|
|
|
+ <el-form-item label="周期:" prop="periodValue">
|
|
|
+ <el-input
|
|
|
+ v-model="form.periodValue"
|
|
|
+ placeholder="请输入"
|
|
|
+ :disabled="isDetail"
|
|
|
+ style="width: calc(100% - 98px); margin-right: 8px;"
|
|
|
+ />
|
|
|
+ <el-select
|
|
|
+ v-model="form.periodUnit"
|
|
|
+ placeholder="请选择"
|
|
|
+ style="width: 90px;"
|
|
|
+ :disabled="isDetail"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in periodUnitOptions"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="状态:" prop="status">
|
|
|
+ <el-switch
|
|
|
+ v-model="form.status"
|
|
|
+ active-color="#1890ff"
|
|
|
+ inactive-color="#ccc"
|
|
|
+ :active-value="1"
|
|
|
+ :inactive-value="0"
|
|
|
+ :disabled="isDetail"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 盘点策略 -->
|
|
|
+ <div class="basic-details-title">
|
|
|
+ <span class="border-span">盘点策略</span>
|
|
|
+ </div>
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-form-item label="盘点精度:" prop="accuracy">
|
|
|
+ <el-select
|
|
|
+ v-model="form.accuracy"
|
|
|
+ placeholder="请选择"
|
|
|
+ style="width: 100%"
|
|
|
+ :disabled="isDetail"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in accuracyOptions"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="16">
|
|
|
+ <el-form-item label="盘点范围:" prop="range">
|
|
|
+ <el-select
|
|
|
+ v-model="form.range"
|
|
|
+ placeholder="请选择"
|
|
|
+ style="width: 180px; margin-right: 8px;"
|
|
|
+ :disabled="isDetail"
|
|
|
+ @change="handleRangeChange"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in rangeOptions"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ <el-input
|
|
|
+ v-model="form.rangeDesc"
|
|
|
+ :placeholder="rangePlaceholder"
|
|
|
+ :disabled="isDetail"
|
|
|
+ style="width: calc(100% - 188px);"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8" v-if="form.type !== 2">
|
|
|
+ <el-form-item label="冻结策略:" prop="freezeStrategy">
|
|
|
+ <el-select
|
|
|
+ v-model="form.freezeStrategy"
|
|
|
+ placeholder="请选择"
|
|
|
+ style="width: 100%"
|
|
|
+ :disabled="isDetail"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="item in freezeStrategyOptions"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="16" v-if="form.type !== 2">
|
|
|
+ <el-form-item label="是否允许新增'无账货':" prop="allowAddNoAccount">
|
|
|
+ <el-radio-group v-model="form.allowAddNoAccount" :disabled="isDetail">
|
|
|
+ <el-radio :label="0">否</el-radio>
|
|
|
+ <el-radio :label="1">是</el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <template v-slot:footer>
|
|
|
+ <template v-if="isDetail">
|
|
|
+ <el-button @click="updateVisible(false)">关闭</el-button>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <el-button type="primary" :loading="loading" @click="save">
|
|
|
+ 确定
|
|
|
+ </el-button>
|
|
|
+ <el-button @click="updateVisible(false)">取消</el-button>
|
|
|
+ </template>
|
|
|
+ </template>
|
|
|
+ </ele-modal>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+ import {
|
|
|
+ addInventoryRules,
|
|
|
+ updateInventoryRules
|
|
|
+ } from '@/api/rulesManagement/inventoryRules';
|
|
|
+
|
|
|
+ export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ visible: false,
|
|
|
+ loading: false,
|
|
|
+ isUpdate: false,
|
|
|
+ isDetail: false,
|
|
|
+ // 盘点类型:1-库内盘点 2-库外盘点
|
|
|
+ // 库内盘点范围
|
|
|
+ rangeOptionsIn: [
|
|
|
+ { value: 1, label: '按仓库' },
|
|
|
+ { value: 2, label: '按库区' },
|
|
|
+ { value: 3, label: '按货架' },
|
|
|
+ { value: 4, label: '按货位' }
|
|
|
+ ],
|
|
|
+ // 库外盘点范围
|
|
|
+ rangeOptionsOut: [
|
|
|
+ { value: 5, label: '按区域' },
|
|
|
+ { value: 6, label: '按工厂' },
|
|
|
+ { value: 7, label: '按厂房' },
|
|
|
+ { value: 8, label: '按车间' },
|
|
|
+ { value: 9, label: '按产线' },
|
|
|
+ { value: 10, label: '按部门' }
|
|
|
+ ],
|
|
|
+ // 所有冻结策略
|
|
|
+ allFreezeStrategy: [
|
|
|
+ { value: 1, label: '库存快照(不锁库)' },
|
|
|
+ { value: 2, label: '仓库锁' },
|
|
|
+ { value: 3, label: '库区锁' },
|
|
|
+ { value: 4, label: '货架锁' },
|
|
|
+ { value: 5, label: '货位锁' }
|
|
|
+ ],
|
|
|
+ // 周期单位
|
|
|
+ periodUnitOptions: [
|
|
|
+ { value: '分/次', label: '分/次' },
|
|
|
+ { value: '时/次', label: '时/次' },
|
|
|
+ { value: '天/次', label: '天/次' },
|
|
|
+ { value: '周/次', label: '周/次' },
|
|
|
+ { value: '月/次', label: '月/次' },
|
|
|
+ { value: '年/次', label: '年/次' },
|
|
|
+ { value: '次/天', label: '次/天' },
|
|
|
+ { value: '次/周', label: '次/周' },
|
|
|
+ { value: '次/月', label: '次/月' },
|
|
|
+ { value: '次/年', label: '次/年' }
|
|
|
+ ],
|
|
|
+ // 盘点精度
|
|
|
+ accuracyOptions: [
|
|
|
+ { value: 1, label: '物品维度' },
|
|
|
+ { value: 2, label: '批次维度' },
|
|
|
+ { value: 3, label: '包装维度' }
|
|
|
+ ],
|
|
|
+ form: {
|
|
|
+ id: null,
|
|
|
+ code: '',
|
|
|
+ name: '',
|
|
|
+ type: null, // 1-库内 2-库外
|
|
|
+ category: null, // 1-产品 2-物料 3-生产设备
|
|
|
+ mode: null, // 1-明盘 2-盲盘
|
|
|
+ frequencyType: null, // 1-一次性 2-周期性
|
|
|
+ periodValue: '',
|
|
|
+ periodUnit: '分/次',
|
|
|
+ status: 1,
|
|
|
+ accuracy: null, // 1-物品 2-批次 3-包装
|
|
|
+ range: null, // 盘点范围
|
|
|
+ rangeDesc: '', // 范围描述
|
|
|
+ freezeStrategy: null, // 冻结策略
|
|
|
+ allowAddNoAccount: 1 // 是否允许新增无账货:默认是
|
|
|
+ },
|
|
|
+ rules: {
|
|
|
+ name: [{ required: true, message: '请输入盘点规则名称', trigger: 'blur' }],
|
|
|
+ type: [{ required: true, message: '请选择盘点类型', trigger: 'change' }],
|
|
|
+ category: [{ required: true, message: '请选择盘点分类', trigger: 'change' }],
|
|
|
+ mode: [{ required: true, message: '请选择盘点模式', trigger: 'change' }],
|
|
|
+ frequencyType: [{ required: true, message: '请选择频率类型', trigger: 'change' }],
|
|
|
+ periodValue: [
|
|
|
+ {
|
|
|
+ validator: (value, callback) => {
|
|
|
+ if (this.form.frequencyType === 1) {
|
|
|
+ callback();
|
|
|
+ } else if (!value) {
|
|
|
+ callback(new Error('请输入周期'));
|
|
|
+ } else {
|
|
|
+ callback();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ trigger: 'blur'
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ accuracy: [{ required: true, message: '请选择盘点精度', trigger: 'change' }],
|
|
|
+ range: [{ required: true, message: '请选择盘点范围', trigger: 'change' }],
|
|
|
+ rangeDesc: [{ required: true, message: '请选择范围', trigger: 'change' }],
|
|
|
+ freezeStrategy: [
|
|
|
+ {
|
|
|
+ validator: (value, callback) => {
|
|
|
+ if (this.form.type === 2) {
|
|
|
+ callback();
|
|
|
+ } else if (value === null || value === undefined || value === '') {
|
|
|
+ callback(new Error('请选择冻结策略'));
|
|
|
+ } else {
|
|
|
+ callback();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ trigger: 'change'
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ };
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ // 弹窗标题(根据盘点类型动态切换)
|
|
|
+ dialogTitle() {
|
|
|
+ if (this.isDetail) {
|
|
|
+ return '盘点规则详情';
|
|
|
+ }
|
|
|
+ if (this.isUpdate) {
|
|
|
+ return '修改盘点规则';
|
|
|
+ }
|
|
|
+ if (this.form.type === 1) {
|
|
|
+ return '新增库内盘点规则';
|
|
|
+ }
|
|
|
+ if (this.form.type === 2) {
|
|
|
+ return '新增库外盘点规则';
|
|
|
+ }
|
|
|
+ return '新增盘点规则';
|
|
|
+ },
|
|
|
+ // 当前盘点类型下可选的盘点范围
|
|
|
+ rangeOptions() {
|
|
|
+ if (this.form.type === 1) {
|
|
|
+ return this.rangeOptionsIn;
|
|
|
+ }
|
|
|
+ if (this.form.type === 2) {
|
|
|
+ return this.rangeOptionsOut;
|
|
|
+ }
|
|
|
+ return [];
|
|
|
+ },
|
|
|
+ // 范围描述占位符
|
|
|
+ rangePlaceholder() {
|
|
|
+ if (this.form.type === 1) {
|
|
|
+ return '选择仓库';
|
|
|
+ }
|
|
|
+ if (this.form.type === 2) {
|
|
|
+ return '选择区域';
|
|
|
+ }
|
|
|
+ return '请先选择盘点类型';
|
|
|
+ },
|
|
|
+ // 根据盘点范围和盘点类型限制冻结策略
|
|
|
+ freezeStrategyOptions() {
|
|
|
+ // 库外盘点:只允许库存快照(不锁库)
|
|
|
+ if (this.form.type === 2) {
|
|
|
+ return this.allFreezeStrategy.filter(item => item.value === 1);
|
|
|
+ }
|
|
|
+ // 库内盘点:根据范围限制
|
|
|
+ if (this.form.type === 1) {
|
|
|
+ switch (this.form.range) {
|
|
|
+ case 1: // 按仓库:库存快照、仓库锁
|
|
|
+ return this.allFreezeStrategy.filter(item => item.value === 1 || item.value === 2);
|
|
|
+ case 2: // 按库区:库存快照、仓库锁、库区锁
|
|
|
+ return this.allFreezeStrategy.filter(item => [1, 2, 3].includes(item.value));
|
|
|
+ case 3: // 按货架:库存快照、仓库锁、库区锁、货架锁
|
|
|
+ return this.allFreezeStrategy.filter(item => [1, 2, 3, 4].includes(item.value));
|
|
|
+ case 4: // 按货位:所有
|
|
|
+ return this.allFreezeStrategy;
|
|
|
+ default:
|
|
|
+ return this.allFreezeStrategy;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return this.allFreezeStrategy;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ 'form.type'() {
|
|
|
+ // 切换盘点类型时,清空已选择的范围、范围描述、冻结策略
|
|
|
+ if (this.form.range !== null) {
|
|
|
+ this.form.range = null;
|
|
|
+ this.form.rangeDesc = '';
|
|
|
+ }
|
|
|
+ if (this.form.freezeStrategy !== null) {
|
|
|
+ this.form.freezeStrategy = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 'form.frequencyType'() {
|
|
|
+ // 切换频率类型时,清空已填周期值
|
|
|
+ this.form.periodValue = '';
|
|
|
+ this.form.periodUnit = '分';
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ // 生成编码 PDGZ+年月日+3位数字
|
|
|
+ generateCode() {
|
|
|
+ const now = new Date();
|
|
|
+ const year = now.getFullYear();
|
|
|
+ const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
|
+ const day = String(now.getDate()).padStart(2, '0');
|
|
|
+ const random = String(Math.floor(Math.random() * 900) + 100);
|
|
|
+ this.form.code = `PDGZ${year}${month}${day}${random}`;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 盘点类型变化
|
|
|
+ handleTypeChange() {
|
|
|
+ // watch中已处理清空,这里不需要额外操作
|
|
|
+ },
|
|
|
+
|
|
|
+ // 盘点范围变化
|
|
|
+ handleRangeChange() {
|
|
|
+ // 切换范围时清空范围描述和冻结策略
|
|
|
+ this.form.rangeDesc = '';
|
|
|
+ this.form.freezeStrategy = null;
|
|
|
+ },
|
|
|
+
|
|
|
+ open(type, row) {
|
|
|
+ this.visible = true;
|
|
|
+ this.isUpdate = type === 'edit';
|
|
|
+ this.isDetail = type === 'detail';
|
|
|
+ if ((this.isUpdate || this.isDetail) && row) {
|
|
|
+ this.form = {
|
|
|
+ ...this.form,
|
|
|
+ ...row
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ this.resetForm();
|
|
|
+ // this.generateCode();
|
|
|
+ }
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs.form && this.$refs.form.clearValidate();
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ resetForm() {
|
|
|
+ this.form = {
|
|
|
+ id: null,
|
|
|
+ code: '',
|
|
|
+ name: '',
|
|
|
+ type: null,
|
|
|
+ category: null,
|
|
|
+ mode: null,
|
|
|
+ frequencyType: null,
|
|
|
+ periodValue: '',
|
|
|
+ periodUnit: '分/次',
|
|
|
+ status: 1,
|
|
|
+ accuracy: null,
|
|
|
+ range: null,
|
|
|
+ rangeDesc: '',
|
|
|
+ freezeStrategy: null,
|
|
|
+ allowAddNoAccount: 1
|
|
|
+ };
|
|
|
+ },
|
|
|
+
|
|
|
+ save() {
|
|
|
+ this.$refs.form.validate((valid) => {
|
|
|
+ if (!valid) return;
|
|
|
+ this.loading = true;
|
|
|
+ const saveOrUpdate = this.isUpdate ? updateInventoryRules : addInventoryRules;
|
|
|
+ saveOrUpdate(this.form)
|
|
|
+ .then(() => {
|
|
|
+ this.loading = false;
|
|
|
+ this.$message.success(this.isUpdate ? '修改成功' : '新增成功');
|
|
|
+ this.updateVisible(false);
|
|
|
+ this.$emit('done');
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ this.loading = false;
|
|
|
+ });
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ updateVisible(value) {
|
|
|
+ this.visible = value;
|
|
|
+ if (!value) {
|
|
|
+ this.resetForm();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+ .inventory-rules-dialog {
|
|
|
+ :deep(.el-dialog__body) {
|
|
|
+ padding: 16px 24px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .basic-details-title {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ margin-top: 8px;
|
|
|
+ border-bottom: 1px solid #1890ff;
|
|
|
+ padding-bottom: 8px;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ &:first-of-type {
|
|
|
+ margin-top: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .basic-details-title .border-span {
|
|
|
+ height: 18px;
|
|
|
+ line-height: 18px;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #303133;
|
|
|
+ border-left: 4px solid #1890ff;
|
|
|
+ padding-left: 8px;
|
|
|
+ }
|
|
|
+</style>
|