index.tsx 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. import request from '@/helpers/request'
  2. import { moneyFormat } from '@/helpers/utils'
  3. import {
  4. Swipe,
  5. SwipeItem,
  6. Image,
  7. CellGroup,
  8. Cell,
  9. ImagePreview,
  10. RadioGroup,
  11. Radio,
  12. Tag,
  13. Row,
  14. Col,
  15. Sticky,
  16. ActionBar,
  17. ActionBarButton,
  18. ActionBarIcon,
  19. Icon,
  20. Badge,
  21. Toast
  22. } from 'vant'
  23. import { defineComponent } from 'vue'
  24. import styles from './index.module.less'
  25. import iconShopCart from '../images/icon-shop-cart.png'
  26. export default defineComponent({
  27. name: 'goods-detail',
  28. data() {
  29. const query = this.$route.query
  30. return {
  31. id: query.id,
  32. albumPics: [],
  33. product: {} as any,
  34. radio: '',
  35. skuStockListTemp: [],
  36. detailMobileHtml: '',
  37. loading: false
  38. }
  39. },
  40. computed: {
  41. skuStockList() {
  42. // 处理规格
  43. const product = this.product
  44. const skuStockList: any =
  45. this.skuStockListTemp.length > 0
  46. ? this.skuStockListTemp
  47. : [
  48. {
  49. id: -1,
  50. price: product.price,
  51. pic: product.pic,
  52. stock: product.stock,
  53. spData: null
  54. }
  55. ]
  56. skuStockList.forEach((item: any) => {
  57. if (item.spData) {
  58. const spData = JSON.parse(item.spData)
  59. let str = ''
  60. spData.forEach((sp: any) => {
  61. str += `${sp.value}`
  62. })
  63. item.spDataJson = str
  64. } else {
  65. item.spDataJson = '默认'
  66. }
  67. })
  68. return skuStockList
  69. }
  70. },
  71. async mounted() {
  72. try {
  73. this.loading = true
  74. const res = await request.get(
  75. `/api-mall-portal/product/detail/${this.id}`
  76. )
  77. this.loading = false
  78. const result = res.data || {}
  79. this.albumPics = result.product.albumPics
  80. ? result.product.albumPics.split(',')
  81. : [result.product.pic]
  82. this.product = result.product
  83. this.skuStockListTemp = result.skuStockList || []
  84. this.detailMobileHtml = result.product.detailMobileHtml
  85. } catch {}
  86. },
  87. methods: {
  88. onPreview(index: number) {
  89. // 图片预览
  90. ImagePreview({
  91. images: this.albumPics,
  92. startPosition: index,
  93. closeable: true
  94. })
  95. },
  96. onShowImg(target: any) {
  97. const { localName } = target.srcElement
  98. if (localName !== 'img') {
  99. return
  100. }
  101. let startPosition = 0
  102. const domList = document.querySelectorAll('.msgWrap img')
  103. let imgList = Array.from(domList).map((item: any, index: number) => {
  104. if (target.srcElement == item) {
  105. startPosition = index
  106. }
  107. return item.src
  108. })
  109. ImagePreview({
  110. images: imgList,
  111. startPosition: startPosition,
  112. closeable: true
  113. })
  114. },
  115. onBuy() {
  116. // 购买
  117. if (!this.radio) {
  118. return Toast('请选择规格')
  119. }
  120. console.log(true)
  121. }
  122. },
  123. render() {
  124. const product = this.product
  125. return (
  126. <div class={styles.goodsDetail}>
  127. <Swipe
  128. class={styles.swipe}
  129. lazyRender
  130. v-slots={{
  131. indicator: (item: any) =>
  132. item.total > 1 && (
  133. <div class={styles['custom-indicator']}>
  134. {(item.active || 0) + 1} / {item.total}
  135. </div>
  136. )
  137. }}
  138. >
  139. {this.albumPics.map((item: string, index: number) => (
  140. <SwipeItem>
  141. <Image
  142. class={styles.swipeItemImg}
  143. src={item}
  144. onClick={() => this.onPreview(index)}
  145. fit="cover"
  146. />
  147. </SwipeItem>
  148. ))}
  149. </Swipe>
  150. <CellGroup border={false} class={[styles.goodsHead, 'mb12']}>
  151. <Cell
  152. center
  153. border={false}
  154. v-slots={{
  155. title: () => (
  156. <div class={styles.priceGroup}>
  157. <span class={styles.price}>
  158. <i>¥</i>
  159. {moneyFormat(product.price)}
  160. </span>
  161. <del class={styles.delPrice}>
  162. ¥{moneyFormat(product.originalPrice)}
  163. </del>
  164. </div>
  165. )
  166. // default: () => <div class={styles.stock}>销量4件</div>
  167. }}
  168. />
  169. <Cell
  170. center
  171. border={false}
  172. title={product.name}
  173. titleClass={[styles.goodsName, 'van-ellipsis']}
  174. />
  175. </CellGroup>
  176. <Row class={[styles.row, 'mb12']}>
  177. <Col span={4} class={styles.col}>
  178. 规格
  179. </Col>
  180. <Col span={20}>
  181. <RadioGroup
  182. class={styles['radio-group']}
  183. modelValue={this.radio}
  184. onUpdate:modelValue={val => (this.radio = val)}
  185. >
  186. {this.skuStockList.map((item: any) => {
  187. const isActive = item.id === this.radio
  188. const type = isActive ? 'primary' : 'default'
  189. return (
  190. <Badge
  191. position="top-right"
  192. content={item.stock <= 0 ? '缺货' : ''}
  193. color={'#999999'}
  194. class={styles.badge}
  195. offset={[-20, 0]}
  196. >
  197. <Radio
  198. class={styles.radio}
  199. name={item.id}
  200. disabled={item.stock <= 0}
  201. onClick={() => {
  202. // 判断是否有库存
  203. if (item.stock <= 0) {
  204. return
  205. }
  206. this.radio = item.id
  207. }}
  208. >
  209. <Tag size="large" plain={isActive} type={type}>
  210. {item.spDataJson}
  211. </Tag>
  212. </Radio>
  213. </Badge>
  214. )
  215. })}
  216. </RadioGroup>
  217. </Col>
  218. </Row>
  219. {this.detailMobileHtml && (
  220. <div class={[styles.section]}>
  221. <div class={styles.detail}>
  222. <span>图文详情</span>
  223. </div>
  224. <div
  225. class={[styles.photoDetail, 'msgWrap']}
  226. onClick={this.onShowImg}
  227. v-html={this.detailMobileHtml}
  228. ></div>
  229. </div>
  230. )}
  231. {!this.loading && (
  232. <ActionBar class={styles.actionBar}>
  233. <ActionBarIcon
  234. icon="cart-o"
  235. // text="购物车"
  236. v-slots={{
  237. icon: () => <Icon name={iconShopCart} size={30} />
  238. }}
  239. />
  240. <div class={styles.buyGroup}>
  241. <ActionBarButton
  242. type="primary"
  243. class={styles.addCertBtn}
  244. text="加入购物车"
  245. />
  246. <ActionBarButton
  247. type="primary"
  248. text="立即购买"
  249. onClick={this.onBuy}
  250. />
  251. </div>
  252. </ActionBar>
  253. )}
  254. </div>
  255. )
  256. }
  257. })