index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. <template>
  2. <div class="tenant">
  3. <div class="content">
  4. <byTable
  5. :source="sourceList.data"
  6. :pagination="sourceList.pagination"
  7. :config="config"
  8. :loading="loading"
  9. :selectConfig="selectConfig"
  10. highlight-current-row
  11. :action-list="[
  12. {
  13. text: '手动出库',
  14. action: () => openModal(),
  15. },
  16. ]"
  17. @get-list="getList"
  18. >
  19. </byTable>
  20. </div>
  21. <el-dialog
  22. title="手动出库"
  23. v-if="dialogVisible"
  24. v-model="dialogVisible"
  25. width="1000"
  26. v-loading="loadingDialog"
  27. >
  28. <byForm
  29. :formConfig="formConfig"
  30. :formOption="formOption"
  31. v-model="formData.data"
  32. :rules="rules"
  33. ref="submit"
  34. >
  35. <template #workOrderId>
  36. <div style="width: 100%">
  37. <el-button type="primary" @click="openOrder = true"
  38. >选择工单</el-button
  39. >
  40. <div style="margin-top: 10px" v-if="selectOrder">
  41. 已选择: {{ selectOrder }}
  42. </div>
  43. </div>
  44. </template>
  45. <template #details>
  46. <div style="width: 100%">
  47. <el-button type="primary" @click="clickAdd()">添加明细</el-button>
  48. <el-table
  49. :data="formData.data.list"
  50. style="width: 100%; margin-top: 16px"
  51. >
  52. <el-table-column
  53. prop="productCode"
  54. label="产品编码"
  55. width="140"
  56. />
  57. <el-table-column
  58. prop="productName"
  59. label="产品名称"
  60. min-width="160"
  61. />
  62. <el-table-column
  63. prop="productSpec"
  64. label="规格型号"
  65. width="160"
  66. />
  67. <el-table-column
  68. prop="productUnit"
  69. label="单位"
  70. width="100"
  71. :formatter="
  72. (row) => dictValueLabel(row.productUnit, productUnit)
  73. "
  74. />
  75. <el-table-column
  76. prop="productQuantity"
  77. label="库存数量"
  78. width="120"
  79. />
  80. <el-table-column label="出库数量" width="160">
  81. <template #default="{ row, $index }">
  82. <div style="width: 100%">
  83. <el-form-item
  84. :prop="'list.' + $index + '.quantity'"
  85. :rules="rules.quantity"
  86. :inline-message="true"
  87. >
  88. <el-input-number
  89. v-model="row.quantity"
  90. placeholder="请输入出库数量"
  91. style="width: 100%"
  92. :precision="0"
  93. :controls="false"
  94. :min="1"
  95. onmousewheel="return false;"
  96. />
  97. </el-form-item>
  98. </div>
  99. </template>
  100. </el-table-column>
  101. <el-table-column
  102. align="center"
  103. label="操作"
  104. width="80"
  105. fixed="right"
  106. >
  107. <template #default="{ row, $index }">
  108. <el-button type="primary" link @click="handleDelete($index)"
  109. >删除</el-button
  110. >
  111. </template>
  112. </el-table-column>
  113. </el-table>
  114. </div>
  115. </template>
  116. </byForm>
  117. <template #footer>
  118. <el-button @click="dialogVisible = false" size="large">取 消</el-button>
  119. <el-button type="primary" @click="submitForm()" size="large"
  120. >确 定</el-button
  121. >
  122. </template>
  123. </el-dialog>
  124. <el-dialog
  125. v-model="openProduct"
  126. title="选择商品"
  127. width="80%"
  128. append-to-body
  129. >
  130. <InventoryInquiry
  131. :selectStatus="true"
  132. :warehouseId="formData.data.warehouseId"
  133. @cancel="openProduct = false"
  134. @select="pushGoods"
  135. :key="formData.data.warehouseId"
  136. >
  137. </InventoryInquiry>
  138. </el-dialog>
  139. <el-dialog
  140. v-model="openOrder"
  141. title="工单选择"
  142. width="80%"
  143. append-to-body
  144. destroy-on-close
  145. >
  146. <WorkOrder :isShowSelect="true" @handleSelectRow="handleSelectRow">
  147. </WorkOrder>
  148. <template #footer>
  149. <el-button @click="openOrder = false" size="large">取 消</el-button>
  150. </template>
  151. </el-dialog>
  152. </div>
  153. </template>
  154. <script setup>
  155. import { computed, ref } from "vue";
  156. import byTable from "@/components/byTable/index";
  157. import byForm from "@/components/byForm/index";
  158. import { ElMessage } from "element-plus";
  159. import InventoryInquiry from "@/views/purchaseSales/outAndInWarehouse/inventoryInquiry/index";
  160. import WorkOrder from "@/views/JXSK/production/workOrder/index";
  161. import useUserStore from "@/store/modules/user";
  162. const { proxy } = getCurrentInstance();
  163. const warehouseList = ref([]);
  164. const productUnit = ref([]);
  165. const sourceList = ref({
  166. data: [],
  167. pagination: {
  168. total: 0,
  169. pageNum: 1,
  170. pageSize: 10,
  171. keyword: "",
  172. warehouseId: "",
  173. type: "2",
  174. },
  175. });
  176. const loading = ref(false);
  177. const selectConfig = computed(() => {
  178. return [
  179. {
  180. label: "仓库名称",
  181. prop: "warehouseId",
  182. data: warehouseList.value,
  183. },
  184. ];
  185. });
  186. const config = computed(() => {
  187. return [
  188. {
  189. attrs: {
  190. label: "仓库名称",
  191. prop: "warehouseName",
  192. width: 220,
  193. },
  194. },
  195. {
  196. attrs: {
  197. label: "关联工单号",
  198. prop: "workOrderCode",
  199. width: 120,
  200. },
  201. },
  202. {
  203. attrs: {
  204. label: "物品编码",
  205. prop: "productCode",
  206. width: 160,
  207. },
  208. },
  209. {
  210. attrs: {
  211. label: "物品名称",
  212. prop: "productName",
  213. "min-width": 220,
  214. },
  215. },
  216. {
  217. attrs: {
  218. label: "规格型号",
  219. prop: "productSpec",
  220. width: 160,
  221. },
  222. },
  223. {
  224. attrs: {
  225. label: "单位",
  226. prop: "productUnit",
  227. width: 120,
  228. },
  229. render(unit) {
  230. return proxy.dictValueLabel(unit, productUnit.value);
  231. },
  232. },
  233. {
  234. attrs: {
  235. label: "出库数量",
  236. prop: "quantity",
  237. width: 140,
  238. },
  239. },
  240. {
  241. attrs: {
  242. label: "操作人",
  243. prop: "opUserName",
  244. width: 140,
  245. },
  246. },
  247. {
  248. attrs: {
  249. label: "操作时间",
  250. prop: "createTime",
  251. width: 160,
  252. },
  253. },
  254. ];
  255. });
  256. const getDict = () => {
  257. proxy.post("/warehouse/page", { pageNum: 1, pageSize: 999 }).then((res) => {
  258. if (res.rows && res.rows.length > 0) {
  259. warehouseList.value = res.rows.map((item) => {
  260. return {
  261. label: item.name,
  262. value: item.id,
  263. };
  264. });
  265. }
  266. });
  267. proxy.getDictOne(["unit"]).then((res) => {
  268. productUnit.value = res["unit"].map((x) => ({
  269. label: x.dictValue,
  270. value: x.dictKey,
  271. }));
  272. });
  273. };
  274. const getList = async (req) => {
  275. sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
  276. loading.value = true;
  277. proxy
  278. .post("/stockJournalDetails/page", sourceList.value.pagination)
  279. .then((res) => {
  280. sourceList.value.data = res.rows;
  281. sourceList.value.pagination.total = res.total;
  282. setTimeout(() => {
  283. loading.value = false;
  284. }, 200);
  285. });
  286. };
  287. getDict();
  288. getList();
  289. const dialogVisible = ref(false);
  290. const loadingDialog = ref(false);
  291. const submit = ref(null);
  292. const openProduct = ref(false);
  293. const formOption = reactive({
  294. inline: true,
  295. labelWidth: 100,
  296. itemWidth: 100,
  297. rules: [],
  298. });
  299. const formData = reactive({
  300. data: {},
  301. });
  302. const formConfig = computed(() => {
  303. return [
  304. {
  305. label: "基本信息",
  306. },
  307. {
  308. type: "select",
  309. prop: "warehouseId",
  310. label: "仓库名称",
  311. required: true,
  312. data: warehouseList.value,
  313. fn: () => {
  314. changeWarehouse();
  315. },
  316. },
  317. {
  318. type: "slot",
  319. slotName: "workOrderId",
  320. label: "绑定工单",
  321. },
  322. {
  323. type: "slot",
  324. slotName: "details",
  325. label: "出库明细",
  326. },
  327. {
  328. type: "input",
  329. prop: "exWarehousePerson",
  330. label: "出库人",
  331. itemWidth: 50,
  332. },
  333. {
  334. type: "input",
  335. prop: "receivingPerson",
  336. label: "接收人",
  337. itemWidth: 50,
  338. },
  339. ];
  340. });
  341. const rules = ref({
  342. warehouseId: [{ required: true, message: "请选择仓库", trigger: "change" }],
  343. quantity: [{ required: true, message: "请输入出库数量", trigger: "blur" }],
  344. exWarehousePerson: [
  345. { required: true, message: "请输入出库人", trigger: "blur" },
  346. ],
  347. receivingPerson: [
  348. { required: true, message: "请输入接收人", trigger: "blur" },
  349. ],
  350. });
  351. const openModal = () => {
  352. formData.data = {
  353. type: "2",
  354. exWarehousePerson: useUserStore().user.nickName,
  355. list: [],
  356. };
  357. loadingDialog.value = false;
  358. dialogVisible.value = true;
  359. };
  360. const clickAdd = () => {
  361. if (formData.data.warehouseId) {
  362. openProduct.value = true;
  363. } else {
  364. ElMessage("请先选择仓库");
  365. }
  366. };
  367. const changeWarehouse = () => {
  368. formData.data.list = [];
  369. };
  370. const select = (item) => {
  371. if (formData.data.list && formData.data.list.length > 0) {
  372. let data = formData.data.list.filter(
  373. (row) => row.productId === item.productId
  374. );
  375. if (data && data.length > 0) {
  376. return ElMessage("请勿重复添加");
  377. }
  378. }
  379. formData.data.list.push({
  380. productCode: item.productCode,
  381. productId: item.productId,
  382. productName: item.productName,
  383. productSpec: item.productSpec,
  384. productUnit: item.productUnit,
  385. productQuantity: item.quantity,
  386. quantity: undefined,
  387. });
  388. ElMessage({
  389. message: "添加成功!",
  390. type: "success",
  391. });
  392. };
  393. const pushGoods = (goods) => {
  394. const arr = goods.map((item) => ({
  395. productCode: item.productCode,
  396. productId: item.productId,
  397. productName: item.productName,
  398. productSpec: item.productSpec,
  399. productUnit: item.productUnit,
  400. productQuantity: item.quantity,
  401. quantity: undefined,
  402. }));
  403. formData.data.list = formData.data.list.concat(arr);
  404. openProduct.value = false;
  405. return ElMessage({
  406. message: "添加成功!",
  407. type: "success",
  408. });
  409. };
  410. const submitForm = () => {
  411. submit.value.handleSubmit(() => {
  412. if (!formData.data.workOrderId) {
  413. return ElMessage({
  414. message: "请绑定工单!",
  415. type: "info",
  416. });
  417. }
  418. if (formData.data.list && formData.data.list.length > 0) {
  419. for (let i = 0; i < formData.data.list.length; i++) {
  420. if (
  421. formData.data.list[i].productQuantity < formData.data.list[i].quantity
  422. ) {
  423. return ElMessage("出库数量不能大于库存数量");
  424. }
  425. }
  426. } else {
  427. return ElMessage("请添加出库产品");
  428. }
  429. loadingDialog.value = true;
  430. proxy.post("/stock/edit", formData.data).then(
  431. () => {
  432. ElMessage({
  433. message: "提交成功",
  434. type: "success",
  435. });
  436. dialogVisible.value = false;
  437. getList();
  438. },
  439. (err) => {
  440. console.log(err);
  441. loadingDialog.value = false;
  442. }
  443. );
  444. });
  445. };
  446. const handleDelete = (index) => {
  447. formData.data.list.splice(index, 1);
  448. };
  449. const openOrder = ref(false);
  450. const selectOrder = ref("");
  451. const handleSelectRow = (row) => {
  452. formData.data.workOrderId = row.id;
  453. selectOrder.value = row.code;
  454. ElMessage({
  455. message: "选择成功",
  456. type: "success",
  457. });
  458. openOrder.value = false;
  459. };
  460. </script>
  461. <style lang="scss" scoped>
  462. .tenant {
  463. padding: 20px;
  464. }
  465. ::v-deep(.el-input-number .el-input__inner) {
  466. text-align: left;
  467. }
  468. </style>