addDialog.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. <!-- 新增/编辑弹窗 -->
  2. <template>
  3. <ele-modal
  4. width="1000px"
  5. :visible="visible"
  6. :append-to-body="true"
  7. :close-on-click-modal="false"
  8. custom-class="ele-dialog-form inventory-rules-dialog"
  9. :title="dialogTitle"
  10. @update:visible="updateVisible"
  11. >
  12. <el-form
  13. ref="form"
  14. :model="form"
  15. :rules="rules"
  16. label-width="120px"
  17. label-position="right"
  18. >
  19. <!-- 基本信息 -->
  20. <div class="basic-details-title">
  21. <span class="border-span">基本信息</span>
  22. </div>
  23. <el-row :gutter="20">
  24. <el-col :span="8">
  25. <el-form-item label="盘点规则编码:" prop="code">
  26. <el-input
  27. v-model="form.code"
  28. placeholder="系统自动生成,PDGZ+年月日+3位数字"
  29. :disabled="true"
  30. />
  31. </el-form-item>
  32. </el-col>
  33. <el-col :span="8">
  34. <el-form-item label="盘点规则名称:" prop="name">
  35. <el-input
  36. clearable
  37. v-model="form.name"
  38. placeholder="请输入盘点规则名称"
  39. :disabled="isDetail"
  40. />
  41. </el-form-item>
  42. </el-col>
  43. <el-col :span="8">
  44. <el-form-item label="盘点类型:" prop="type">
  45. <el-select
  46. v-model="form.type"
  47. placeholder="请选择"
  48. style="width: 100%"
  49. :disabled="isDetail"
  50. @change="handleTypeChange"
  51. >
  52. <el-option label="库内盘点" :value="1" />
  53. <el-option label="库外盘点" :value="2" />
  54. </el-select>
  55. </el-form-item>
  56. </el-col>
  57. <el-col :span="8">
  58. <el-form-item label="盘点分类:" prop="category">
  59. <el-select
  60. v-model="form.category"
  61. placeholder="请选择"
  62. style="width: 100%"
  63. :disabled="isDetail"
  64. >
  65. <el-option label="产品" :value="1" />
  66. <el-option label="物料" :value="2" />
  67. <el-option label="生产设备" :value="3" />
  68. </el-select>
  69. </el-form-item>
  70. </el-col>
  71. <el-col :span="8">
  72. <el-form-item label="盘点模式:" prop="mode">
  73. <el-select
  74. v-model="form.mode"
  75. placeholder="请选择"
  76. style="width: 100%"
  77. :disabled="isDetail"
  78. >
  79. <el-option label="明盘" :value="1" />
  80. <el-option label="盲盘" :value="2" />
  81. </el-select>
  82. </el-form-item>
  83. </el-col>
  84. <el-col :span="8">
  85. <el-form-item label="频率类型:" prop="frequencyType">
  86. <el-select
  87. v-model="form.frequencyType"
  88. placeholder="请选择"
  89. style="width: 100%"
  90. :disabled="isDetail"
  91. >
  92. <el-option label="一次性" :value="1" />
  93. <el-option label="周期性" :value="2" />
  94. </el-select>
  95. </el-form-item>
  96. </el-col>
  97. <el-col :span="8" v-if="form.frequencyType !== 1">
  98. <el-form-item label="周期:" prop="periodValue">
  99. <el-input
  100. v-model="form.periodValue"
  101. placeholder="请输入"
  102. :disabled="isDetail"
  103. style="width: calc(100% - 98px); margin-right: 8px;"
  104. />
  105. <el-select
  106. v-model="form.periodUnit"
  107. placeholder="请选择"
  108. style="width: 90px;"
  109. :disabled="isDetail"
  110. >
  111. <el-option
  112. v-for="item in periodUnitOptions"
  113. :key="item.value"
  114. :label="item.label"
  115. :value="item.value"
  116. />
  117. </el-select>
  118. </el-form-item>
  119. </el-col>
  120. <el-col :span="8">
  121. <el-form-item label="状态:" prop="status">
  122. <el-switch
  123. v-model="form.status"
  124. active-color="#1890ff"
  125. inactive-color="#ccc"
  126. :active-value="1"
  127. :inactive-value="0"
  128. :disabled="isDetail"
  129. />
  130. </el-form-item>
  131. </el-col>
  132. </el-row>
  133. <!-- 盘点策略 -->
  134. <div class="basic-details-title">
  135. <span class="border-span">盘点策略</span>
  136. </div>
  137. <el-row :gutter="20">
  138. <el-col :span="8">
  139. <el-form-item label="盘点精度:" prop="accuracy">
  140. <el-select
  141. v-model="form.accuracy"
  142. placeholder="请选择"
  143. style="width: 100%"
  144. :disabled="isDetail"
  145. >
  146. <el-option
  147. v-for="item in accuracyOptions"
  148. :key="item.value"
  149. :label="item.label"
  150. :value="item.value"
  151. />
  152. </el-select>
  153. </el-form-item>
  154. </el-col>
  155. <el-col :span="16">
  156. <el-form-item label="盘点范围:" prop="range">
  157. <el-select
  158. v-model="form.range"
  159. placeholder="请选择"
  160. style="width: 180px; margin-right: 8px;"
  161. :disabled="isDetail"
  162. @change="handleRangeChange"
  163. >
  164. <el-option
  165. v-for="item in rangeOptions"
  166. :key="item.value"
  167. :label="item.label"
  168. :value="item.value"
  169. />
  170. </el-select>
  171. <el-input
  172. v-model="form.rangeDesc"
  173. :placeholder="rangePlaceholder"
  174. :disabled="isDetail"
  175. style="width: calc(100% - 188px);"
  176. />
  177. </el-form-item>
  178. </el-col>
  179. <el-col :span="8" v-if="form.type !== 2">
  180. <el-form-item label="冻结策略:" prop="freezeStrategy">
  181. <el-select
  182. v-model="form.freezeStrategy"
  183. placeholder="请选择"
  184. style="width: 100%"
  185. :disabled="isDetail"
  186. >
  187. <el-option
  188. v-for="item in freezeStrategyOptions"
  189. :key="item.value"
  190. :label="item.label"
  191. :value="item.value"
  192. />
  193. </el-select>
  194. </el-form-item>
  195. </el-col>
  196. <el-col :span="16" v-if="form.type !== 2">
  197. <el-form-item label="是否允许新增'无账货':" prop="allowAddNoAccount">
  198. <el-radio-group v-model="form.allowAddNoAccount" :disabled="isDetail">
  199. <el-radio :label="0">否</el-radio>
  200. <el-radio :label="1">是</el-radio>
  201. </el-radio-group>
  202. </el-form-item>
  203. </el-col>
  204. </el-row>
  205. </el-form>
  206. <template v-slot:footer>
  207. <template v-if="isDetail">
  208. <el-button @click="updateVisible(false)">关闭</el-button>
  209. </template>
  210. <template v-else>
  211. <el-button type="primary" :loading="loading" @click="save">
  212. 确定
  213. </el-button>
  214. <el-button @click="updateVisible(false)">取消</el-button>
  215. </template>
  216. </template>
  217. </ele-modal>
  218. </template>
  219. <script>
  220. import {
  221. addInventoryRules,
  222. updateInventoryRules
  223. } from '@/api/rulesManagement/inventoryRules';
  224. export default {
  225. data() {
  226. return {
  227. visible: false,
  228. loading: false,
  229. isUpdate: false,
  230. isDetail: false,
  231. // 盘点类型:1-库内盘点 2-库外盘点
  232. // 库内盘点范围
  233. rangeOptionsIn: [
  234. { value: 1, label: '按仓库' },
  235. { value: 2, label: '按库区' },
  236. { value: 3, label: '按货架' },
  237. { value: 4, label: '按货位' }
  238. ],
  239. // 库外盘点范围
  240. rangeOptionsOut: [
  241. { value: 5, label: '按区域' },
  242. { value: 6, label: '按工厂' },
  243. { value: 7, label: '按厂房' },
  244. { value: 8, label: '按车间' },
  245. { value: 9, label: '按产线' },
  246. { value: 10, label: '按部门' }
  247. ],
  248. // 所有冻结策略
  249. allFreezeStrategy: [
  250. { value: 1, label: '库存快照(不锁库)' },
  251. { value: 2, label: '仓库锁' },
  252. { value: 3, label: '库区锁' },
  253. { value: 4, label: '货架锁' },
  254. { value: 5, label: '货位锁' }
  255. ],
  256. // 周期单位
  257. periodUnitOptions: [
  258. { value: '分/次', label: '分/次' },
  259. { value: '时/次', label: '时/次' },
  260. { value: '天/次', label: '天/次' },
  261. { value: '周/次', label: '周/次' },
  262. { value: '月/次', label: '月/次' },
  263. { value: '年/次', label: '年/次' },
  264. { value: '次/天', label: '次/天' },
  265. { value: '次/周', label: '次/周' },
  266. { value: '次/月', label: '次/月' },
  267. { value: '次/年', label: '次/年' }
  268. ],
  269. // 盘点精度
  270. accuracyOptions: [
  271. { value: 1, label: '物品维度' },
  272. { value: 2, label: '批次维度' },
  273. { value: 3, label: '包装维度' }
  274. ],
  275. form: {
  276. id: null,
  277. code: '',
  278. name: '',
  279. type: null, // 1-库内 2-库外
  280. category: null, // 1-产品 2-物料 3-生产设备
  281. mode: null, // 1-明盘 2-盲盘
  282. frequencyType: null, // 1-一次性 2-周期性
  283. periodValue: '',
  284. periodUnit: '分/次',
  285. status: 1,
  286. accuracy: null, // 1-物品 2-批次 3-包装
  287. range: null, // 盘点范围
  288. rangeDesc: '', // 范围描述
  289. freezeStrategy: null, // 冻结策略
  290. allowAddNoAccount: 1 // 是否允许新增无账货:默认是
  291. },
  292. rules: {
  293. name: [{ required: true, message: '请输入盘点规则名称', trigger: 'blur' }],
  294. type: [{ required: true, message: '请选择盘点类型', trigger: 'change' }],
  295. category: [{ required: true, message: '请选择盘点分类', trigger: 'change' }],
  296. mode: [{ required: true, message: '请选择盘点模式', trigger: 'change' }],
  297. frequencyType: [{ required: true, message: '请选择频率类型', trigger: 'change' }],
  298. periodValue: [
  299. {
  300. validator: (value, callback) => {
  301. if (this.form.frequencyType === 1) {
  302. callback();
  303. } else if (!value) {
  304. callback(new Error('请输入周期'));
  305. } else {
  306. callback();
  307. }
  308. },
  309. trigger: 'blur'
  310. }
  311. ],
  312. accuracy: [{ required: true, message: '请选择盘点精度', trigger: 'change' }],
  313. range: [{ required: true, message: '请选择盘点范围', trigger: 'change' }],
  314. rangeDesc: [{ required: true, message: '请选择范围', trigger: 'change' }],
  315. freezeStrategy: [
  316. {
  317. validator: (value, callback) => {
  318. if (this.form.type === 2) {
  319. callback();
  320. } else if (value === null || value === undefined || value === '') {
  321. callback(new Error('请选择冻结策略'));
  322. } else {
  323. callback();
  324. }
  325. },
  326. trigger: 'change'
  327. }
  328. ]
  329. }
  330. };
  331. },
  332. computed: {
  333. // 弹窗标题(根据盘点类型动态切换)
  334. dialogTitle() {
  335. if (this.isDetail) {
  336. return '盘点规则详情';
  337. }
  338. if (this.isUpdate) {
  339. return '修改盘点规则';
  340. }
  341. if (this.form.type === 1) {
  342. return '新增库内盘点规则';
  343. }
  344. if (this.form.type === 2) {
  345. return '新增库外盘点规则';
  346. }
  347. return '新增盘点规则';
  348. },
  349. // 当前盘点类型下可选的盘点范围
  350. rangeOptions() {
  351. if (this.form.type === 1) {
  352. return this.rangeOptionsIn;
  353. }
  354. if (this.form.type === 2) {
  355. return this.rangeOptionsOut;
  356. }
  357. return [];
  358. },
  359. // 范围描述占位符
  360. rangePlaceholder() {
  361. if (this.form.type === 1) {
  362. return '选择仓库';
  363. }
  364. if (this.form.type === 2) {
  365. return '选择区域';
  366. }
  367. return '请先选择盘点类型';
  368. },
  369. // 根据盘点范围和盘点类型限制冻结策略
  370. freezeStrategyOptions() {
  371. // 库外盘点:只允许库存快照(不锁库)
  372. if (this.form.type === 2) {
  373. return this.allFreezeStrategy.filter(item => item.value === 1);
  374. }
  375. // 库内盘点:根据范围限制
  376. if (this.form.type === 1) {
  377. switch (this.form.range) {
  378. case 1: // 按仓库:库存快照、仓库锁
  379. return this.allFreezeStrategy.filter(item => item.value === 1 || item.value === 2);
  380. case 2: // 按库区:库存快照、仓库锁、库区锁
  381. return this.allFreezeStrategy.filter(item => [1, 2, 3].includes(item.value));
  382. case 3: // 按货架:库存快照、仓库锁、库区锁、货架锁
  383. return this.allFreezeStrategy.filter(item => [1, 2, 3, 4].includes(item.value));
  384. case 4: // 按货位:所有
  385. return this.allFreezeStrategy;
  386. default:
  387. return this.allFreezeStrategy;
  388. }
  389. }
  390. return this.allFreezeStrategy;
  391. }
  392. },
  393. watch: {
  394. 'form.type'() {
  395. // 切换盘点类型时,清空已选择的范围、范围描述、冻结策略
  396. if (this.form.range !== null) {
  397. this.form.range = null;
  398. this.form.rangeDesc = '';
  399. }
  400. if (this.form.freezeStrategy !== null) {
  401. this.form.freezeStrategy = null;
  402. }
  403. },
  404. 'form.frequencyType'() {
  405. // 切换频率类型时,清空已填周期值
  406. this.form.periodValue = '';
  407. this.form.periodUnit = '分';
  408. }
  409. },
  410. methods: {
  411. // 生成编码 PDGZ+年月日+3位数字
  412. generateCode() {
  413. const now = new Date();
  414. const year = now.getFullYear();
  415. const month = String(now.getMonth() + 1).padStart(2, '0');
  416. const day = String(now.getDate()).padStart(2, '0');
  417. const random = String(Math.floor(Math.random() * 900) + 100);
  418. this.form.code = `PDGZ${year}${month}${day}${random}`;
  419. },
  420. // 盘点类型变化
  421. handleTypeChange() {
  422. // watch中已处理清空,这里不需要额外操作
  423. },
  424. // 盘点范围变化
  425. handleRangeChange() {
  426. // 切换范围时清空范围描述和冻结策略
  427. this.form.rangeDesc = '';
  428. this.form.freezeStrategy = null;
  429. },
  430. open(type, row) {
  431. this.visible = true;
  432. this.isUpdate = type === 'edit';
  433. this.isDetail = type === 'detail';
  434. if ((this.isUpdate || this.isDetail) && row) {
  435. this.form = {
  436. ...this.form,
  437. ...row
  438. };
  439. } else {
  440. this.resetForm();
  441. // this.generateCode();
  442. }
  443. this.$nextTick(() => {
  444. this.$refs.form && this.$refs.form.clearValidate();
  445. });
  446. },
  447. resetForm() {
  448. this.form = {
  449. id: null,
  450. code: '',
  451. name: '',
  452. type: null,
  453. category: null,
  454. mode: null,
  455. frequencyType: null,
  456. periodValue: '',
  457. periodUnit: '分/次',
  458. status: 1,
  459. accuracy: null,
  460. range: null,
  461. rangeDesc: '',
  462. freezeStrategy: null,
  463. allowAddNoAccount: 1
  464. };
  465. },
  466. save() {
  467. this.$refs.form.validate((valid) => {
  468. if (!valid) return;
  469. this.loading = true;
  470. const saveOrUpdate = this.isUpdate ? updateInventoryRules : addInventoryRules;
  471. saveOrUpdate(this.form)
  472. .then(() => {
  473. this.loading = false;
  474. this.$message.success(this.isUpdate ? '修改成功' : '新增成功');
  475. this.updateVisible(false);
  476. this.$emit('done');
  477. })
  478. .catch(() => {
  479. this.loading = false;
  480. });
  481. });
  482. },
  483. updateVisible(value) {
  484. this.visible = value;
  485. if (!value) {
  486. this.resetForm();
  487. }
  488. }
  489. }
  490. };
  491. </script>
  492. <style lang="scss" scoped>
  493. .inventory-rules-dialog {
  494. :deep(.el-dialog__body) {
  495. padding: 16px 24px;
  496. }
  497. }
  498. .basic-details-title {
  499. margin-bottom: 16px;
  500. margin-top: 8px;
  501. border-bottom: 1px solid #1890ff;
  502. padding-bottom: 8px;
  503. display: flex;
  504. justify-content: space-between;
  505. &:first-of-type {
  506. margin-top: 0;
  507. }
  508. }
  509. .basic-details-title .border-span {
  510. height: 18px;
  511. line-height: 18px;
  512. font-size: 16px;
  513. font-weight: 600;
  514. color: #303133;
  515. border-left: 4px solid #1890ff;
  516. padding-left: 8px;
  517. }
  518. </style>