product.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. // 商品相关接口
  2. //引入封装好的的get请求方法
  3. import { get, post } from '@/core/http/http-client'
  4. import { buildRestPath } from '@/core/config/api-paths'
  5. // 商品图片 CDN 配置
  6. import { buildOssUrl } from '@/core/media/oss-url'
  7. /**
  8. * 商品列表(分页)
  9. * 对接 88live 的 /wanlshop/product/lists 接口
  10. *
  11. */
  12. export async function pullMallProductPage({
  13. type = 'goods',
  14. page = 1,
  15. search = '',
  16. filter = {},
  17. op = {},
  18. sort = 'weigh',
  19. order = 'asc'
  20. } = {}) {
  21. const params = { type, page, sort, order }
  22. if (search) params.search = search
  23. if (Object.keys(filter).length) params.filter = JSON.stringify(filter)
  24. if (Object.keys(op).length) params.op = JSON.stringify(op)
  25. const { data, raw } = await get(buildRestPath('product/lists'), params)
  26. const body = data || {}
  27. const rows = (Array.isArray(body.data) ? body.data : []).map(item => {
  28. if (!item || item.id == null) return null
  29. return {
  30. id: item.id,
  31. title: item.title || item.name || '',
  32. image: buildOssUrl(item.image),
  33. price: item.price || '0.00',
  34. market_price: item.market_price || '',
  35. sales: Number(item.sales || 0),
  36. shop_id: item.shop_id,
  37. shop: item.shop || null,
  38. comment: Number(item.comment || 0),
  39. praise: Number(item.praise || 0),
  40. isLive: item.isLive || false,
  41. raw: item
  42. }
  43. }).filter(Boolean)
  44. return {
  45. rows,
  46. total: Number(body.total || rows.length),
  47. currentPage: Number(body.current_page || page),
  48. lastPage: Number(body.last_page || 1)
  49. }
  50. }
  51. /**
  52. * 商品筛选条件(品牌/属性/价格区间)
  53. * 对接 88live 的 /wanlshop/product/drawer 接口
  54. */
  55. export async function pullMallFilterDrawer({ type = 'goods', search = '', category_id = '' } = {}) {
  56. const params = { type }
  57. if (search) params.search = search
  58. if (category_id) params.category_id = category_id
  59. const { data } = await get(buildRestPath('product/drawer'), params)
  60. return {
  61. brand: (data.brand || []).map(item => ({
  62. id: item.id,
  63. name: item.name,
  64. choice: false
  65. })),
  66. attribute: (data.attribute || []).map(item => ({
  67. name: item.name,
  68. value: (item.value || []).map(vo => ({
  69. name: vo.name,
  70. choice: false
  71. })),
  72. fold: false
  73. })),
  74. price: { low: '', high: '' }
  75. }
  76. }
  77. /**
  78. * 商城初始化数据(分类+搜索热词+首页模块)
  79. * 对接 88live 的 /wanlshop/common/init 接口
  80. * 原始调用在 88live/store/modules/common.js 第108行
  81. */
  82. export async function pullMallInit() {
  83. const { data } = await get(buildRestPath('common/init'))
  84. return {
  85. categoryModules: (data.modulesData?.categoryModules || []).map(item => ({
  86. id: item.id,
  87. name: item.name,
  88. image: buildOssUrl(item.image),
  89. childlist: (item.childlist || []).map(child => ({
  90. id: child.id,
  91. name: child.name,
  92. image: buildOssUrl(child.image)
  93. }))
  94. })),
  95. searchModules: data.modulesData?.searchModules || [],
  96. homeModules: data.modulesData?.homeModules || {}
  97. }
  98. }
  99. /**
  100. * 分类商品列表(含秒杀/拼团/商品)
  101. * 对接 88live 的 /wanlshop/common/category 接口
  102. * 原始调用在 88live/components/wanl-shop/category.vue 第117行
  103. */
  104. export async function pullMallCategoryGoods({ id, page = 1 } = {}) {
  105. const { data } = await get(buildRestPath('common/category'), { id, page })
  106. const goods = data.goods || {}
  107. return {
  108. goods: (Array.isArray(goods.data) ? goods.data : []).map(item => {
  109. if (!item || item.id == null) return null
  110. return {
  111. id: item.id,
  112. title: item.title || '',
  113. image: buildOssUrl(item.image),
  114. price: item.price || '0.00',
  115. market_price: item.market_price || '',
  116. sales: Number(item.sales || 0),
  117. shop_id: item.shop_id,
  118. shop: item.shop || null,
  119. comment: Number(item.comment || 0),
  120. praise: Number(item.praise || 0),
  121. isLive: item.isLive || false,
  122. raw: item
  123. }
  124. }).filter(Boolean),
  125. currentPage: Number(goods.current_page || page),
  126. lastPage: Number(goods.last_page || 1),
  127. total: Number(goods.total || 0),
  128. seckill: data.seckill || [],
  129. groups: data.groups || []
  130. }
  131. }
  132. /**
  133. * 商品详情
  134. * 对接 88live 的 /wanlshop/product/goods 接口
  135. * 原始调用在 88live/pages/product/goods.vue 第734行
  136. */
  137. export async function pullMallGoodsDetail({ id } = {}) {
  138. const { data } = await get(buildRestPath('product/goods'), { id })
  139. if (typeof data.images === 'string') {
  140. data.images = data.images.split(',').filter(Boolean).map(img => buildOssUrl(img))
  141. } else if (Array.isArray(data.images)) {
  142. data.images = data.images.map(img => buildOssUrl(img))
  143. }
  144. if (!Array.isArray(data.images) || data.images.length === 0) {
  145. data.images = data.image ? [buildOssUrl(data.image)] : []
  146. }
  147. data.image = buildOssUrl(data.image)
  148. if (data.content) {
  149. data.content = data.content.replace(
  150. /<img [^>]*src=['"]([^'"]+)[^>]*>/gi,
  151. (match, capture) => `<img style="display:block;max-width:100%;" src="${buildOssUrl(capture)}">`
  152. )
  153. }
  154. if (data.sku && data.sku.length > 0) {
  155. data.sku.forEach(item => {
  156. if (item.thumbnail) item.thumbnail = buildOssUrl(item.thumbnail)
  157. })
  158. const prices = data.sku.map(s => Number(s.price))
  159. const marketPrices = data.sku.map(s => Number(s.market_price))
  160. const minP = Math.min(...prices)
  161. const maxP = Math.max(...prices)
  162. data.interval_price = minP === maxP ? minP.toFixed(2) : `${minP}-${maxP.toFixed(2)}`
  163. data.market_price = Math.max(...marketPrices).toFixed(2)
  164. } else {
  165. data.interval_price = data.price
  166. }
  167. if (data.shop) {
  168. if (data.shop.avatar) data.shop.avatar = buildOssUrl(data.shop.avatar)
  169. }
  170. if (data.shop_recommend && Array.isArray(data.shop_recommend)) {
  171. data.shop_recommend.forEach(item => {
  172. if (item.image) item.image = buildOssUrl(item.image)
  173. })
  174. }
  175. if (data.comment_list) {
  176. if (Array.isArray(data.comment_list.data)) {
  177. data.comment_list.data.forEach(item => {
  178. if (item.user && item.user.avatar) item.user.avatar = buildOssUrl(item.user.avatar)
  179. if (Array.isArray(item.images)) {
  180. item.images = item.images.map(img => buildOssUrl(img))
  181. }
  182. })
  183. }
  184. }
  185. return data
  186. }
  187. /**
  188. * 商品SKU库存查询
  189. * 对接 88live 的 /wanlshop/product/stock 接口
  190. * 原始调用在 88live/pages/product/goods.vue 第811行
  191. */
  192. export async function pullMallGoodsStock({ sku_id } = {}) {
  193. const { data } = await get(buildRestPath('product/stock'), { sku_id })
  194. return data
  195. }
  196. /**
  197. * 商品收藏/取消收藏
  198. * 对接 88live 的 /wanlshop/product/follow 接口
  199. * 原始调用在 88live/pages/product/goods.vue 第960行
  200. */
  201. export async function pullMallGoodsFollow({ id } = {}) {
  202. const { data } = await post(buildRestPath('product/follow'), { id })
  203. return data
  204. }
  205. /**
  206. * 热门搜索列表
  207. * 对接 88live 的 /wanlshop/common/searchList 接口
  208. * 原始调用在 88live/pages/page/search.vue 第141行
  209. */
  210. export async function pullMallSearchList() {
  211. const { data } = await get(buildRestPath('common/searchList'))
  212. return data || []
  213. }
  214. /**
  215. * 实时搜索(关键词建议+分类匹配)
  216. * 对接 88live 的 /wanlshop/common/search 接口
  217. * 原始调用在 88live/pages/page/search.vue 第149行
  218. */
  219. export async function pullMallSearch({ search = '' } = {}) {
  220. const { data } = await get(buildRestPath('common/search'), { search })
  221. return {
  222. searchList: data.searchList || [],
  223. categoryList: data.categoryList || []
  224. }
  225. }
  226. /**
  227. * 保存搜索关键词
  228. * 对接 88live 的 /wanlshop/common/setSearch 接口
  229. * 原始调用在 88live/pages/page/search.vue 第157行
  230. */
  231. export async function pullMallSetSearch({ keywords = '' } = {}) {
  232. await get(buildRestPath('common/setSearch'), { keywords })
  233. }
  234. /**
  235. * 猜你喜欢(首页推荐商品列表,分页)
  236. * 对接 88live 的 /wanlshop/product/likes 接口
  237. * 原始调用在 88live/components/wanl-page-likes/wanl-page-likes.vue
  238. * 用于首页 homeModules 中 type=likes 的模块
  239. */
  240. export async function pullMallLikes({ page = 1 } = {}) {
  241. const { data } = await get(buildRestPath('product/likes'), { page })
  242. return {
  243. rows: (Array.isArray(data.data) ? data.data : []).map(item => {
  244. if (!item || item.id == null) return null
  245. return {
  246. id: item.id,
  247. title: item.title || item.name || '',
  248. image: buildOssUrl(item.image),
  249. price: item.price || '0.00',
  250. market_price: item.market_price || '',
  251. sales: Number(item.sales || 0),
  252. shop_id: item.shop_id,
  253. shop: item.shop || null,
  254. comment: Number(item.comment || 0),
  255. praise: Number(item.praise || 0),
  256. isLive: item.isLive || false,
  257. raw: item
  258. }
  259. }).filter(Boolean),
  260. currentPage: data.current_page || 1,
  261. lastPage: data.last_page || 1
  262. }
  263. }
  264. /**
  265. * 首页指定商品列表(根据商品ID批量查询)
  266. * 对接 88live 的 /wanlshop/page/goods 接口
  267. * 原始调用在 88live/components/wanl-page-goods/wanl-page-goods.vue
  268. * 用于首页 homeModules 中 type=goods 的模块
  269. * ids 参数为逗号分隔的商品ID字符串
  270. */
  271. export async function pullMallPageGoods({ ids = '' } = {}) {
  272. const { data } = await get(buildRestPath('page/goods'), { ids })
  273. return Array.isArray(data) ? data.map(item => {
  274. if (!item || item.id == null) return null
  275. return {
  276. id: item.id,
  277. title: item.title || item.name || '',
  278. image: buildOssUrl(item.image),
  279. price: item.price || '0.00',
  280. market_price: item.market_price || '',
  281. sales: Number(item.sales || 0),
  282. shop_id: item.shop_id,
  283. shop: item.shop || null,
  284. comment: Number(item.comment || 0),
  285. praise: Number(item.praise || 0),
  286. isLive: item.isLive || false,
  287. raw: item
  288. }
  289. }).filter(Boolean) : []
  290. }
  291. /**
  292. * 优惠券查询(根据商品和价格查询可用优惠券)
  293. * 对接 88live 的 /wanlshop/coupon/query 接口
  294. * 原始调用在 88live/pages/product/goods.vue 第830行
  295. * 当SKU价格变化时重新查询
  296. */
  297. export async function pullMallCouponQuery({ shop_id, goods_id, shop_category_id, price } = {}) {
  298. const { data } = await post(buildRestPath('coupon/query'), { shop_id, goods_id, shop_category_id, price })
  299. return data || []
  300. }
  301. /**
  302. * 领取优惠券
  303. * 对接 88live 的 /wanlshop/coupon/receive 接口
  304. * 原始调用在 88live/pages/product/goods.vue 第844行
  305. */
  306. export async function pullMallCouponReceive({ id } = {}) {
  307. const { data } = await post(buildRestPath('coupon/receive'), { id })
  308. return data
  309. }
  310. /**
  311. * 详情页猜你喜欢(带pages=goods参数)
  312. * 对接 88live 的 /wanlshop/product/likes?pages=goods 接口
  313. * 原始调用在 88live/pages/product/goods.vue 第822行
  314. * 和首页猜你喜欢是同一个接口,但多了pages参数
  315. */
  316. export async function pullMallGoodsLikes({ page = 1 } = {}) {
  317. const { data } = await get(buildRestPath('product/likes'), { pages: 'goods', page })
  318. return {
  319. rows: (Array.isArray(data.data) ? data.data : []).map(item => {
  320. if (!item || item.id == null) return null
  321. return {
  322. id: item.id,
  323. title: item.title || item.name || '',
  324. image: buildOssUrl(item.image),
  325. price: item.price || '0.00',
  326. market_price: item.market_price || '',
  327. sales: Number(item.sales || 0),
  328. shop_id: item.shop_id,
  329. shop: item.shop || null,
  330. comment: Number(item.comment || 0),
  331. praise: Number(item.praise || 0),
  332. isLive: item.isLive || false,
  333. raw: item
  334. }
  335. }).filter(Boolean),
  336. currentPage: data.current_page || 1,
  337. lastPage: data.last_page || 1
  338. }
  339. }
  340. /**
  341. * 商品评论列表
  342. * 对接 88live 的 /wanlshop/product/comment 接口
  343. */
  344. export async function pullMallProductComment({ id, page = 1, tag = '' } = {}) {
  345. const { data } = await get(buildRestPath('product/comment'), { id, page, tag })
  346. const commentData = data.comment || {}
  347. const statistics = data.statistics || { rate: 0, good: 0, pertinent: 0, poor: 0, figure: 0, total: 0 }
  348. if (Array.isArray(commentData.data)) {
  349. commentData.data.forEach(item => {
  350. if (item.user && item.user.avatar) {
  351. item.user.avatar = buildOssUrl(item.user.avatar)
  352. }
  353. if (Array.isArray(item.images)) {
  354. item.images = item.images.map(img => buildOssUrl(img))
  355. }
  356. })
  357. }
  358. return {
  359. list: Array.isArray(commentData.data) ? commentData.data : [],
  360. total: commentData.total || 0,
  361. currentPage: commentData.current_page || 1,
  362. lastPage: commentData.last_page || 1,
  363. statistics
  364. }
  365. }
  366. /**
  367. * 我的评论列表
  368. * 对接 88live 的 /wanlshop/user/comment 接口
  369. */
  370. export async function pullMyComments({ page = 1 } = {}) {
  371. const { data } = await get('/wanlshop/user/comment', { page })
  372. const resData = data || {}
  373. const list = Array.isArray(resData.data) ? resData.data : []
  374. return {
  375. list,
  376. currentPage: resData.current_page || 1,
  377. lastPage: resData.last_page || 1,
  378. total: resData.total || 0
  379. }
  380. }