index.tsx 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. import request from '@/helpers/request'
  2. import { browser, 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. Badge,
  16. Button,
  17. Dialog
  18. } from 'vant'
  19. import { defineComponent } from 'vue'
  20. import styles from './index.module.less'
  21. import ColSticky from '@/components/col-sticky'
  22. import { shareCall } from '../share'
  23. import { state } from '@/state'
  24. import qs from 'query-string'
  25. import { getAssetsHomeFile } from '../share-video'
  26. import { postMessage } from '@/helpers/native-message'
  27. export default defineComponent({
  28. name: 'goods-detail',
  29. data() {
  30. const query = this.$route.query
  31. return {
  32. id: query.id,
  33. albumPics: [] as any[],
  34. product: {} as Record<string | number | symbol, any>,
  35. radio: 0,
  36. skuStockList: [] as any[],
  37. detailMobileHtml: '',
  38. loading: false,
  39. wxStatus: false
  40. }
  41. },
  42. computed: {
  43. getPrice() {
  44. let item = this.skuStockList.filter(n => n.id == this.radio) as any
  45. if (item && Array.isArray(item) && item.length) {
  46. return item[0].price
  47. }
  48. const price = this.product.price as any
  49. return price
  50. }
  51. },
  52. created() {
  53. if (browser().isApp) {
  54. if (state.platformType === 'STUDENT') {
  55. const { query } = this.$route as any
  56. const params = Object.assign(
  57. {
  58. id: query.bizId,
  59. promoterId: query.userId
  60. },
  61. query
  62. )
  63. // 自动跳转到学生端商品详情
  64. // this.locationReplace(
  65. // `${location.origin}/student/#/goodsDetail?${qs.stringify(params)}`
  66. // )
  67. if (browser().ios) {
  68. window.location.replace(
  69. `${location.origin}/student/#/goodsDetail?${qs.stringify(params)}`
  70. )
  71. } else {
  72. postMessage({
  73. api: 'openWebView',
  74. content: {
  75. url: `${location.origin}/student/#/goodsDetail?${qs.stringify(
  76. params
  77. )}`,
  78. orientation: 1,
  79. isHideTitle: false
  80. }
  81. })
  82. postMessage({ api: 'back' })
  83. }
  84. } else if (state.platformType === 'TEACHER') {
  85. Dialog.alert({
  86. title: '提示',
  87. message: '请使用酷乐秀学生端扫码打开'
  88. }).then(() => {
  89. postMessage({ api: 'back' })
  90. })
  91. }
  92. } else {
  93. const { origin } = location
  94. const query = this.$route.query
  95. const str =
  96. origin +
  97. `/student/#/goodsDetail?id=${query.bizId}&promoterId=${query.userId}`
  98. shareCall(str, {})
  99. }
  100. },
  101. mounted() {
  102. this.getProduct()
  103. },
  104. methods: {
  105. locationReplace(url: string) {
  106. // alert(url)
  107. if (history.replaceState) {
  108. history.replaceState(null, document.title, url)
  109. window.location.reload()
  110. } else {
  111. location.replace(url)
  112. }
  113. },
  114. async getProduct() {
  115. this.loading = true
  116. let product = {} as any
  117. let skuStockList = []
  118. // 获取产品信息
  119. try {
  120. const result = await request.post(
  121. `/api-mall-portal/open/productProfit`,
  122. {
  123. data: {
  124. bizId: this.$route.query.bizId,
  125. userId: this.$route.query.userId
  126. }
  127. }
  128. )
  129. if (result.data && result.data.productDetail) {
  130. product = result.data.productDetail.product
  131. skuStockList = result.data.productDetail.skuStockList || []
  132. }
  133. } catch {}
  134. this.product = product
  135. this.skuStockList = skuStockList.map((item: any) => {
  136. if (item.spData) {
  137. const spData = JSON.parse(item.spData)
  138. item.spDataJson = spData.reduce((spDataJson, value) => {
  139. spDataJson += value.value
  140. return spDataJson
  141. }, '')
  142. item.sku = spData
  143. .reduce((sku, value) => {
  144. sku.push(`${value.key}: ${value.value}`)
  145. return sku
  146. }, [])
  147. .join(',')
  148. } else {
  149. item.spDataJson = '默认'
  150. }
  151. return item
  152. })
  153. this.albumPics = [product.pic]
  154. .concat(product.albumPics.split(','))
  155. .filter(n => n)
  156. this.detailMobileHtml = product.detailMobileHtml
  157. this.loading = false
  158. },
  159. onPreview(index: number) {
  160. // 图片预览
  161. ImagePreview({
  162. images: this.albumPics,
  163. startPosition: index,
  164. closeable: true
  165. })
  166. },
  167. onShowImg(target: any) {
  168. const { localName } = target.srcElement
  169. if (localName !== 'img') {
  170. return
  171. }
  172. let startPosition = 0
  173. const domList = document.querySelectorAll('.msgWrap img')
  174. let imgList = Array.from(domList).map((item: any, index: number) => {
  175. if (target.srcElement == item) {
  176. startPosition = index
  177. }
  178. return item.src
  179. })
  180. ImagePreview({
  181. images: imgList,
  182. startPosition: startPosition,
  183. closeable: true
  184. })
  185. },
  186. //打开APP
  187. onDetail() {
  188. if (browser().weixin) {
  189. this.wxStatus = true
  190. return
  191. }
  192. const { origin } = location
  193. const query = this.$route.query
  194. let str =
  195. origin +
  196. `/student/#/goodsDetail?id=${query.bizId}&promoterId=${query.userId}`
  197. shareCall(str, {})
  198. setTimeout(() => {
  199. location.href = origin + '/student/#/download'
  200. }, 3000)
  201. }
  202. },
  203. render() {
  204. const product = this.product
  205. const selectSku = this.skuStockList.find((n: any) => n.id === this.radio)
  206. return (
  207. <div class={styles.goodsDetail}>
  208. <Swipe
  209. class={styles.swipe}
  210. lazyRender
  211. v-slots={{
  212. indicator: (item: any) =>
  213. item.total > 1 && (
  214. <div class={styles['custom-indicator']}>
  215. {(item.active || 0) + 1} / {item.total}
  216. </div>
  217. )
  218. }}
  219. >
  220. {this.albumPics.map((item: string, index: number) => (
  221. <SwipeItem>
  222. <Image
  223. class={styles.swipeItemImg}
  224. src={item}
  225. onClick={() => this.onPreview(index)}
  226. fit="cover"
  227. />
  228. </SwipeItem>
  229. ))}
  230. </Swipe>
  231. <CellGroup border={false} class={[styles.goodsHead, 'mb12']}>
  232. <Cell
  233. center
  234. border={false}
  235. v-slots={{
  236. title: () => (
  237. <div class={styles.priceGroup}>
  238. <span class={styles.price}>
  239. <i>¥</i>
  240. {moneyFormat(this.getPrice)}
  241. </span>
  242. {/* <del class={styles.delPrice}>
  243. ¥{moneyFormat(product.originalPrice)}
  244. </del> */}
  245. </div>
  246. )
  247. }}
  248. />
  249. <Cell
  250. center
  251. border={false}
  252. title={product.name}
  253. titleClass={[styles.goodsName, 'van-ellipsis']}
  254. />
  255. </CellGroup>
  256. <Row class={[styles.row, 'mb12']}>
  257. <Col span={4} class={styles.col}>
  258. 规格
  259. </Col>
  260. <Col span={20}>
  261. {selectSku ? (
  262. <div class={styles.selectWrap}>
  263. {selectSku.stock <= 0
  264. ? `当前款式暂时缺货`
  265. : `已选择 ${selectSku.spDataJson}`}
  266. </div>
  267. ) : (
  268. <div class={styles.selectWrap}>请选择 规格</div>
  269. )}
  270. <RadioGroup
  271. class={styles['radio-group']}
  272. modelValue={this.radio}
  273. onUpdate:modelValue={val => (this.radio = val)}
  274. >
  275. {this.skuStockList.map((item: any) => {
  276. const isActive = item.id === this.radio
  277. const type = isActive ? 'primary' : 'default'
  278. return (
  279. <Badge
  280. position="top-right"
  281. content={item.stock <= 0 ? '缺货' : ''}
  282. color={'#999999'}
  283. class={styles.badge}
  284. offset={[-20, 0]}
  285. >
  286. <Radio
  287. class={styles.radio}
  288. name={item.id}
  289. disabled={item.stock <= 0}
  290. onClick={() => {
  291. // 判断是否有库存
  292. if (item.stock <= 0) {
  293. return
  294. }
  295. this.radio = item.id
  296. }}
  297. >
  298. <Tag size="large" plain={isActive} type={type}>
  299. {item.spDataJson}
  300. </Tag>
  301. </Radio>
  302. </Badge>
  303. )
  304. })}
  305. </RadioGroup>
  306. </Col>
  307. </Row>
  308. {this.detailMobileHtml && (
  309. <div class={[styles.section]}>
  310. <div class={styles.detail}>
  311. <span>图文详情</span>
  312. </div>
  313. <div
  314. class={[styles.photoDetail, 'msgWrap']}
  315. onClick={this.onShowImg}
  316. v-html={this.detailMobileHtml}
  317. ></div>
  318. </div>
  319. )}
  320. <ColSticky position="bottom">
  321. <div class={['btnGroup']} style={{ paddingTop: '12px' }}>
  322. <Button block round type="primary" onClick={this.onDetail}>
  323. 下载酷乐秀购买商品
  324. </Button>
  325. </div>
  326. </ColSticky>
  327. {this.wxStatus && (
  328. <div
  329. class={styles.wxpopup}
  330. onClick={() => {
  331. this.wxStatus = false
  332. }}
  333. >
  334. <img src={getAssetsHomeFile('wx_bg.png')} alt="" />
  335. </div>
  336. )}
  337. </div>
  338. )
  339. }
  340. })