index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. <template>
  2. <div class="tenant">
  3. <!-- <Banner /> -->
  4. <div class="content">
  5. <byTable
  6. :source="sourceList.data"
  7. :pagination="sourceList.pagination"
  8. :config="config"
  9. :loading="loading"
  10. highlight-current-row
  11. :selectConfig="selectConfig"
  12. :table-events="{
  13. //element talbe事件都能传
  14. select: select,
  15. }"
  16. :action-list="[
  17. {
  18. text: '发起盘点',
  19. action: () => openModal('add'),
  20. },
  21. ]"
  22. @get-list="getList"
  23. >
  24. <template #code="{ item }">
  25. <div
  26. style="cursor: pointer; color: #409eff"
  27. @click="handleClickCode(item)"
  28. >
  29. {{ item.code }}
  30. </div>
  31. </template>
  32. </byTable>
  33. </div>
  34. <el-dialog
  35. :title="modalType == 'add' ? '发起盘点' : '盘点详情'"
  36. v-model="dialogVisible"
  37. width="800"
  38. v-loading="loading"
  39. destroy-on-close
  40. >
  41. <byForm
  42. :formConfig="formConfig"
  43. :formOption="formOption"
  44. v-model="formData.data"
  45. :rules="rules"
  46. ref="byform"
  47. >
  48. <template #products>
  49. <div style="width: 100%">
  50. <el-button
  51. type="primary"
  52. @click="openProduct = true"
  53. style="margin-bottom: 10px"
  54. v-if="modalType == 'add'"
  55. :disabled="!formData.data.warehouseId"
  56. >
  57. 添加物品
  58. </el-button>
  59. <el-table :data="formData.data.list" :row-class-name="changeClass">
  60. <el-table-column prop="productCode" label="物品编码" />
  61. <el-table-column prop="productName" label="物品名称" />
  62. <el-table-column
  63. prop="productUnit"
  64. label="单位"
  65. :formatter="
  66. (row) => dictValueLabel(row.productUnit, productUnit)
  67. "
  68. />
  69. <el-table-column prop="quantity" label="库存数量" />
  70. <el-table-column
  71. prop="checkQuantity"
  72. label="盘点数量"
  73. min-width="150"
  74. >
  75. <template #default="{ row, $index }">
  76. <el-form-item
  77. :prop="'list.' + $index + '.checkQuantity'"
  78. :rules="rules.checkQuantity"
  79. :inline-message="true"
  80. >
  81. <el-input-number
  82. v-model="row.checkQuantity"
  83. :precision="4"
  84. :controls="false"
  85. :min="0"
  86. @change="() => handleChange($index)"
  87. />
  88. </el-form-item>
  89. </template>
  90. </el-table-column>
  91. <el-table-column prop="result" label="盘点结果" />
  92. <el-table-column
  93. prop="zip"
  94. label="操作"
  95. width="100"
  96. v-if="modalType == 'add'"
  97. >
  98. <template #default="{ $index }">
  99. <el-button type="primary" link @click="handleRemove($index)"
  100. >删除</el-button
  101. >
  102. </template>
  103. </el-table-column>
  104. </el-table>
  105. </div>
  106. </template>
  107. </byForm>
  108. <template #footer v-if="modalType == 'add'">
  109. <el-button @click="dialogVisible = false" size="large">取 消</el-button>
  110. <el-button
  111. type="primary"
  112. @click="submitForm('byform')"
  113. size="large"
  114. :loading="submitLoading"
  115. >
  116. 确 定
  117. </el-button>
  118. </template>
  119. </el-dialog>
  120. <el-dialog
  121. v-model="openProduct"
  122. title="选择物品"
  123. width="70%"
  124. append-to-body
  125. >
  126. <SelectGoods
  127. @cancel="openProduct = false"
  128. @pushGoods="pushGoods"
  129. ></SelectGoods>
  130. </el-dialog>
  131. </div>
  132. </template>
  133. <script setup>
  134. /* eslint-disable vue/no-unused-components */
  135. import { ElMessage, ElMessageBox } from "element-plus";
  136. import byTable from "@/components/byTable/index";
  137. import byForm from "@/components/byForm/index";
  138. import { computed, defineComponent, ref } from "vue";
  139. import useUserStore from "@/store/modules/user";
  140. import SelectGoods from "@/components/product/SelectGoods";
  141. const loading = ref(false);
  142. const submitLoading = ref(false);
  143. const sourceList = ref({
  144. data: [],
  145. pagination: {
  146. total: 3,
  147. pageNum: 1,
  148. pageSize: 10,
  149. },
  150. });
  151. let dialogVisible = ref(false);
  152. let openProduct = ref(false);
  153. let roomDialogVisible = ref(false);
  154. let modalType = ref("add");
  155. let rules = ref({
  156. checkQuantity: [
  157. { required: true, message: "请输入盘点数量", trigger: "blur" },
  158. ],
  159. warehouseId: [
  160. { required: true, message: "请选择盘点仓库", trigger: "change" },
  161. ],
  162. });
  163. const { proxy } = getCurrentInstance();
  164. const selectConfig = reactive([
  165. {
  166. label: "盘点结论",
  167. prop: "result",
  168. data: [
  169. {
  170. label: "正常",
  171. value: "0",
  172. },
  173. {
  174. label: "异常",
  175. value: "1",
  176. },
  177. ],
  178. },
  179. ]);
  180. const config = computed(() => {
  181. return [
  182. {
  183. attrs: {
  184. label: "盘点单号",
  185. prop: "code",
  186. slot: "code",
  187. },
  188. },
  189. {
  190. attrs: {
  191. label: "仓库名称",
  192. prop: "warehouseName",
  193. },
  194. },
  195. {
  196. attrs: {
  197. label: "盘点时间",
  198. prop: "createTime",
  199. },
  200. },
  201. {
  202. attrs: {
  203. label: "盘点人",
  204. prop: "userName",
  205. },
  206. },
  207. {
  208. attrs: {
  209. label: "盘点结论",
  210. prop: "result",
  211. },
  212. render(result) {
  213. return result == 0 ? "正常" : "异常";
  214. },
  215. },
  216. {
  217. attrs: {
  218. label: "盘点物品数",
  219. prop: "totalNum",
  220. },
  221. },
  222. {
  223. attrs: {
  224. label: "正常物品数",
  225. prop: "normalNum",
  226. },
  227. },
  228. {
  229. attrs: {
  230. label: "异常物品数",
  231. prop: "anomalyNum",
  232. },
  233. },
  234. // {
  235. // attrs: {
  236. // label: "操作",
  237. // width: "200",
  238. // align: "right",
  239. // },
  240. // // 渲染 el-button,一般用在最后一列。
  241. // renderHTML(row) {
  242. // return [
  243. // {
  244. // attrs: {
  245. // label: "接收",
  246. // type: "primary",
  247. // text: true,
  248. // },
  249. // el: "button",
  250. // click() {
  251. // getDtl(row);
  252. // },
  253. // },
  254. // {
  255. // attrs: {
  256. // label: "打印",
  257. // type: "primary",
  258. // text: true,
  259. // },
  260. // el: "button",
  261. // click() {
  262. // getDtl(row);
  263. // },
  264. // },
  265. // ];
  266. // },
  267. // },
  268. ];
  269. });
  270. let formData = reactive({
  271. data: {},
  272. treeData: [],
  273. });
  274. const formOption = reactive({
  275. inline: true,
  276. labelWidth: 100,
  277. itemWidth: 100,
  278. rules: [],
  279. });
  280. const byform = ref(null);
  281. const treeData = ref([]);
  282. const formConfig = reactive([
  283. {
  284. type: "select",
  285. prop: "warehouseId",
  286. label: "仓库名称",
  287. itemWidth: 33,
  288. isLoad: {
  289. url: "/warehouse/page",
  290. req: {
  291. pageNum: 1,
  292. pageSize: 9999,
  293. },
  294. labelKey: "name",
  295. labelVal: "id",
  296. method: "post",
  297. resUrl: "rows",
  298. },
  299. },
  300. {
  301. type: "title",
  302. title: "盘点明细",
  303. },
  304. {
  305. type: "slot",
  306. slotName: "products",
  307. label: "",
  308. },
  309. ]);
  310. const getList = async (req) => {
  311. sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
  312. loading.value = true;
  313. proxy
  314. .post("/stockCheck/page", sourceList.value.pagination)
  315. .then((message) => {
  316. console.log(message);
  317. sourceList.value.data = message.rows;
  318. sourceList.value.pagination.total = message.total;
  319. setTimeout(() => {
  320. loading.value = false;
  321. }, 200);
  322. });
  323. };
  324. const openModal = () => {
  325. dialogVisible.value = true;
  326. modalType.value = "add";
  327. formConfig[0].disabled = false;
  328. formData.data = {
  329. list: [],
  330. };
  331. };
  332. const submitForm = () => {
  333. console.log(byform.value);
  334. byform.value.handleSubmit((valid) => {
  335. // const list = formData.data.list;
  336. // for (let i = 0; i < list.length; i++) {
  337. // const e = list[i];
  338. // if (e.groupNum > e.canSum) {
  339. // return ElMessage({
  340. // message: "组合数量不可大于可组合数量!",
  341. // type: "info",
  342. // });
  343. // }
  344. // }
  345. submitLoading.value = true;
  346. proxy.post("/stockCheck/" + modalType.value, formData.data).then(
  347. (res) => {
  348. ElMessage({
  349. message: modalType.value == "add" ? "添加成功" : "编辑成功",
  350. type: "success",
  351. });
  352. dialogVisible.value = false;
  353. submitLoading.value = false;
  354. getList();
  355. },
  356. (err) => (submitLoading.value = false)
  357. );
  358. });
  359. };
  360. const getDtl = (row) => {
  361. modalType.value = "edit";
  362. proxy.post("/productSpu/detail", { id: row.id }).then((res) => {
  363. res.list = res.productInfoList;
  364. formData.data = res;
  365. dialogVisible.value = true;
  366. });
  367. };
  368. const productUnit = ref([]);
  369. const getDict = () => {
  370. proxy.getDictOne(["unit"]).then((res) => {
  371. productUnit.value = res["unit"].map((x) => ({
  372. label: x.dictValue,
  373. value: x.dictKey,
  374. }));
  375. });
  376. };
  377. getDict();
  378. getList();
  379. const handleSelect = (row) => {
  380. const flag = formData.data.list.some((x) => x.id === row.id);
  381. if (flag)
  382. return ElMessage({
  383. message: "该物品已选择",
  384. type: "info",
  385. });
  386. formData.data.list.push({
  387. name: row.name,
  388. code: row.code,
  389. id: row.id,
  390. });
  391. return ElMessage({
  392. message: "选择成功",
  393. type: "success",
  394. });
  395. };
  396. const pushGoods = (goods) => {
  397. const arr = goods.map((x) => ({
  398. productId: x.id,
  399. quantity: "",
  400. checkQuantity: "",
  401. productName: x.name,
  402. productCode: x.code,
  403. productUnit: x.unit,
  404. }));
  405. const ids = arr.map((x) => x.productId);
  406. if (formData.data.warehouseId) {
  407. proxy
  408. .post("/stock/pageByWarehouse", {
  409. id: formData.data.warehouseId,
  410. productIds: ids,
  411. })
  412. .then((res) => {
  413. const productList = res.rows;
  414. for (let i = 0; i < productList.length; i++) {
  415. const e = productList[i];
  416. for (let j = 0; j < arr.length; j++) {
  417. const jele = arr[j];
  418. if (e.productId === jele.productId) {
  419. formData.data.list.push({ ...jele, quantity: e.quantity });
  420. }
  421. }
  422. }
  423. });
  424. }
  425. return ElMessage({
  426. message: "添加成功,已为你自动过滤库存数量为0的数据 !",
  427. type: "success",
  428. });
  429. };
  430. const handleRemove = (index) => {
  431. formData.data.list.splice(index, 1);
  432. return ElMessage({
  433. message: "删除成功",
  434. type: "success",
  435. });
  436. };
  437. // watchEffect(() => {
  438. // const list = formData.data.list;
  439. // if (
  440. // formData.data.warehouseId &&
  441. // list.length > 0 &&
  442. // modalType.value == "add"
  443. // ) {
  444. // proxy
  445. // .post("/stock/pageByWarehouse", {
  446. // id: formData.data.warehouseId,
  447. // productIds: list.map((x) => x.productId),
  448. // })
  449. // .then((res) => {
  450. // const productList = res.rows;
  451. // for (let i = 0; i < list.length; i++) {
  452. // const e = list[i];
  453. // e.quantity = 0;
  454. // for (let j = 0; j < productList.length; j++) {
  455. // if (e.productId == x.productId) {
  456. // e.quantity = x.quantity;
  457. // }
  458. // }
  459. // }
  460. // handleChange();
  461. // });
  462. // }
  463. // });
  464. const handleChange = (index) => {
  465. if (!formData.data.list[index].quantity) return;
  466. if (
  467. formData.data.list[index].quantity > formData.data.list[index].checkQuantity
  468. ) {
  469. formData.data.list[index].result = "盘亏";
  470. }
  471. if (
  472. formData.data.list[index].quantity ==
  473. formData.data.list[index].checkQuantity
  474. ) {
  475. formData.data.list[index].result = "正常";
  476. }
  477. if (
  478. formData.data.list[index].quantity < formData.data.list[index].checkQuantity
  479. ) {
  480. formData.data.list[index].result = "盘盈";
  481. }
  482. };
  483. const changeClass = ({ row }) => {
  484. if (row.result == "盘亏") {
  485. return "redClass";
  486. } else if (row.result == "盘盈") {
  487. return "greenClass";
  488. }
  489. };
  490. const handleClickCode = (row) => {
  491. modalType.value = "edit";
  492. proxy.post("/stockCheck/detail", { id: row.id }).then((res) => {
  493. formConfig[0].disabled = true;
  494. res.list.forEach((x) => {
  495. if (Number(x.checkQuantity) > Number(x.quantity)) {
  496. x.result = "盘盈";
  497. }
  498. if (Number(x.checkQuantity) < Number(x.quantity)) {
  499. x.result = "盘亏";
  500. }
  501. if (Number(x.checkQuantity) == Number(x.quantity)) {
  502. x.result = "正常";
  503. }
  504. });
  505. formData.data = {
  506. warehouseId: row.warehouseId,
  507. list: res.list,
  508. };
  509. dialogVisible.value = true;
  510. });
  511. };
  512. </script>
  513. <style lang="scss" scoped>
  514. .tenant {
  515. padding: 20px;
  516. }
  517. </style>
  518. <style >
  519. .redClass {
  520. color: #f54a45 !important;
  521. }
  522. .greenClass {
  523. color: #39c55a !important;
  524. }
  525. </style>