browse.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. <template>
  2. <view class="bg-[var(--page-bg-color)] min-h-screen overflow-hidden" :style="themeColor()">
  3. <view class="fixed top-0 left-0 right-0 z-200">
  4. <view class="tab-style-1 py-[20rpx] bg-[#fff] border-0 border-solid border-b-[1rpx] border-[#f6f6f6] ">
  5. <view class="tab-left text-[28rpx]">
  6. <text>共</text>
  7. <text class="text-primary">{{ browseTotal }}</text>
  8. <text>条</text>
  9. </view>
  10. <view class="tab-right !items-center">
  11. <view class="flex items-center" @click="handleSelect">
  12. <view class="tab-right-date">日期</view>
  13. <view class="nc-iconfont nc-icon-a-riliV6xx-36 tab-right-icon"></view>
  14. </view>
  15. <view class="w-[2rpx] h-[28rpx] mx-[20rpx] bg-gradient-to-b from-[#333] to-[#fff]"></view>
  16. <view @click="isEdit = !isEdit" class="text-[#333] text-[28rpx]">{{ isEdit ? '完成' : '管理' }}</view>
  17. </view>
  18. </view>
  19. </view>
  20. <mescroll-body ref="mescrollRef" top="76" bottom="168" @init="mescrollInit" :down="{ use: false }" @up="getBrowseListFn">
  21. <view v-if="browseList.length">
  22. <view class="bg-[#fff] mb-[20rpx] pt-[30rpx] px-[20rpx]" v-for="(item,index) in browseList"
  23. :key="index">
  24. <view class="flex items-center h-[34rpx] mb-[20rpx]">
  25. <view class="self-center w-[58rpx] flex items-center" v-if="isEdit" @click.stop="isSelectGroup(item)">
  26. <view class="bg-[#fff] w-[34rpx] h-[34rpx] rounded-[17rpx] flex items-center justify-center">
  27. <text class=" iconfont text-primary text-[34rpx] w-[34rpx] h-[34rpx] rounded-[17rpx] overflow-hidden shrink-0"
  28. :class="{ 'iconxuanze1':item.checked,'bg-[#F5F5F5]':!item.checked}"></text>
  29. </view>
  30. </view>
  31. <view class="text-[28rpx] font-500 text-[#333] ">{{ item.date }}</view>
  32. </view>
  33. <view class="flex flex-wrap">
  34. <view class="w-[230rpx] mb-[20rpx]" :class="{'mr-[10rpx]': (subIndex + 1) % 3 }" v-for="(subItem,subIndex) in item.list" :key="subIndex" @click="toDetail(subItem)">
  35. <view class="relative w-[230rpx] h-[230rpx] rounded-[var(--goods-rounded-mid)] overflow-hidden mb-[10rpx]">
  36. <u--image width="230rpx" height="230rpx" :radius="'var(--goods-rounded-mid)'" :src="img(subItem.goods_cover_thumb_mid ? subItem.goods_cover_thumb_mid : '')" mode="aspectFill">
  37. <template #error>
  38. <image class="w-[230rpx] h-[230rpx] rounded-[var(--goods-rounded-mid)] overflow-hidden" :src="img('static/resource/images/diy/shop_default.jpg')" mode="aspectFill" />
  39. </template>
  40. </u--image>
  41. <view v-if="subItem.status == 0 " class="absolute left-0 top-0 w-[230rpx] h-[230rpx] leading-[230rpx] text-center " style="background-color: rgba(0,0,0,0.3);">
  42. <text class="text-[#fff] text-[28rpx]">已失效</text>
  43. </view>
  44. <view class="absolute top-0 left-0 right-0 bottom-0 p-[10rpx] flex justify-end items-start z-100"
  45. v-if="isEdit" @click.stop="changeItem(item,subItem)">
  46. <view class="bg-[#fff] w-[32rpx] h-[32rpx] rounded-[17rpx] flex items-center justify-center">
  47. <text class="iconfont text-primary text-[34rpx] w-[34rpx] h-[34rpx] rounded-[17rpx] overflow-hidden flex-shrink-0"
  48. :class="{ 'iconxuanze1':subItem.checked,'bg-[#F5F5F5]':!subItem.checked}"></text>
  49. </view>
  50. </view>
  51. </view>
  52. <view class="text-[var(--price-text-color)] price-font">
  53. <text class="text-[24rpx] font-500">¥</text>
  54. <text class="text-[40rpx] font-500">{{ parseFloat(subItem.price).toFixed(2).split('.')[0] }}</text>
  55. <text class="text-[24rpx] font-500">.{{ parseFloat(subItem.price).toFixed(2).split('.')[1] }}</text>
  56. </view>
  57. </view>
  58. </view>
  59. </view>
  60. </view>
  61. <mescroll-empty v-if="!browseList.length && loading" :option="{tip : '暂无浏览的商品'}"></mescroll-empty>
  62. </mescroll-body>
  63. <view v-if="browseList.length && isEdit" class="fixed left-0 right-0 bottom-0 z-200 bg-[#fff] pb-ios">
  64. <view v-if="checkedNum"
  65. class="h-[66rpx] flex items-center justify-between pl-[30rpx] pr-[20rpx] border-0 border-b-[1rpx] border-solid border-[#f6f6f6]">
  66. <view class="text-[24rpx]">
  67. <text>已选</text>
  68. <text class="text-primary">{{ checkedNum }}</text>
  69. <text>件宝贝</text>
  70. </view>
  71. <view class="text-[24rpx] text-[#999]" @click="clearBrowseFn">一键清空宝贝足迹</view>
  72. </view>
  73. <view class="flex h-[100rpx] items-center justify-between pl-[30rpx] pr-[20rpx]">
  74. <view class="flex items-center" @click="allChange">
  75. <text class="self-center iconfont text-primary text-[34rpx] mr-[10rpx] w-[34rpx] h-[34rpx] rounded-[17rpx] overflow-hidden flex-shrink-0"
  76. :class="{'iconxuanze1': isSelectAll, 'bg-color': !isSelectAll } "></text>
  77. <text class="font-400 text-[#303133] text-[26rpx]">全选</text>
  78. </view>
  79. <button class="w-[180rpx] h-[70rpx] font-500 text-[26rpx] leading-[70rpx] !text-[#fff] m-0 rounded-full primary-btn-bg remove-border"
  80. @click="deleteBrowseFn">删除</button>
  81. </view>
  82. </view>
  83. <!-- 时间选择 -->
  84. <select-date ref="selectDateRef" @confirm="confirmFn" />
  85. </view>
  86. </template>
  87. <script setup lang="ts">
  88. import { ref, computed } from 'vue'
  89. import { img, redirect } from '@/utils/common';
  90. import { getBrowse, delBrowse } from '@/addon/mall/api/goods';
  91. import selectDate from '@/components/select-date/select-date.vue';
  92. import MescrollBody from '@/components/mescroll/mescroll-body/mescroll-body.vue';
  93. import MescrollEmpty from '@/components/mescroll/mescroll-empty/mescroll-empty.vue';
  94. import useMescroll from '@/components/mescroll/hooks/useMescroll.js';
  95. import { onPageScroll, onReachBottom } from '@dcloudio/uni-app';
  96. const { mescrollInit, downCallback, getMescroll } = useMescroll(onPageScroll, onReachBottom);
  97. const isEdit = ref(false)
  98. let loading = ref<boolean>(false);
  99. const optionLoading = ref(false)
  100. const browseTotal = ref<number>(0);
  101. let browseList = ref<any>([]);
  102. const create_time = ref([])
  103. const getBrowseListFn = (mescroll: any) => {
  104. loading.value = false;
  105. let data: object = {
  106. page: mescroll.num,
  107. limit: mescroll.size,
  108. date: create_time.value
  109. };
  110. getBrowse(data).then((res: any) => {
  111. browseTotal.value = res.data.total;
  112. let newArr = (res.data.data as Array<Object>);
  113. //设置列表数据
  114. if (Number(mescroll.num) === 1) {
  115. browseList.value = []; //如果是第一页需手动制空列表
  116. }
  117. // 按日期分组
  118. const groupedData = newArr.reduce((acc: any, item: any) => {
  119. const date = item.browse_time.split(' ')[0]; // 提取日期部分
  120. if (!acc[date]) {
  121. acc[date] = [];
  122. }
  123. acc[date].push(item);
  124. return acc;
  125. }, {});
  126. // 转换为所需格式
  127. const formattedData = Object.keys(groupedData).map(date => ({
  128. date: date,
  129. list: groupedData[date]
  130. }));
  131. formattedData.forEach((item: any) => {
  132. item.checked = false;
  133. item.list.forEach((subItem: any) => {
  134. subItem.checked = false; // 初始化选中状态
  135. });
  136. });
  137. // 合并相同日期的数据
  138. formattedData.forEach((newItem: any) => {
  139. const existingItemIndex = browseList.value.findIndex((item: any) => item.date === newItem.date);
  140. if (existingItemIndex !== -1) {
  141. // 合并到已有的日期数据中
  142. browseList.value[existingItemIndex].list = [...browseList.value[existingItemIndex].list, ...newItem.list];
  143. } else {
  144. // 新增日期数据
  145. browseList.value.push(newItem);
  146. }
  147. });
  148. mescroll.endSuccess(newArr.length);
  149. loading.value = true;
  150. }).catch(() => {
  151. loading.value = true;
  152. mescroll.endErr(); // 请求失败, 结束加载
  153. })
  154. }
  155. // 选择数量
  156. const checkedNum = computed(() => {
  157. let num = 0
  158. browseList.value.forEach((item: any) => {
  159. item.list.forEach((subItem: any) => {
  160. subItem.checked && (num += 1)
  161. })
  162. })
  163. return num
  164. })
  165. /**
  166. * 全选
  167. */
  168. //判断是否全选状态
  169. const isSelectAll = ref(false)
  170. const isallclick = () => {
  171. const isActive = browseList.value.every((item: any) => {
  172. return item.checked
  173. })
  174. if (isActive) {
  175. isSelectAll.value = true
  176. } else {
  177. isSelectAll.value = false
  178. }
  179. }
  180. // 选择每天的
  181. const isSelectGroup = (data: any) => {
  182. data.checked = !data.checked
  183. data.list.forEach((item: any) => {
  184. item.checked = data.checked
  185. })
  186. isallclick()
  187. }
  188. // 全选
  189. const allChange = () => {
  190. isSelectAll.value = !isSelectAll.value
  191. browseList.value.forEach((item: any) => {
  192. item.checked = isSelectAll.value
  193. item.list.forEach((subItem: any) => {
  194. subItem.checked = isSelectAll.value
  195. })
  196. })
  197. }
  198. // 选择单个商品
  199. const changeItem = (data: any, value: any) => {
  200. value.checked = !value.checked
  201. const isActive = data.list.every((item: any) => {
  202. return item.checked
  203. })
  204. if (isActive) {
  205. data.checked = true
  206. } else {
  207. data.checked = false
  208. }
  209. isallclick()
  210. }
  211. // 删除
  212. const deleteBrowseFn = () => {
  213. if (!checkedNum.value) {
  214. uni.showToast({ title: '还没有选择商品', icon: 'none' })
  215. return
  216. }
  217. if (optionLoading.value) return
  218. optionLoading.value = true
  219. const ids: any = []
  220. browseList.value.forEach((item: any) => {
  221. item.list.forEach((subItem: any) => {
  222. subItem.checked && ids.push(subItem.goods_id)
  223. })
  224. })
  225. delBrowse({ goods_ids: ids }).then((res: any) => {
  226. optionLoading.value = false
  227. getMescroll().resetUpScroll();
  228. })
  229. }
  230. const clearBrowseFn = () => {
  231. if (optionLoading.value) return
  232. optionLoading.value = true
  233. const ids: any = []
  234. browseList.value.forEach((item: any) => {
  235. item.list.forEach((subItem: any) => {
  236. ids.push(subItem.goods_id)
  237. })
  238. })
  239. delBrowse({ goods_ids: ids }).then((res: any) => {
  240. getMescroll().resetUpScroll();
  241. optionLoading.value = false
  242. })
  243. }
  244. //日期筛选
  245. const selectDateRef = ref<any>(null)
  246. const handleSelect = () => {
  247. selectDateRef.value.show = true
  248. }
  249. // 确定时间筛选
  250. const confirmFn = (data: any) => {
  251. create_time.value = data;
  252. browseList.value = []
  253. getMescroll().resetUpScroll();
  254. }
  255. const toDetail = (data: any) => {
  256. redirect({ url: '/addon/mall/pages/goods/detail', param: { goods_id: data.goods_id } })
  257. }
  258. </script>
  259. <style lang="scss" scoped>
  260. .bg-color {
  261. background-color: #f5f5f5;
  262. }
  263. </style>