BookInfo.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. <template>
  2. <view class="book-info">
  3. <template v-if="isShow">
  4. <view class="font-bold mt-12 mb-10" style="padding: 0 20rpx">
  5. {{ bookList.length }}条记录,共{{ detail.totalNum }}本,已审{{ auditNum || 0 }}本
  6. </view>
  7. <view class="font-light" style="padding: 0 20rpx">
  8. 其中已审:良好{{ getAuditCateNum(1) }}本,一般0本,极差{{ getAuditCateNum(3) }}本
  9. </view>
  10. </template>
  11. <view
  12. class="book-info-item bg-white mt-12"
  13. :style="{ backgroundColor: item.backgroundColor }"
  14. v-for="item in formatList"
  15. :key="item.title"
  16. :id="item.id"
  17. v-show="item?.title"
  18. >
  19. <view class="book-info-item-title">{{ item.title }}</view>
  20. <view class="book-info-item-list">
  21. <BookItem v-for="book in item.list" :key="book.bookId" :item="book" @click="handleBookClick(book)" />
  22. </view>
  23. </view>
  24. </view>
  25. </template>
  26. <script setup>
  27. import { ref, watch, computed } from "vue";
  28. import BookItem from "./BookItem.vue";
  29. import toPinyin from "@/utils/toPinyin.js";
  30. const props = defineProps({
  31. bookList: {
  32. type: Array,
  33. default: () => [],
  34. },
  35. detail: {
  36. type: Object,
  37. default: () => ({}),
  38. },
  39. isShow: {
  40. type: Boolean,
  41. default: true,
  42. },
  43. });
  44. const emit = defineEmits(["get-all-firstLetter"]);
  45. //计算已审书籍数量
  46. const auditNum = computed(() => {
  47. return props.bookList.reduce((total, item) => {
  48. let num = item.auditCommentList?.filter((v) => v.sts != 0).length || 0;
  49. return total + num;
  50. }, 0);
  51. });
  52. function handleBookClick(book) {
  53. if (props.detail.status >= 10) {
  54. uni.$u.ttsModule.speak("订单已审核完成");
  55. return;
  56. }
  57. uni.navigateTo({
  58. url: `/pages/index/detail/book-audit?isbn=${book.isbn}&orderId=${props.detail.orderId}`,
  59. });
  60. uni.setStorageSync("auditBook", book);
  61. }
  62. function getAuditCateNum(cate) {
  63. let auditList = [];
  64. props.bookList.forEach((v) => {
  65. auditList.push(...(v.auditCommentList || []));
  66. });
  67. return auditList.filter((v) => v.sts == cate).length;
  68. }
  69. //格式化书籍列表
  70. const formatBookList = (list) => {
  71. const poorBooks = { title: "极差", list: [], backgroundColor: "#de868f", id: "poor" };
  72. const goodBooks = { title: "良好", list: [], backgroundColor: "#81b337", id: "good" };
  73. const otherBooks = [];
  74. //获取所有的首字母
  75. const allFirstLetter = [];
  76. const handleBookCategorization = (book) => {
  77. let firstLetter;
  78. // 找到第一个中文
  79. let firstChineseChar = null;
  80. for (let i = 0; i < book.bookName.length; i++) {
  81. const char = book.bookName.charAt(i);
  82. if (/^[\u4E00-\u9FA5]$/.test(char)) {
  83. // 检查是否为中文
  84. firstChineseChar = char;
  85. break;
  86. }
  87. }
  88. // 如果找到中文,使用其拼音首字母
  89. if (firstChineseChar) {
  90. firstLetter = toPinyin.chineseToInitials(toPinyin.chineseToPinYin(firstChineseChar));
  91. } else {
  92. // 如果没有找到中文,使用第一个字符
  93. const char = book.bookName.charAt(0);
  94. if (/^[A-Za-z]$/.test(char)) {
  95. firstLetter = char.toUpperCase();
  96. } else {
  97. firstLetter = char;
  98. }
  99. }
  100. // 兜底方案:如果首字母为空或无效,放入"其他"分类
  101. if (!firstLetter || firstLetter.trim() === "") {
  102. firstLetter = "其他";
  103. }
  104. let bool = otherBooks.some((item) => item.title == firstLetter);
  105. if (!bool) {
  106. otherBooks.push({ id: firstLetter, title: firstLetter, list: [{ ...book }] });
  107. allFirstLetter.push(firstLetter);
  108. emit("get-all-firstLetter", allFirstLetter);
  109. } else {
  110. otherBooks.find((item) => item.title == firstLetter).list.push(book);
  111. }
  112. };
  113. list.forEach((book) => {
  114. book.goodNum = book.auditCommentList?.filter((v) => v.sts == 1).length || 0;
  115. book.badNum = book.auditCommentList?.filter((v) => v.sts == 3).length || 0;
  116. // Check if book is fully audited (all items have been reviewed)
  117. const totalAudited = book.auditCommentList?.filter((v) => v.sts !== 0).length || 0;
  118. const isFullyAudited = totalAudited === book.num;
  119. if (isFullyAudited) {
  120. // If any audit result is poor (sts === 3), push to poorBooks
  121. if (book.auditCommentList.some((v) => v.sts === 3)) {
  122. poorBooks.list.push(book);
  123. } else if (book.auditCommentList.every((v) => v.sts === 1)) {
  124. // If all audit results are good (sts === 1), push to goodBooks
  125. goodBooks.list.push(book);
  126. } else {
  127. handleBookCategorization(book);
  128. }
  129. } else {
  130. handleBookCategorization(book);
  131. }
  132. });
  133. return { poorBooks, goodBooks, otherBooks };
  134. };
  135. const formatList = ref([]);
  136. watch(
  137. () => props.bookList,
  138. (newVal) => {
  139. let { poorBooks, goodBooks, otherBooks } = formatBookList(newVal);
  140. // 按照首字母排序 A-Z
  141. otherBooks.sort((a, b) => a.id.localeCompare(b.id));
  142. let poor = poorBooks.list.length > 0 ? poorBooks : {};
  143. let good = goodBooks.list.length > 0 ? goodBooks : {};
  144. formatList.value = [...otherBooks, poor, good];
  145. },
  146. { immediate: true, deep: true }
  147. );
  148. </script>
  149. <style scoped>
  150. .book-info-item-title {
  151. padding: 10rpx 30rpx;
  152. font-size: 28rpx;
  153. font-weight: 600;
  154. border-bottom: 1px solid #e5e5e5;
  155. }
  156. </style>