goods.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. <template>
  2. <view class="content-box">
  3. <uni-nav-bar
  4. fixed="true"
  5. statusBar="true"
  6. left-icon="back"
  7. :title="title"
  8. background-color="#157A2C"
  9. color="#fff"
  10. @clickLeft="back"
  11. ></uni-nav-bar>
  12. <view class="list_box">
  13. <u-list>
  14. <u-list-item v-for="(item, index) in goodsList" :key="index">
  15. <view class="goods-item">
  16. <view class="goods-header">
  17. <checkbox-group
  18. v-if="type === 'add'"
  19. @change="checkboxChange($event, item)"
  20. >
  21. <checkbox
  22. :value="item.id"
  23. :checked="item.checked"
  24. color="#157A2C"
  25. />
  26. </checkbox-group>
  27. <text class="goods-name">{{ item.categoryName }}</text>
  28. <view class="goods-type">
  29. <text class="type-tag">{{
  30. getTypeName(item.rootCategoryLevelId)
  31. }}</text>
  32. </view>
  33. </view>
  34. <view class="goods-row">
  35. <text class="label">牌号:</text>
  36. <text class="value">{{ item.brandNum }}</text>
  37. </view>
  38. <view class="goods-row">
  39. <text class="label">批次号:</text>
  40. <text class="value">{{ item.batchNo }}</text>
  41. </view>
  42. <view class="goods-row">
  43. <text class="label">数量:</text>
  44. <text class="value"
  45. >{{ item.quantity }}{{ item.measuringUnit }}</text
  46. >
  47. </view>
  48. <view class="goods-row">
  49. <text class="label">重量:</text>
  50. <text class="value">{{ item.weight }}{{ item.weightUnit }}</text>
  51. </view>
  52. <view class="goods-row">
  53. <text class="label">生产工单号:</text>
  54. <text class="value">{{ item.workOrderCode }}</text>
  55. </view>
  56. <view class="goods-row">
  57. <text class="label">规格:</text>
  58. <text class="value">{{ item.specification }}</text>
  59. </view>
  60. <view class="goods-row">
  61. <text class="label">型号:</text>
  62. <text class="value">{{ item.modelType }}</text>
  63. </view>
  64. <view class="goods-row">
  65. <text class="label">编码:</text>
  66. <text class="value">{{ item.categoryCode }}</text>
  67. </view>
  68. <view v-if="type !== 'send'" class="goods-row receive-row">
  69. <text class="label">接收数量:</text>
  70. <view class="input-wrapper">
  71. <u-input
  72. v-model="item.receiveQuantity"
  73. type="digit"
  74. :disabled="type === 'detail'"
  75. placeholder="请输入接收数量"
  76. :customStyle="{ flex: 1 }"
  77. :inputAlign="'left'"
  78. border="surround"
  79. />
  80. <text class="unit">{{ item.measuringUnit }}</text>
  81. </view>
  82. </view>
  83. </view>
  84. </u-list-item>
  85. <u-list-item v-if="goodsList.length === 0">
  86. <view class="empty-wrapper">
  87. <u-empty iconSize="150" textSize="32" text="暂无货物信息"></u-empty>
  88. </view>
  89. </u-list-item>
  90. </u-list>
  91. </view>
  92. <view v-if="type === 'add'" class="footer-btns">
  93. <u-button class="btn-cancel" @click="handleCancel">取消</u-button>
  94. <u-button type="error" class="btn-reject" @click="handleReject"
  95. >驳回</u-button
  96. >
  97. <u-button type="success" class="btn-confirm" @click="handleConfirm"
  98. >确定</u-button
  99. >
  100. </view>
  101. </view>
  102. </template>
  103. <script>
  104. import {
  105. beEntrustDetail,
  106. beEntrustGoodsDetail,
  107. confirmReceipt,
  108. rejectGoods,
  109. } from "@/api/beEntrust/index";
  110. export default {
  111. data() {
  112. return {
  113. type: "add",
  114. title: "收货单",
  115. itemData: {},
  116. goodsList: [],
  117. selection: [],
  118. id: "",
  119. };
  120. },
  121. onLoad(options) {
  122. this.type = options.type || "add";
  123. this.id = options.id;
  124. this.title =
  125. this.type === "add"
  126. ? "收货单"
  127. : this.type === "send"
  128. ? "发货单"
  129. : "收货详情";
  130. if (this.id) {
  131. this.getDetailData();
  132. }
  133. },
  134. methods: {
  135. back() {
  136. uni.navigateBack();
  137. },
  138. async getDetailData() {
  139. try {
  140. const detailRes = await beEntrustDetail(this.id);
  141. this.itemData = detailRes || {};
  142. await this.getGoodsDetail();
  143. } catch (error) {
  144. uni.showToast({
  145. title: "加载失败",
  146. icon: "none",
  147. });
  148. }
  149. },
  150. async getGoodsDetail() {
  151. try {
  152. const detailType = this.type !== "send" ? 1 : 2;
  153. const res = await beEntrustGoodsDetail({
  154. id: this.id,
  155. detailType,
  156. });
  157. this.goodsList = [];
  158. if (res && res.length > 0) {
  159. res.forEach((item) => {
  160. if (this.type !== "detail") {
  161. item.receiveQuantity = item.quantity;
  162. }
  163. item.checked = false;
  164. item.sendStatus = 2;
  165. this.goodsList.push({ ...item });
  166. });
  167. }
  168. } catch (error) {
  169. uni.showToast({
  170. title: "加载失败",
  171. icon: "none",
  172. });
  173. }
  174. },
  175. checkboxChange(e, item) {
  176. item.checked = e.detail.value.length > 0;
  177. this.updateSelection();
  178. },
  179. updateSelection() {
  180. this.selection = this.goodsList.filter((item) => item.checked);
  181. },
  182. getTypeName(type) {
  183. const typeMap = {
  184. 1: "原材料",
  185. 2: "半成品",
  186. 3: "成品",
  187. 4: "辅料",
  188. 5: "包材",
  189. };
  190. return typeMap[type] || "";
  191. },
  192. handleCancel() {
  193. uni.navigateBack();
  194. },
  195. handleReject() {
  196. uni.showModal({
  197. title: "驳回原因",
  198. editable: true,
  199. placeholderText: "请输入驳回原因",
  200. success: async (res) => {
  201. if (res.confirm && res.content) {
  202. uni.showModal({
  203. title: "提示",
  204. content: "确认要驳回该收货单吗?",
  205. success: async (confirmRes) => {
  206. if (confirmRes.confirm) {
  207. uni.showLoading({ title: "驳回中..." });
  208. try {
  209. const ids = this.goodsList.map((it) => it.id);
  210. await rejectGoods({
  211. ids,
  212. applyId: this.id,
  213. beReason: res.content,
  214. });
  215. uni.hideLoading();
  216. uni.showToast({
  217. title: "驳回成功",
  218. icon: "success",
  219. });
  220. setTimeout(() => {
  221. uni.navigateBack();
  222. }, 1500);
  223. } catch (error) {
  224. uni.hideLoading();
  225. uni.showToast({
  226. title: "驳回失败",
  227. icon: "none",
  228. });
  229. }
  230. }
  231. },
  232. });
  233. }
  234. },
  235. });
  236. },
  237. async handleConfirm() {
  238. if (this.selection.length === 0) {
  239. uni.showToast({
  240. title: "请先选择一条收货数据",
  241. icon: "none",
  242. });
  243. return;
  244. }
  245. const selectionIds = new Set(this.selection.map((it) => it.id));
  246. this.goodsList.forEach((item) => {
  247. if (!selectionIds.has(item.id)) {
  248. item.receiveQuantity = 0;
  249. }
  250. });
  251. const totalNum = this.selection.reduce(
  252. (total, pro) => total + Number(pro.receiveQuantity || 0),
  253. 0,
  254. );
  255. const formingNum = Number(this.itemData.sendOutQuantity);
  256. let confirmMessage = "";
  257. if (totalNum < formingNum) {
  258. confirmMessage = "收货数量小于请托数量,是否继续收货?";
  259. } else if (totalNum > formingNum) {
  260. confirmMessage = "收货数量大于请托数量,是否继续收货?";
  261. }
  262. const doConfirmReceipt = async () => {
  263. uni.showLoading({ title: "收货中..." });
  264. try {
  265. await confirmReceipt(this.goodsList);
  266. uni.hideLoading();
  267. uni.showToast({
  268. title: "收货成功",
  269. icon: "success",
  270. });
  271. setTimeout(() => {
  272. uni.navigateBack();
  273. }, 1500);
  274. } catch (error) {
  275. uni.hideLoading();
  276. uni.showToast({
  277. title: "收货失败",
  278. icon: "none",
  279. });
  280. }
  281. };
  282. if (confirmMessage) {
  283. uni.showModal({
  284. title: "提示",
  285. content: confirmMessage,
  286. success: (res) => {
  287. if (res.confirm) {
  288. doConfirmReceipt();
  289. }
  290. },
  291. });
  292. } else {
  293. doConfirmReceipt();
  294. }
  295. },
  296. },
  297. };
  298. </script>
  299. <style lang="scss" scoped>
  300. .content-box {
  301. height: 100vh;
  302. overflow: hidden;
  303. display: flex;
  304. flex-direction: column;
  305. background-color: $page-bg;
  306. }
  307. .list_box {
  308. flex: 1;
  309. overflow: hidden;
  310. padding: 16rpx 0;
  311. .u-list {
  312. height: 100% !important;
  313. }
  314. /deep/ .u-list-item {
  315. width: 100%;
  316. }
  317. }
  318. .goods-item {
  319. background-color: #fff;
  320. border-radius: 12rpx;
  321. padding: 24rpx;
  322. margin: 0 24rpx 20rpx;
  323. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
  324. overflow: hidden;
  325. .goods-header {
  326. display: flex;
  327. justify-content: space-between;
  328. align-items: center;
  329. margin-bottom: 20rpx;
  330. padding-bottom: 20rpx;
  331. border-bottom: 1rpx solid #f0f0f0;
  332. gap: 16rpx;
  333. checkbox-group {
  334. display: flex;
  335. align-items: center;
  336. }
  337. .goods-name {
  338. flex: 1;
  339. font-size: 32rpx;
  340. font-weight: bold;
  341. color: #333;
  342. }
  343. .type-tag {
  344. display: inline-block;
  345. padding: 4rpx 16rpx;
  346. background-color: rgba(21, 122, 44, 0.1);
  347. color: $theme-color;
  348. border-radius: 4rpx;
  349. font-size: 24rpx;
  350. }
  351. }
  352. .goods-row {
  353. display: flex;
  354. align-items: center;
  355. margin-bottom: 12rpx;
  356. font-size: 28rpx;
  357. width: 100%;
  358. .label {
  359. color: #999;
  360. width: 180rpx;
  361. flex-shrink: 0;
  362. }
  363. .value {
  364. flex: 1;
  365. color: #333;
  366. word-break: break-all;
  367. }
  368. .input-wrapper {
  369. flex: 1;
  370. display: flex;
  371. align-items: center;
  372. min-width: 0;
  373. .unit {
  374. margin-left: 8rpx;
  375. color: #999;
  376. white-space: nowrap;
  377. flex-shrink: 0;
  378. }
  379. }
  380. &.receive-row {
  381. /deep/ .u-input {
  382. flex: 1;
  383. }
  384. }
  385. }
  386. }
  387. .empty-wrapper {
  388. display: flex;
  389. align-items: center;
  390. justify-content: center;
  391. padding-top: 25vh;
  392. }
  393. .footer-btns {
  394. display: flex;
  395. gap: 20rpx;
  396. padding: 20rpx 32rpx;
  397. background-color: #fff;
  398. box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.08);
  399. padding-bottom: constant(safe-area-inset-bottom);
  400. padding-bottom: env(safe-area-inset-bottom);
  401. /deep/ .u-button {
  402. flex: 1;
  403. height: 80rpx;
  404. border-radius: 40rpx;
  405. font-size: 30rpx;
  406. }
  407. .btn-cancel {
  408. background-color: #fff;
  409. color: #666;
  410. border: 1rpx solid #e0e0e0;
  411. }
  412. }
  413. </style>