index.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. <template>
  2. <view class="content-box">
  3. <uni-nav-bar
  4. fixed="true"
  5. statusBar="true"
  6. left-icon="back"
  7. title="请托列表"
  8. background-color="#157A2C"
  9. color="#fff"
  10. @clickLeft="back"
  11. ></uni-nav-bar>
  12. <view class="top-wrapper">
  13. <uni-section>
  14. <uni-easyinput
  15. prefixIcon="search"
  16. v-model="searchForm.name"
  17. placeholder="请输入名称"
  18. ></uni-easyinput>
  19. </uni-section>
  20. <image
  21. class="menu_icon"
  22. src="~@/static/pda/menu.svg"
  23. @click="showFilter = true"
  24. ></image>
  25. <button class="search_btn" @click="handleSearch">搜索</button>
  26. <button class="search_btn" @click="handleAdd">新增</button>
  27. </view>
  28. <view class="list_box">
  29. <u-list @scrolltolower="loadMore">
  30. <u-list-item v-for="item in list" :key="item.id">
  31. <view class="list-item" @click="goDetail(item)">
  32. <view class="item-header">
  33. <view class="item-code">{{ item.code }}</view>
  34. <view class="item-status">
  35. <text class="status-tag" :class="getStatusClass(item.status)">{{
  36. getStatusText(item.status)
  37. }}</text>
  38. </view>
  39. </view>
  40. <view class="item-row">
  41. <text class="label">类型:</text>
  42. <text class="value">{{ getTypeText(item.type) }}</text>
  43. </view>
  44. <view class="item-row">
  45. <text class="label">名称:</text>
  46. <text class="value">{{ item.name }}</text>
  47. </view>
  48. <view class="item-row">
  49. <text class="label">紧急程度:</text>
  50. <text class="value">{{
  51. item.priority == 1 ? "一般" : "紧急"
  52. }}</text>
  53. </view>
  54. <view class="item-row">
  55. <text class="label">请托数量:</text>
  56. <text class="value"
  57. >{{ item.totalCount }}{{ item.measuringUnit }}</text
  58. >
  59. </view>
  60. <view class="item-row">
  61. <text class="label">受托工厂:</text>
  62. <text class="value">{{ item.beEntrustedFactoriesName }}</text>
  63. </view>
  64. <view class="item-row">
  65. <text class="label">发货状态:</text>
  66. <text class="value">
  67. <text
  68. class="status-tag"
  69. :class="getSendStatusClass(item.sendStatus)"
  70. >{{ getSendStatusText(item.sendStatus) }}</text
  71. >
  72. </text>
  73. </view>
  74. <view class="item-row">
  75. <text class="label">审批状态:</text>
  76. <text class="value">
  77. <text
  78. class="status-tag"
  79. :class="getApprovalStatusClass(item.approvalStatus)"
  80. >{{ getApprovalStatusText(item.approvalStatus) }}</text
  81. >
  82. </text>
  83. </view>
  84. <view class="item-footer">
  85. <text class="time">{{ item.createTime }}</text>
  86. </view>
  87. <view
  88. class="item-actions"
  89. v-if="
  90. canEdit(item) ||
  91. canSubmit(item) ||
  92. canDelete(item) ||
  93. canSend(item) ||
  94. canReceive(item) ||
  95. canViewReceiveDetail(item) ||
  96. canViewSendDetail(item)
  97. "
  98. >
  99. <button
  100. v-if="canEdit(item)"
  101. class="action-btn btn-primary"
  102. @click.stop="handleEdit(item)"
  103. >
  104. 编辑
  105. </button>
  106. <button
  107. v-if="canDelete(item)"
  108. class="action-btn btn-error"
  109. @click.stop="handleDelete(item)"
  110. >
  111. 删除
  112. </button>
  113. <button
  114. v-if="canSubmit(item)"
  115. class="action-btn btn-success"
  116. @click.stop="handleSubmit(item)"
  117. >
  118. 提交
  119. </button>
  120. <button
  121. v-if="canSend(item)"
  122. class="action-btn btn-warning"
  123. @click.stop="handleSend(item)"
  124. >
  125. 发货
  126. </button>
  127. <button
  128. v-if="canReceive(item)"
  129. class="action-btn btn-success"
  130. @click.stop="handleReceive(item)"
  131. >
  132. 收货
  133. </button>
  134. <button
  135. v-if="canViewReceiveDetail(item)"
  136. class="action-btn btn-info"
  137. @click.stop="handleReceiveDetail(item)"
  138. >
  139. 收货详情
  140. </button>
  141. <button
  142. v-if="canViewSendDetail(item)"
  143. class="action-btn btn-order"
  144. @click.stop="handleSendDetail(item)"
  145. >
  146. 发货详情
  147. </button>
  148. </view>
  149. </view>
  150. </u-list-item>
  151. <u-list-item v-if="list.length === 0">
  152. <view class="empty-wrapper">
  153. <u-empty iconSize="150" textSize="32" text="暂无数据"></u-empty>
  154. </view>
  155. </u-list-item>
  156. </u-list>
  157. </view>
  158. <SearchPopup mode="top" v-if="showFilter">
  159. <template v-slot:list>
  160. <view class="search_list">
  161. <u-form
  162. labelPosition="left"
  163. :model="searchForm"
  164. labelWidth="180"
  165. labelAlign="left"
  166. >
  167. <u-form-item label="类型:" borderBottom>
  168. <picker
  169. mode="selector"
  170. :range="typeList"
  171. range-key="label"
  172. @change="typeChange"
  173. >
  174. <view class="picker-value">{{ currentType || "全部" }}</view>
  175. </picker>
  176. </u-form-item>
  177. </u-form>
  178. </view>
  179. </template>
  180. <template v-slot:operate>
  181. <view class="operate_box">
  182. <u-button
  183. size="small"
  184. class="u-reset-button reset-btn"
  185. @click="filterCancel"
  186. >重置</u-button
  187. >
  188. <u-button
  189. type="success"
  190. size="small"
  191. class="u-reset-button confirm-btn"
  192. @click="filterConfirm"
  193. >确定</u-button
  194. >
  195. </view>
  196. </template>
  197. </SearchPopup>
  198. </view>
  199. </template>
  200. <script>
  201. import { getList, submit, remove } from "@/api/entrust/index.js";
  202. import SearchPopup from "../../components/searchPopup.vue";
  203. let isEnd = false;
  204. export default {
  205. components: {
  206. SearchPopup,
  207. },
  208. data() {
  209. return {
  210. searchForm: {
  211. type: "",
  212. name: "",
  213. beEntrustedFactoriesId: "",
  214. },
  215. currentType: "",
  216. typeList: [],
  217. list: [],
  218. pageNum: 1,
  219. pageSize: 20,
  220. total: 0,
  221. loading: false,
  222. hasMore: true,
  223. showFilter: false,
  224. };
  225. },
  226. onLoad() {
  227. this.getTypeList();
  228. },
  229. onShow() {
  230. this.loadData(true);
  231. },
  232. methods: {
  233. back() {
  234. uni.navigateBack();
  235. },
  236. async getTypeList() {
  237. // 获取字典数据
  238. this.typeList = [
  239. { label: "全部", value: "" },
  240. { label: "加工", value: "1" },
  241. { label: "装配", value: "2" },
  242. ];
  243. },
  244. typeChange(e) {
  245. const index = e.detail.value;
  246. this.searchForm.type = this.typeList[index].value;
  247. this.currentType = this.typeList[index].label;
  248. },
  249. async loadData(isRefresh = false) {
  250. if (this.loading) return;
  251. if (isRefresh) {
  252. this.pageNum = 1;
  253. this.list = [];
  254. this.hasMore = true;
  255. }
  256. if (!this.hasMore) return;
  257. this.loading = true;
  258. isEnd = false;
  259. try {
  260. const res = await getList({
  261. pageNum: this.pageNum,
  262. size: this.pageSize,
  263. ...this.searchForm,
  264. });
  265. if (this.pageNum === 1) {
  266. this.list = [];
  267. }
  268. if (res.list && res.list.length > 0) {
  269. this.list.push(...res.list);
  270. this.total = res.total;
  271. isEnd = this.list.length >= this.total;
  272. this.hasMore = !isEnd;
  273. } else {
  274. isEnd = true;
  275. this.hasMore = false;
  276. }
  277. } catch (error) {
  278. uni.showToast({
  279. title: "加载失败",
  280. icon: "none",
  281. });
  282. } finally {
  283. this.loading = false;
  284. }
  285. },
  286. loadMore() {
  287. if (isEnd) return;
  288. this.pageNum++;
  289. this.loadData();
  290. },
  291. handleSearch() {
  292. this.loadData(true);
  293. },
  294. filterConfirm() {
  295. this.showFilter = false;
  296. this.loadData(true);
  297. },
  298. filterCancel() {
  299. this.searchForm.type = "";
  300. this.currentType = "";
  301. this.showFilter = false;
  302. this.loadData(true);
  303. },
  304. goDetail(item) {
  305. uni.navigateTo({
  306. url: `/pages/pda/entrust/detail/detail?id=${item.id}`,
  307. });
  308. },
  309. handleAdd() {
  310. uni.navigateTo({
  311. url: "/pages/pda/entrust/create/create?type=add",
  312. });
  313. },
  314. handleEdit(item) {
  315. uni.navigateTo({
  316. url: `/pages/pda/entrust/create/create?type=edit&id=${item.id}`,
  317. });
  318. },
  319. handleDelete(item) {
  320. uni.showModal({
  321. title: "提示",
  322. content: "确定要删除该请托单吗?",
  323. success: async (res) => {
  324. if (res.confirm) {
  325. try {
  326. await remove([item.id]);
  327. uni.showToast({
  328. title: "删除成功",
  329. icon: "success",
  330. });
  331. this.loadData(true);
  332. } catch (error) {
  333. uni.showToast({
  334. title: "删除失败",
  335. icon: "none",
  336. });
  337. }
  338. }
  339. },
  340. });
  341. },
  342. handleSubmit(item) {
  343. uni.showModal({
  344. title: "提示",
  345. content: "确定要提交该请托单吗?",
  346. success: async (res) => {
  347. if (res.confirm) {
  348. try {
  349. await submit({
  350. businessId: item.id,
  351. beEntrustedDeptId: item.beEntrustedDeptId,
  352. });
  353. uni.showToast({
  354. title: "提交成功",
  355. icon: "success",
  356. });
  357. this.loadData(true);
  358. } catch (error) {
  359. uni.showToast({
  360. title: "提交失败",
  361. icon: "none",
  362. });
  363. }
  364. }
  365. },
  366. });
  367. },
  368. handleSend(item) {
  369. uni.navigateTo({
  370. url: `/pages/pda/entrust/goods/goods?id=${item.id}&type=add`,
  371. });
  372. },
  373. handleReceive(item) {
  374. uni.navigateTo({
  375. url: `/pages/pda/entrust/goods/goods?id=${item.id}&type=receipt`,
  376. });
  377. },
  378. canEdit(item) {
  379. return (
  380. (item.status == 0 &&
  381. item.approvalStatus != 1 &&
  382. item.approvalStatus != 2) ||
  383. item.approvalStatus == 3
  384. );
  385. },
  386. canDelete(item) {
  387. return (
  388. item.status == 0 && item.approvalStatus != 1 && item.approvalStatus != 2
  389. );
  390. },
  391. canSubmit(item) {
  392. return item.approvalStatus != 1 && item.approvalStatus != 2;
  393. },
  394. canSend(item) {
  395. return (
  396. item.approvalStatus == 2 &&
  397. (item.sendStatus == 0 || !item.sendStatus || item.sendStatus == 5)
  398. );
  399. },
  400. canReceive(item) {
  401. return item.approvalStatus == 2 && item.sendStatus == 3;
  402. },
  403. canViewReceiveDetail(item) {
  404. return item.approvalStatus == 2 && item.sendStatus == 4;
  405. },
  406. canViewSendDetail(item) {
  407. return (
  408. item.approvalStatus == 2 &&
  409. item.sendStatus != 0 &&
  410. item.sendStatus &&
  411. item.sendStatus != 5 &&
  412. item.sendStatus != 3 &&
  413. item.sendStatus != 4
  414. );
  415. },
  416. handleReceiveDetail(item) {
  417. uni.navigateTo({
  418. url: `/pages/pda/entrust/goods/goods?id=${item.id}&type=receipt`,
  419. });
  420. },
  421. handleSendDetail(item) {
  422. uni.navigateTo({
  423. url: `/pages/pda/entrust/goods/goods?id=${item.id}&type=detail`,
  424. });
  425. },
  426. getTypeText(type) {
  427. const typeMap = {
  428. 1: "加工",
  429. 2: "装配",
  430. };
  431. return typeMap[type] || "";
  432. },
  433. getStatusText(status) {
  434. const statusMap = {
  435. 0: "未提交",
  436. 1: "已提交",
  437. 2: "已发布",
  438. };
  439. return statusMap[status] || "";
  440. },
  441. getStatusClass(status) {
  442. const classMap = {
  443. 0: "status-default",
  444. 1: "status-warning",
  445. 2: "status-success",
  446. };
  447. return classMap[status] || "status-default";
  448. },
  449. getSendStatusText(sendStatus) {
  450. const statusMap = {
  451. 0: "未发货",
  452. 1: "已发货",
  453. 2: "已收货",
  454. 3: "受托已发",
  455. 4: "请托已收",
  456. 5: "受托拒收",
  457. 6: "请托拒收",
  458. };
  459. return statusMap[sendStatus] || "未发货";
  460. },
  461. getSendStatusClass(sendStatus) {
  462. if (!sendStatus || sendStatus == 0) return "status-default";
  463. if (sendStatus == 5 || sendStatus == 6) return "status-danger";
  464. return "status-success";
  465. },
  466. getApprovalStatusText(approvalStatus) {
  467. const statusMap = {
  468. 0: "未提交",
  469. 1: "审核中",
  470. 2: "审核通过",
  471. 3: "审核不通过",
  472. };
  473. return statusMap[approvalStatus] || "未提交";
  474. },
  475. getApprovalStatusClass(approvalStatus) {
  476. const classMap = {
  477. 0: "status-default",
  478. 1: "status-warning",
  479. 2: "status-success",
  480. 3: "status-danger",
  481. };
  482. return classMap[approvalStatus] || "status-default";
  483. },
  484. },
  485. };
  486. </script>
  487. <style lang="scss">
  488. page {
  489. height: 100vh;
  490. overflow: hidden;
  491. }
  492. </style>
  493. <style lang="scss" scoped>
  494. .content-box {
  495. height: 100vh;
  496. overflow: hidden;
  497. display: flex;
  498. flex-direction: column;
  499. background-color: $page-bg;
  500. }
  501. .top-wrapper {
  502. background-color: #fff;
  503. display: flex;
  504. width: 750rpx;
  505. padding: 16rpx 32rpx;
  506. align-items: center;
  507. gap: 16rpx;
  508. flex-shrink: 0;
  509. position: sticky;
  510. top: 88rpx;
  511. z-index: 99;
  512. /deep/.uni-section {
  513. margin-top: 0px;
  514. flex: 1;
  515. }
  516. /deep/.uni-section-header {
  517. padding: 0px;
  518. }
  519. .search_btn {
  520. width: 120rpx;
  521. height: 70rpx;
  522. line-height: 70rpx;
  523. padding: 0 24rpx;
  524. background: $theme-color;
  525. font-size: 32rpx;
  526. color: #fff;
  527. margin: 0;
  528. }
  529. .menu_icon {
  530. width: 44rpx;
  531. height: 44rpx;
  532. }
  533. .add_btn {
  534. width: 70rpx;
  535. height: 70rpx;
  536. line-height: 70rpx;
  537. padding: 0;
  538. background: $theme-color;
  539. font-size: 48rpx;
  540. color: #fff;
  541. border-radius: 50%;
  542. margin: 0;
  543. }
  544. }
  545. .list_box {
  546. flex: 1;
  547. overflow: hidden;
  548. padding: 16rpx 0;
  549. .u-list {
  550. height: 100% !important;
  551. }
  552. }
  553. .list-item {
  554. background-color: #fff;
  555. border-radius: 12rpx;
  556. padding: 24rpx;
  557. margin: 0 24rpx 20rpx;
  558. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
  559. .item-header {
  560. display: flex;
  561. justify-content: space-between;
  562. align-items: center;
  563. margin-bottom: 20rpx;
  564. padding-bottom: 20rpx;
  565. border-bottom: 1rpx solid #f0f0f0;
  566. .item-code {
  567. font-size: 32rpx;
  568. font-weight: bold;
  569. color: $theme-color;
  570. }
  571. }
  572. .item-row {
  573. display: flex;
  574. margin-bottom: 16rpx;
  575. font-size: 28rpx;
  576. .label {
  577. color: #999;
  578. min-width: 160rpx;
  579. }
  580. .value {
  581. flex: 1;
  582. color: #333;
  583. word-break: break-all;
  584. }
  585. }
  586. .item-footer {
  587. margin-top: 20rpx;
  588. padding-top: 20rpx;
  589. border-top: 1rpx solid #f0f0f0;
  590. .time {
  591. font-size: 24rpx;
  592. color: #999;
  593. }
  594. }
  595. .item-actions {
  596. display: flex !important;
  597. justify-content: flex-end !important;
  598. gap: 12rpx;
  599. margin-top: 20rpx;
  600. overflow-x: auto;
  601. .action-btn {
  602. flex: 0 0 auto !important;
  603. width: auto !important;
  604. border-radius: 32rpx !important;
  605. height: 56rpx !important;
  606. line-height: 56rpx !important;
  607. font-size: 26rpx !important;
  608. padding: 0 24rpx !important;
  609. margin: 0 !important;
  610. border: none !important;
  611. color: #fff !important;
  612. &.btn-primary {
  613. background-color: #2979ff !important;
  614. }
  615. &.btn-error {
  616. background-color: #f56c6c !important;
  617. }
  618. &.btn-success {
  619. background-color: $theme-color !important;
  620. }
  621. &.btn-warning {
  622. background-color: #ff9900 !important;
  623. }
  624. &.btn-info {
  625. background-color: #e0ffff !important;
  626. }
  627. &.btn-order {
  628. background-color: #b23aee !important;
  629. }
  630. }
  631. }
  632. }
  633. .status-tag {
  634. display: inline-block;
  635. padding: 4rpx 16rpx;
  636. border-radius: 4rpx;
  637. font-size: 24rpx;
  638. &.status-default {
  639. background-color: #f0f0f0;
  640. color: #666;
  641. }
  642. &.status-warning {
  643. background-color: #fff7e6;
  644. color: #faad14;
  645. }
  646. &.status-success {
  647. background-color: rgba(21, 122, 44, 0.1);
  648. color: $theme-color;
  649. }
  650. &.status-danger {
  651. background-color: #fff1f0;
  652. color: #ff4d4f;
  653. }
  654. }
  655. .empty-wrapper {
  656. display: flex;
  657. align-items: center;
  658. justify-content: center;
  659. padding-top: 25vh;
  660. }
  661. .search_list {
  662. min-height: 100rpx;
  663. padding: 24rpx 32rpx;
  664. .picker-value {
  665. padding: 12rpx 16rpx;
  666. border: 1rpx solid #e0e0e0;
  667. border-radius: 6rpx;
  668. font-size: 28rpx;
  669. }
  670. }
  671. .operate_box {
  672. display: flex;
  673. flex-direction: row;
  674. align-items: center;
  675. justify-content: space-between;
  676. padding: 24rpx 32rpx 32rpx;
  677. gap: 24rpx;
  678. .reset-btn {
  679. flex: 1;
  680. height: 72rpx;
  681. border-radius: 36rpx;
  682. font-size: $uni-font-size-sm;
  683. }
  684. .confirm-btn {
  685. flex: 1;
  686. height: 72rpx;
  687. border-radius: 36rpx;
  688. font-size: $uni-font-size-sm;
  689. }
  690. }
  691. </style>