index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. <template>
  2. <view style="padding-bottom: 32rpx;">
  3. <view class='searchGood'>
  4. <view class='search acea-row row-between-wrapper'>
  5. <view class='input acea-row row-between-wrapper'>
  6. <text class='iconfont icon-sousuo' style="color:#999;"></text>
  7. <input type='text' confirm-type="search" :value='searchValue' :focus="focus" placeholder='点击搜索商品' placeholder-class='placeholder'
  8. @input="setValue" @confirm="searchBut" />
  9. </view>
  10. <view class='bnt' @tap='searchBut'>搜索</view>
  11. </view>
  12. <!-- <view class='title'>热门搜索</view>-->
  13. <!-- <view class='list acea-row'>-->
  14. <!-- <block v-for="(item, index) in hotSearchList" :key="index">-->
  15. <!-- <view class='item' @tap='setHotSearchValue(item.title)'>{{ item.title }}</view>-->
  16. <!-- </block>-->
  17. <!-- </view>-->
  18. <view class='line'></view>
  19. <!-- <goodList :bastList="calculatedProducts" v-if="bastList.length > 0"></goodList> -->
  20. <view class="index-product-wrapper">
  21. <view
  22. class="list-box animated"
  23. :class="bastList.length > 0 ? 'fadeIn on' : ''"
  24. >
  25. <view
  26. class="item"
  27. v-for="(item, index) in calculatedProducts"
  28. :key="index"
  29. @click="goDetail(item)"
  30. >
  31. <view class="pictrue">
  32. <image :src="item.image" mode=""></image>
  33. </view>
  34. <view class="text-info">
  35. <view class="title" >
  36. <view class="text line1">{{ item.storeName }}</view>
  37. <view class="weight">{{ item.weight }}g</view>
  38. </view>
  39. <view class="bottom-row">
  40. <!-- <text class="price">工费: {{ item.price }}/克</text> -->
  41. <text class="price">¥ {{ item.totalPrice }}</text>
  42. <text class="sales" style="color:#666;">
  43. 销量:{{ Number(item.sales || 0) + Number(item.ficti || 0) }}件
  44. </text>
  45. <!-- <view class="txt">券</view> -->
  46. </view>
  47. <view class="bottom-row">
  48. <!-- <text class="price">工费: {{ item.price }}/克</text> -->
  49. <text class="sales">工费:¥{{ item.totalLaborCost }}</text>
  50. <text class="sales">
  51. 附加费:¥{{ item.additionalAmount }}
  52. </text>
  53. <!-- <view class="txt">券</view> -->
  54. </view>
  55. <template v-if="item?.merchant?.id && merchantNameShow()">
  56. <view class="merchantInfo" @click.stop="toMerchant(item.merchant.id)">
  57. <image class="merchantLogo" :src="item.merchant.merchantLogo" mode="scaleToFill"></image>
  58. <text class="merchantName">{{item.merchant.merchantName}}</text>
  59. <uni-icons style="margin-left: 10rpx;" type="right" size="16" color="#999999"></uni-icons>
  60. </view>
  61. </template>
  62. </view>
  63. </view>
  64. </view>
  65. </view>
  66. <view class='loadingicon acea-row row-center-wrapper' v-if="bastList.length > 0">
  67. <text class='loading iconfont icon-jiazai' :hidden='loading == false'></text>{{ loadTitle }}
  68. </view>
  69. </view>
  70. <view class='noCommodity'>
  71. <view class='pictrue' v-if="bastList.length == 0 && isbastList">
  72. <image :src="HTTP_REQUEST_URL_IMG+'noSearch.png'"></image>
  73. </view>
  74. <!-- <recommend :hostProduct='hostProduct' v-if="bastList.length == 0"></recommend>-->
  75. </view>
  76. </view>
  77. </template>
  78. <script setup>
  79. import {computed, ref} from 'vue'
  80. import { onShow, onReachBottom,onLoad } from '@dcloudio/uni-app'
  81. import { getSearchKeyword, getProductslist, getProductHot } from '@/api/store.js'
  82. import goodList from '@/components/goodList'
  83. import recommend from '@/components/recommend'
  84. import { HTTP_REQUEST_URL_IMG } from "@/config/app";
  85. // 获取实时金价
  86. import useRealGoldPrice from "@/hooks/useRealGoldPrice";
  87. import { useAppStore } from "@/stores/app";
  88. const appStore = useAppStore();
  89. // 响应式数据
  90. const hostProduct = ref([])
  91. const searchValue = ref('')
  92. const focus = ref(true)
  93. const bastList = ref([])
  94. const hotSearchList = ref([])
  95. const limit = ref(8)
  96. const page = ref(1)
  97. const loading = ref(false)
  98. const loadend = ref(false)
  99. const loadTitle = ref('加载更多')
  100. const hotPage = ref(1)
  101. const isScroll = ref(true)
  102. const isbastList = ref(false)
  103. const query = ref({})
  104. const merchantId = ref('')
  105. // 实时价格处理
  106. const {
  107. realGoldprice, // 黄金实时销售价(基础)
  108. realPtprice, // 铂金实时销售价(基础)
  109. realAgprice, // 白银实时销售价(基础)
  110. } = useRealGoldPrice({});
  111. // Navigation to detail page
  112. const goDetail = async (item) => {
  113. try {
  114. uni.navigateTo({
  115. url: `/pages/goods/goods_details/index?id=${item.id}`,
  116. });
  117. } catch (err) {
  118. console.error('Navigation error:', err);
  119. }
  120. };
  121. const merchantNameShow = () => {
  122. if(!appStore.userInfo ||(appStore.userInfo && !appStore.userInfo.merchant && !appStore.merchantId && !appStore.userInfo.lastVisitedMerchantId)){
  123. return true;
  124. }else{
  125. return false;
  126. }
  127. }
  128. const toMerchant = (merchantId) => {
  129. uni.navigateTo({ url:"/pages/merchantCenters/merchant?merchantId="+merchantId });
  130. }
  131. const calculatedProducts = computed(() => {
  132. // 计算逻辑与原代码一致,但基于响应式数据 products
  133. return bastList.value.map((product) => {
  134. // 1. 将price字符串转为数字
  135. const price = Number(product.price);
  136. // 2. 计算乘积
  137. const total = (price + product.sales) * realGoldprice.value;
  138. // 3. 向上取整到两位小数(先放大100倍取整,再缩小100倍)
  139. const roundedTotal = Math.ceil(total * 100) / 100;
  140. // 4. 格式化保留两位小数
  141. const formattedTotal = roundedTotal.toFixed(2);
  142. const totalLaborCost = Number(product.totalLaborCost);
  143. const additionalAmount = Number(product.additionalAmount);
  144. const totalPrice = (totalLaborCost+additionalAmount).toFixed(2);
  145. return {
  146. ...product,
  147. calculatedTotal: formattedTotal, // 新增计算结果字段
  148. totalPrice
  149. };
  150. });
  151. });
  152. // 获取热搜
  153. function getRoutineHotSearch() {
  154. getSearchKeyword().then(res => {
  155. hotSearchList.value = res.data
  156. })
  157. }
  158. // 获取商品列表
  159. function getProductList() {
  160. if (loadend.value || loading.value) return
  161. loading.value = true
  162. loadTitle.value = ''
  163. getProductslist({
  164. ...query.value,
  165. keyword: searchValue.value,
  166. page: page.value,
  167. limit: limit.value,
  168. merchantId:merchantId.value,
  169. }).then(res => {
  170. const list = res.data.list
  171. const isLoadend = list.length < limit.value
  172. // 合并数组
  173. bastList.value = (bastList.value || []).concat(list)
  174. loading.value = false
  175. loadend.value = isLoadend
  176. loadTitle.value = isLoadend ? "我是有底线的" : "加载更多"
  177. page.value += 1
  178. isbastList.value = true
  179. }).catch(() => {
  180. loading.value = false
  181. loadTitle.value = '加载更多'
  182. })
  183. }
  184. // 获取热门商品
  185. function getHostProduct() {
  186. if (!isScroll.value) return
  187. getProductHot(hotPage.value, limit.value).then(res => {
  188. isScroll.value = res.data.list.length >= limit.value
  189. hostProduct.value = hostProduct.value.concat(res.data.list)
  190. hotPage.value += 1
  191. })
  192. }
  193. // 设置热搜值
  194. function setHotSearchValue(val) {
  195. searchValue.value = val
  196. page.value = 1
  197. loadend.value = false
  198. bastList.value = []
  199. getProductList()
  200. }
  201. // 输入框赋值
  202. function setValue(event) {
  203. searchValue.value = event.detail.value;
  204. }
  205. // 搜索按钮
  206. function searchBut() {
  207. focus.value = false
  208. if (searchValue.value.length > 0) {
  209. page.value = 1
  210. loadend.value = false
  211. bastList.value = []
  212. uni.showLoading({ title: '正在搜索中' })
  213. getProductList()
  214. uni.hideLoading()
  215. } else {
  216. // 这里假设 $util.Tips 已全局挂载
  217. uni.$u.toast('请输入要搜索的商品')
  218. }
  219. }
  220. // 生命周期
  221. onShow(() => {
  222. getRoutineHotSearch()
  223. getHostProduct();
  224. })
  225. onLoad((options)=>{
  226. query.value = options || {};
  227. merchantId.value =query.value.merchantId || appStore.merchantId||appStore.userInfo?.merchant?.id||'';
  228. if(options && options.cid){
  229. getProductList()
  230. }
  231. })
  232. onReachBottom(() => {
  233. if (bastList.value.length > 0) {
  234. getProductList()
  235. } else {
  236. getHostProduct()
  237. }
  238. })
  239. </script>
  240. <style lang="scss">
  241. page {
  242. // margin-top: var(--status-bar-height);
  243. background-color: #F9F7F0;
  244. height: 100%;
  245. }
  246. .searchGood .search {
  247. padding-left: 30rpx;
  248. background-color: #fff !important;
  249. }
  250. .searchGood .search {
  251. padding: 16rpx;
  252. }
  253. .searchGood .search .input {
  254. width: 598rpx;
  255. background-color: #F9F7F0;
  256. border-radius: 16rpx;
  257. padding: 0 35rpx;
  258. box-sizing: border-box;
  259. height: 66rpx;
  260. }
  261. .searchGood .search .input input {
  262. width: 472rpx;
  263. font-size: 26rpx;
  264. }
  265. .searchGood .search .input .placeholder {
  266. color: #bbb;
  267. }
  268. .searchGood .search .input .iconfont {
  269. color: #000;
  270. font-size: 35rpx;
  271. }
  272. .searchGood .search .bnt {
  273. width: 120rpx;
  274. text-align: center;
  275. height: 66rpx;
  276. line-height: 66rpx;
  277. font-size: 30rpx;
  278. color: #282828;
  279. }
  280. .searchGood .title {
  281. // font-size: 28rpx;
  282. // color: #999;
  283. // margin: 50rpx 30rpx 25rpx 30rpx;
  284. }
  285. .searchGood .list {
  286. padding-left: 10rpx;
  287. }
  288. .searchGood .list .item {
  289. font-size: 26rpx;
  290. color: #454545;
  291. padding: 0 21rpx;
  292. height: 60rpx;
  293. border-radius: 30rpx;
  294. line-height: 60rpx;
  295. border: 1rpx solid #aaa;
  296. margin: 0 0 20rpx 20rpx;
  297. }
  298. .searchGood .line {
  299. // border-bottom: 1rpx solid #eee;
  300. margin: 20rpx 30rpx 0 30rpx;
  301. }
  302. .index-product-wrapper {
  303. padding: 0 16rpx;
  304. margin-bottom: 72rpx; /* 为自定义 tabBar 留出空间 */
  305. // min-height: 700rpx;
  306. //background: #fff;
  307. &.on {
  308. min-height: 1500rpx;
  309. }
  310. .list-box {
  311. display: flex;
  312. flex-wrap: wrap;
  313. justify-content: space-between;
  314. .item {
  315. width: 48.99%;
  316. //height: 490rpx;
  317. background-color: #ffffff;
  318. // box-shadow: 0rpx 3rpx 13rpx 0rpx rgba(0, 0, 0, 0.13);
  319. border-radius: 16rpx;
  320. margin-bottom: 16rpx;
  321. overflow: hidden;
  322. display: flex;
  323. flex-direction: column;
  324. justify-content: space-between;
  325. .pictrue {
  326. position: relative;
  327. image {
  328. width: 100%;
  329. height: 330rpx;
  330. }
  331. }
  332. .text-info {
  333. padding: 16rpx 8rpx;
  334. .title {
  335. color: #333;
  336. display: flex;
  337. align-items: center;
  338. justify-content: space-between;
  339. .tip {
  340. width: 61rpx;
  341. height: auto;
  342. font-size: 22rpx;
  343. display: flex;
  344. justify-content: center;
  345. align-items: center;
  346. color: #ffffff;
  347. position: relative;
  348. margin-right: 5rpx;
  349. background-color: #ef4800;
  350. border-radius: 5rpx;
  351. }
  352. .text{
  353. width: 80%;
  354. font-weight: bold;
  355. }
  356. .weight{
  357. background-color: rgba(197, 128, 3, 0.10);
  358. color: #C58003;
  359. font-size: 24rpx;
  360. padding: 8rpx 16rpx;
  361. border-radius: 8rpx;
  362. float: right;
  363. }
  364. }
  365. .bottom-row {
  366. color: $theme-color;
  367. display: flex;
  368. justify-content: space-between;
  369. align-items: center;
  370. font-size: 28rpx;
  371. margin: 10rpx 0 0;
  372. .price {
  373. padding-bottom: 4rpx;
  374. font-weight: 800;
  375. white-space: nowrap;
  376. font-size: 28rpx;
  377. color: #f16327;
  378. }
  379. .sales {
  380. font-size: 22rpx;
  381. white-space: nowrap;
  382. flex-shrink: 0;
  383. color: #999999;
  384. }
  385. }
  386. .merchantInfo{
  387. display: flex;
  388. align-items: center;
  389. height: 40rpx;
  390. margin-top: 16rpx;
  391. }
  392. .merchantLogo{
  393. width: 40rpx;
  394. height: 40rpx;
  395. border-radius: 50%;
  396. margin-right: 8rpx;
  397. }
  398. .merchantName{
  399. font-size: 24rpx;
  400. color: #333;
  401. }
  402. }
  403. }
  404. &.on {
  405. display: flex;
  406. }
  407. }
  408. }
  409. </style>