index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. <template>
  2. <view class="order-detail" :class="{ 'fixed-bottom-2': type == 2 }" @click="playGlobalSound">
  3. <scroll-view class="scroller" :scroll-into-view="toView" scroll-y="true" scroll-with-animation="true">
  4. <view class="flex flex-a-c flex-j-b bg-white bind-audit mb-16">
  5. <text>绑定审核员</text>
  6. <text class="text-center flex-1">{{
  7. orderDetail.auditUserName || selectedAuditor?.userName || ""
  8. }}</text>
  9. <text @click="showAuditorSelector = true" class="color-primary">绑定</text>
  10. </view>
  11. <OrderInfo :detail="orderDetail" @refresh="() => getOrderDetail(true)" />
  12. <UserInfoCard :detail="orderDetail" />
  13. <view class="mt-16" style="padding: 0 6rpx">
  14. <u-subsection :list="list" mode="subsection" :current="current"
  15. @change="handleSectionChange"></u-subsection>
  16. <BookInfo v-if="current == 0" :bookList="orderDetail.detailVoList" :detail="orderDetail"
  17. @get-all-firstLetter="onGetAllFirstLetter" />
  18. <LogisticsTimeline v-if="current == 1" :list="orderDetail.trackingVoList" />
  19. <FileInfo v-if="current == 2" :orderId="orderDetail.orderId" />
  20. </view>
  21. </scroll-view>
  22. <view class="fixed-left">
  23. <view class="bind-code common-bg" style="margin-bottom: 30px; padding: 20rpx" @click="handleBindCode">绑码
  24. </view>
  25. <view class="common-bg flex flex-a-c flex-j-c flex-d">
  26. <view class="book-status-item" @click="scrollToView('good')">良好</view>
  27. <view class="book-status-item item-center" @click="scrollToView('average')">一般</view>
  28. <view class="book-status-item" @click="scrollToView('poor')">极差</view>
  29. </view>
  30. </view>
  31. <view class="fixed-right">
  32. <view class="letter-bg flex flex-a-c flex-j-c flex-d">
  33. <view class="letter-item"><u-icon name="arrow-up-fill" size="20" color="#ffffff"
  34. @click="scrollToTop"></u-icon></view>
  35. <view class="letter-item" v-for="(item, index) in allLetters" :key="index" @click="scrollToView(item)">
  36. {{ item }}</view>
  37. </view>
  38. </view>
  39. <view class="common-bg fixed-bottom" v-if="type != 2">
  40. <u-button type="warning" size="large" v-if="orderDetail.status == 5"
  41. @click="handleLogisticsConfirm">物流签收</u-button>
  42. <u-button type="primary" size="large" v-if="orderDetail.status == 6" @click="handleConfirm">确认收货</u-button>
  43. <template v-if="orderDetail.status == 8 || orderDetail.status == 9 || orderDetail.status == 10">
  44. <u-button type="warning" size="large" @click="handleScanCode">扫码</u-button>
  45. <u-button type="primary" size="large" @click="handleComplete">完成</u-button>
  46. </template>
  47. </view>
  48. <AuditorSelector :show="showAuditorSelector" @update:show="showAuditorSelector = $event"
  49. @auditor-selected="handleAuditorSelected" />
  50. </view>
  51. </template>
  52. <script setup>
  53. import { ref, onUnmounted } from "vue";
  54. import { onLoad, onShow } from "@dcloudio/uni-app";
  55. import AuditorSelector from "./components/AuditorSelector.vue";
  56. import OrderInfo from "./components/OrderInfo.vue";
  57. import UserInfoCard from "./components/UserInfoCard.vue";
  58. import LogisticsTimeline from "../express/components/LogisticsTimeline.vue";
  59. import BookInfo from "./components/BookInfo.vue";
  60. import FileInfo from "./components/FileInfo.vue";
  61. const showAuditorSelector = ref(false);
  62. const selectedAuditor = ref({});
  63. const handleAuditorSelected = (auditor) => {
  64. selectedAuditor.value = auditor;
  65. orderDetail.value.auditUserId = auditor.userId;
  66. orderDetail.value.auditUserName = auditor.userName;
  67. uni.setStorageSync("checkUserInfo", {
  68. userName: auditor.userName,
  69. userId: auditor.userId,
  70. });
  71. uni.$u.http.post("/app/orderinfo/setCheckUser", {
  72. checkUserId: auditor.userId,
  73. }).then(res => {
  74. if (res.code == 200) {
  75. uni.$u.toast("绑定成功");
  76. }
  77. })
  78. };
  79. //点击全局音效
  80. function playGlobalSound() {
  81. uni.$u.playClickSound();
  82. }
  83. //点击滚动的位置
  84. const toView = ref("");
  85. function scrollToView(to) {
  86. toView.value = to;
  87. uni.pageScrollTo({
  88. selector: "#" + to,
  89. duration: 200,
  90. });
  91. }
  92. //回到顶部
  93. function scrollToTop() {
  94. uni.pageScrollTo({
  95. top: 0,
  96. duration: 200,
  97. });
  98. }
  99. //绑码
  100. function handleBindCode() {
  101. uni.$u.toast("暂无开发");
  102. }
  103. const list = ref(["图书清单", "物流信息", "上传附件"]);
  104. const current = ref(0);
  105. const handleSectionChange = (index) => {
  106. current.value = index;
  107. };
  108. //监听书籍的首字母
  109. const allLetters = ref([]);
  110. function onGetAllFirstLetter(data) {
  111. allLetters.value = data;
  112. }
  113. //物流签收 /app/orderinfo/signLogistics
  114. function handleLogisticsConfirm() {
  115. uni.showModal({
  116. title: "提示",
  117. content: "是否确认物流签收?",
  118. success: (res) => {
  119. if (res.confirm) {
  120. uni.showLoading({ title: "加载中...", mask: true });
  121. uni.$u.http
  122. .post("/app/orderinfo/signLogistics", {
  123. searchType: 1,
  124. search: orderDetail.value.orderId,
  125. })
  126. .then((res) => {
  127. if (res.code == 200) {
  128. uni.showToast({ title: "签收成功", icon: "none" });
  129. uni.$u.ttsModule.speak("签收成功");
  130. uni.navigateBack();
  131. }
  132. })
  133. .finally(() => {
  134. uni.hideLoading();
  135. });
  136. }
  137. },
  138. });
  139. }
  140. //确认收货 /app/orderinfo/confirmOrder
  141. function handleConfirm() {
  142. uni.showLoading({ title: "加载中...", mask: true });
  143. uni.$u.http
  144. .post("/app/orderinfo/confirmOrder", {
  145. searchType: 1,
  146. search: orderDetail.value.orderId,
  147. })
  148. .then((res) => {
  149. if (res.code == 200) {
  150. uni.showToast({ title: "确认收货成功", icon: "none" });
  151. uni.$u.ttsModule.speak("确认收货成功");
  152. getOrderDetail(true); // 强制刷新数据
  153. }
  154. })
  155. .finally(() => {
  156. uni.hideLoading();
  157. });
  158. }
  159. //完成
  160. function handleComplete() {
  161. let bookList = orderDetail.value.detailVoList;
  162. let bool = bookList.some((item) => item.auditCommentList?.length < item.num);
  163. if (bool) {
  164. let text = "还有未审核的书";
  165. uni.$u.toast(text);
  166. uni.$u.ttsModule.speak(text);
  167. return;
  168. } else {
  169. uni.$u.http
  170. .post("/app/orderinfo/checkOrderFinish", {
  171. checkUserId: orderDetail.value.auditUserId,
  172. orderId: orderDetail.value.orderId,
  173. })
  174. .then((res) => {
  175. if (res.code == 200) {
  176. uni.showToast({ title: "审核完成", icon: "none" });
  177. uni.$u.ttsModule.speak("审核完成");
  178. uni.navigateBack();
  179. }
  180. });
  181. }
  182. }
  183. const orderDetail = ref({ status: 0 });
  184. //获取订单详情
  185. function getOrderDetail(forceRefresh = false) {
  186. if (!orderId.value) return;
  187. // 如果是强制刷新,重置标志位
  188. if (forceRefresh) {
  189. hasLoadedOrderDetail.value = false;
  190. hasLoadedCheckUser.value = false;
  191. }
  192. uni.showLoading({
  193. title: "加载中...",
  194. mask: true,
  195. });
  196. uni.$u.http
  197. .get("/app/orderinfo/getOrderInfoForCheck", {
  198. params: {
  199. searchType: 1,
  200. search: orderId.value,
  201. },
  202. })
  203. .then((res) => {
  204. if (res.code == 200) {
  205. orderDetail.value = res.data;
  206. // 只有在首次加载且没有审核员信息时才调用getCheckUserInfo
  207. if (!hasLoadedCheckUser.value && !res.data.auditUserId) {
  208. getCheckUserInfo();
  209. }
  210. if (res.data.auditUserId) {
  211. let auditUserInfo = {
  212. userName: res.data.auditUserName,
  213. userId: res.data.auditUserId,
  214. };
  215. uni.setStorageSync("checkUserInfo", auditUserInfo);
  216. } else {
  217. let userInfo = uni.getStorageSync("checkUserInfo");
  218. if (userInfo && userInfo.userName) {
  219. orderDetail.value.auditUserName = userInfo.userName;
  220. orderDetail.value.auditUserId = userInfo.userId;
  221. }
  222. }
  223. if (isOnLoad.value) {
  224. if (res.data.manageRemark.length > 0 && res.data.status < 10) {
  225. uni.$u.ttsModule.speak("此订单有备注信息,请注意查看");
  226. }
  227. if (res.data.warnArea && res.data.warnArea.length > 0) {
  228. let text = `此订单来源于${res.data.warnArea}`;
  229. uni.$u.ttsModule.speak(text);
  230. }
  231. isOnLoad.value = false;
  232. }
  233. hasLoadedOrderDetail.value = true; // 标记订单详情已加载
  234. } else {
  235. uni.$u.toast(res.msg);
  236. }
  237. })
  238. .finally(() => {
  239. uni.hideLoading();
  240. });
  241. }
  242. //获取上一次绑定的审核员信息
  243. function getCheckUserInfo() {
  244. uni.$u.http.get("/app/orderinfo/getCheckUser").then((res) => {
  245. if (res.code == 200) {
  246. let userInfo = res.data || {};
  247. orderDetail.value.auditUserName = userInfo.userName || '';
  248. orderDetail.value.auditUserId = userInfo.userId || '';
  249. uni.setStorageSync("checkUserInfo", {
  250. userName: userInfo.userName,
  251. userId: userInfo.userId,
  252. });
  253. }
  254. hasLoadedCheckUser.value = true; // 标记审核员信息已加载
  255. });
  256. }
  257. //isbn正则校验是否符合
  258. function checkIsbn(isbn) {
  259. const isbn13Regex = /^(?:97[89]-?\d{1,5}-?\d{1,7}-?\d{1,6}-?\d)$/;
  260. if (isbn13Regex.test(isbn)) {
  261. return true;
  262. }
  263. return false;
  264. }
  265. //扫码之后的逻辑
  266. function handleScan(isbn) {
  267. if (!checkIsbn(isbn)) {
  268. let text = `不是正确的ISBN码`;
  269. uni.$u.ttsModule.speak(text);
  270. return;
  271. }
  272. if (orderDetail.value.status >= 10) {
  273. uni.$u.ttsModule.speak("订单已审核完成");
  274. return;
  275. }
  276. //取 isbn 的后四位字符串进行播报
  277. let isbnStr = `${isbn.slice(-4)}`;
  278. let isbnList = orderDetail.value.detailVoList.map((item) => item.isbn);
  279. if (isbnList.includes(isbn)) {
  280. let book = orderDetail.value.detailVoList.find((item) => item.isbn == isbn);
  281. //扫描到套装书
  282. if (book.suit == 1) {
  283. let text = `${isbnStr}请注意套装书是否齐全`;
  284. uni.$u.ttsModule.speak(text);
  285. }
  286. //扫描到需要取出的书
  287. if (book.bookWarn == 1) {
  288. let text = `请注意${isbnStr}需要取出`;
  289. uni.$u.ttsModule.speak(text);
  290. }
  291. uni.navigateTo({
  292. url: `/pages/index/detail/book-audit?isbn=${isbn}&orderId=${orderDetail.value.orderId}`,
  293. });
  294. uni.setStorageSync("auditBook", book);
  295. uni.setStorageSync("orderDetail", orderDetail.value);
  296. } else {
  297. let text = `此订单中不存在${isbnStr}这本书 `;
  298. uni.$u.ttsModule.speak(text);
  299. }
  300. }
  301. //扫码
  302. function handleScanCode() {
  303. uni.scanCode({
  304. success: (res) => {
  305. res.result && handleScan(res.result);
  306. },
  307. });
  308. }
  309. const orderId = ref("");
  310. const isOnLoad = ref(false);
  311. const hasLoadedOrderDetail = ref(false); // 添加标志位控制订单详情是否已加载
  312. const hasLoadedCheckUser = ref(false); // 添加标志位控制审核员信息是否已加载
  313. // 1 表示到货审核 2 表示查看订单
  314. const type = ref(1);
  315. onLoad((options) => {
  316. isOnLoad.value = true;
  317. orderId.value = options.id;
  318. orderDetail.value = uni.getStorageSync("orderDetail") || {};
  319. type.value = options.type || 1;
  320. uni.removeStorageSync("scannedBooks");
  321. });
  322. const { unregister, register } = uni.$u.useEventListener((e) => {
  323. handleScan(e.barcode);
  324. }, true);
  325. onShow(() => {
  326. register();
  327. // 只有在首次加载时才调用getOrderDetail,避免重复请求
  328. getOrderDetail();
  329. });
  330. onUnmounted(() => {
  331. // #ifdef APP-PLUS
  332. if (typeof unregister === 'function') {
  333. unregister();
  334. }
  335. // #endif
  336. });
  337. </script>
  338. <style>
  339. page {
  340. background-color: #f5f5f5;
  341. }
  342. </style>
  343. <style lang="scss" scoped>
  344. .order-detail {
  345. font-size: 30rpx;
  346. padding-bottom: 140rpx;
  347. position: relative;
  348. &.fixed-bottom-2 {
  349. padding-bottom: 30rpx;
  350. }
  351. .bind-audit {
  352. padding: 20rpx 30rpx;
  353. border-bottom: 1px solid #e5e5e5;
  354. }
  355. .fixed-left {
  356. position: fixed;
  357. left: 0;
  358. top: 11%;
  359. width: 100rpx;
  360. .common-bg {
  361. background-color: rgba(34, 172, 56, 0.7);
  362. border-radius: 0 30rpx 30rpx 0;
  363. font-weight: 500;
  364. color: #ffffff;
  365. }
  366. .book-status-item {
  367. padding: 20rpx;
  368. border-top: 1rpx solid #ffffff;
  369. border-bottom: 1rpx solid #ffffff;
  370. }
  371. }
  372. .fixed-right {
  373. position: fixed;
  374. right: 0;
  375. top: 12%;
  376. width: 70rpx;
  377. .letter-bg {
  378. background-color: rgba(34, 172, 56, 0.7);
  379. border-radius: 10rpx 0 0 10rpx;
  380. font-weight: 500;
  381. color: #ffffff;
  382. padding: 12rpx 0;
  383. padding-bottom: 6rpx;
  384. }
  385. .letter-item {
  386. padding-bottom: 12rpx;
  387. }
  388. }
  389. }
  390. </style>