PageSourceSearch

https://www.joaciijewelry.com/assets/js/unit/detail-pattern.js

js joaciijewelry.com collected 2026-10-08 05:02:12 UTC 15,413 bytes, 439 lines download raw bytes

1
2/**执行文件 */
3class ScriptDetailPattern extends Script {
4  constructor(params) {
5    super(params)
6    this.params = params
7    this.$dom = document.querySelector(`#${this.attrId}`)
8    this.modal = null
9    this.$main = this.$dom.querySelector('.unit-detail-pattern')
10    this.isM = window.innerWidth < 768
11  }
12  init() { }
13  /** 手机端初始化 */
14  initMobile() {
15    this.$modalBtn = this.$dom.querySelector('.unit-detail-pattern__modal-btn')
16    if (!this.$modalBtn) return
17    this.$modalBtn.addEventListener('click', () => {
18      let mode = 0
19      const isCart = window.app.info.productDetail.config.button_authority.includes('cart')
20      const isBuy = window.app.info.productDetail.config.button_authority.includes('order')
21      if (isCart && isBuy) mode = 1
22      else if (isCart) mode = 2
23      else if (isBuy) mode = 3
24      else mode = 0
25      window.app.info.renderDetailPattern(mode)
26    })
27  }
28
29  render() {
30    if (!this.$main) return
31    this.$main.innerHTML = ''
32    new PatternDocument(this.$main).init()
33    new Pattern(this.attrId, this)
34
35    if (this.isM) this.initMobile()
36  }
37}
38/** 产品参数匹配 */
39class Pattern {
40  constructor(attr_id, parent) {
41    this.parent = parent
42    this.attr_id = attr_id
43    this.activeAttr = {}
44    this.allAttr = {}
45    this.matchList = []
46    this.matchInfo = {}
47    this.attrItemNode = document.querySelectorAll(`#${attr_id} [attr-item]`)
48    this.utilts = Utilts.ins()
49    // 储存所有属性
50    this.initAttr()
51    // 兄弟元素, 当前类型的款式其他项
52    this.initBrotherAttr()
53    // 获取所有搭配组合(多款式/单款式)
54    this.initMatchInfo()
55    this.initCheckDisabled()
56  }
57  // 储存所有属性
58  initAttr() {
59    const _this = this
60    if (this.attrItemNode) {
61      for (const $el of this.attrItemNode) {
62        // 获取产品款式类型id
63        const items_id = $el.getAttribute('attr-items')
64        _this.allAttr[items_id] = _this.allAttr[items_id] || []
65        // 储存所属类型的选项
66        _this.allAttr[items_id].push(new Attr($el, _this))
67      }
68    }
69  }
70  // 储存兄弟元素
71  initBrotherAttr() {
72    Object.keys(this.allAttr).forEach(key => {
73      this.allAttr[key].forEach((item, index) => {
74        for (let i = 0; i < this.allAttr[key].length; i++) {
75          if (i !== index) {
76            item.brotherAttrList.push(this.allAttr[key][i])
77          }
78        }
79      })
80    })
81  }
82  // 存储匹配款式 attr_ids:款式类型id  attr_detail_ids: 款式参数id
83  initMatchInfo() {
84    const detail = window.app.info.productDetail
85    if (detail.pattern.length > 1) {
86      detail.pattern.forEach(item => {
87        // 款式参数存在时为多款式
88        if (!!item.attr_ids) {
89          const ids = item.attr_detail_ids.split(',').filter(v => v)
90          this.matchList.push(ids)
91        }
92      })
93      detail.price_id = 0
94      detail.quantity = 1
95      detail.weight = 0
96      detail.stock = 'none'
97      detail.is_pattern = false
98      // 单款式
99    } else if (detail.pattern.length == 1) {
100      console.log('detail',detail);
101      // 单款式
102      this.matchInfo = detail.pattern[0]
103      detail.is_pattern = true
104      detail.price_id = Number(this.matchInfo.id)
105      detail.quantity = 1
106      detail.weight = Number(this.matchInfo.weight)
107      detail.length = Number(this.matchInfo.length)
108      detail.width = Number(this.matchInfo.width)
109      detail.height = Number(this.matchInfo.height)
110      detail.stock = Number(this.matchInfo.stock)
111      if (window.app.script.ScriptDetailLogistics) {
112        window.app.script.ScriptDetailLogistics[0].shipment_id = this.matchInfo.shipment_ids
113      }
114    }
115  }
116  // 检查属性是否禁用
117  initCheckDisabled() {
118    // 每个属性都检查一下自己是否需要禁用
119    Object.keys(this.allAttr).forEach(itemsKey => {
120      // 除了同类属性,获取其他款式类型激活的属性值
121      const activeAttrList = Object.keys(this.activeAttr).filter(key => key !== itemsKey).map(key => this.activeAttr[key])
122
123      // 符合已选属性的新的搭配组合
124      const newMatchList = this.matchList
125        .filter(items => {
126          let isTrue = true // 默认合格
127          activeAttrList.forEach(item => {
128            if (!items.includes(item)) isTrue = false
129          })
130          return isTrue
131        })
132      this.allAttr[itemsKey].forEach(obj => {
133        obj.checkDisabled(newMatchList)
134      })
135    })
136  }
137  // 获取匹配信息
138  getMatchInfo() {
139    const detail = window.app.info.productDetail
140    detail.is_discount = detail.discount_attr.length > 0 ? 1 : 0
141    const activeAttrList = Object.keys(this.activeAttr).map(key => this.activeAttr[key])
142    if (activeAttrList.length === detail.pattern_attr_list.length) {
143      let match_index
144      this.matchList.forEach((arr, index) => {
145        let isTrue = true
146        activeAttrList.forEach(id => { if (!arr.includes(id)) isTrue = false })
147        if (isTrue) match_index = index
148      })
149      this.matchInfo = detail.pattern[match_index]
150      detail.price_id = Number(this.matchInfo.id)
151      detail.weight = Number(this.matchInfo.weight)
152      detail.thumb = this.matchInfo.thumb_prefix || ''
153      detail.length = Number(this.matchInfo.length)
154      detail.width = Number(this.matchInfo.width)
155      detail.height = Number(this.matchInfo.height)
156      console.log('this.matchInfo1', this.matchInfo)
157      if (window.app.script.ScriptDetailLogistics) {
158        window.app.script.ScriptDetailLogistics[0].price_method = this.matchInfo.shipment_method
159        window.app.script.ScriptDetailLogistics[0].shipment_id = this.matchInfo.shipment_ids
160        window.app.script.ScriptDetailLogistics[0].info = { id: 0, price: '', region: '', time: '' }
161        window.app.script.ScriptDetailLogistics[0].init()
162        if (this.matchInfo.shipment_method !== 0 && this.matchInfo.shipment_method !== 3) {
163          window.app.script.ScriptDetailLogistics[0].updataInfoView()
164        }
165      }
166    } else {
167      this.matchInfo = {}
168      detail.price_id = 0
169      detail.thumb = ''
170    }
171  }
172  // 改变价格
173  changePrice() {
174    const detail = window.app.info.productDetail
175    // 如果当前产品是限时折扣,修改打折后的价格
176    if (detail.is_discount) {
177      this.setDiscoutProductPrice(detail)
178    } else
179      this.setNormalProductPrice(detail)
180  }
181
182  /**
183   * 设置折扣产品款式价格
184   * @param {*} detail
185   * @returns
186   */
187  setDiscoutProductPrice(detail) {
188    if (!this.matchInfo.id) {
189      detail.stock = 'none'
190      detail.is_pattern = false
191      detail.off_rate = this.utilts.toFixed((1000 - detail.max_discount) / 10)
192      detail.discount_price = detail.min_price
193    } else {
194      // 如果匹配上限时折扣信息,但折扣属性找不到相关信息
195      // 表示当前款式不打折
196      detail.stock = this.matchInfo.stock
197      const attr = detail.discount_attr.find(p => p.price_id === detail.price_id)
198      if (attr) {
199        detail.is_discount = 1
200        detail.is_pattern = true
201        detail.discount_original_price = this.matchInfo.selling_price
202        detail.off_rate = this.utilts.toFixed((1000 - attr.percent) / 10)
203        detail.discount_price = this.utilts.toFixed(this.matchInfo.selling_price * attr.percent / 1000)
204      } else {
205        detail.is_pattern = false
206        detail.is_discount = 0
207        this.setNormalProductPrice(detail)
208      }
209    }
210  }
211
212  /**
213   * 设置普通产品价格
214   * @param {*} detail
215   */
216  setNormalProductPrice(detail) {
217    if (this.matchInfo.id) {// 如果不是限时折扣
218      detail.is_pattern = true
219      detail.stock = this.matchInfo.stock
220      detail.min_price = this.matchInfo.selling_price
221      detail.max_price = 0
222      detail.min_cost_price = this.matchInfo.cost_price
223      detail.max_cost_price = 0
224    } else detail.is_pattern = false
225    let matchTotal = document.querySelector(`#${this.attr_id} .pm-total-price`)
226    // mainPrice 为当前主产品的价格,给搭配产品使用算一起购买的总价
227    window.app.info.productDetail.mainPrice = detail.min_price
228    let matchProductsTotal = window.app.info.productDetail.matchProductsTotal || 0
229    // 如果有搭配产品,计算总价
230    if (matchTotal) {
231      matchTotal.innerHTML = `$${(detail.min_price + matchProductsTotal).toFixed(2)}`
232    }
233
234    let matchOriginalTotal = document.querySelector(`#${this.attr_id} .pm-original-total-price`)
235    window.app.info.productDetail.mainOriginalPrice = detail.min_cost_price
236    let matchProductsOriginalTotal = window.app.info.productDetail.matchProductsOriginalTotal || 0
237    // 如果有搭配产品,计算总价
238    if (matchOriginalTotal) {
239      matchOriginalTotal.innerHTML = `$${(detail.min_cost_price + matchProductsOriginalTotal).toFixed(2)}`
240    }
241  }
242  // 更新匹配视图 (m端使用)
243  updataAttrView() {
244    if (!this.parent.isM || !this.matchInfo.id) return
245    let html = ''
246    window.app.info.productDetail.pattern_attr_list.forEach(item => {
247      if (this.activeAttr[item.product_attr_id]) {
248        const attr = item.content
249        const value = item.attr_value.filter(x => x.id === Number(this.activeAttr[item.product_attr_id]))[0].content
250        html += `<span>${attr}: ${value}</span>`
251      }
252    })
253    this.parent.$dom.querySelector('.unit-detail-pattern__attr-items').innerHTML = html
254  }
255}
256
257/** 单个参数交互 */
258class Attr {
259  constructor($dom, parent) {
260    this.$dom = $dom
261    this.parent = parent
262    this.items_id = this.$dom.getAttribute('attr-items')
263    this.item_id = this.$dom.getAttribute('attr-item')
264    this.isDisabled = false
265    this.isActive = false
266    this.brotherAttrList = []
267    this.$dom.addEventListener('click', () => this.setActive())
268    this.isM = window.innerWidth < 768
269  }
270  /**激活当前款式参数 */
271  setActive() {
272    // 禁点状态
273    if (this.isDisabled) return
274    // 高亮状态取消
275    if (this.isActive) {
276      delete this.parent.activeAttr[this.items_id]
277      this.cancelActive()
278    } else {
279      // 点亮当前属性
280      this.isActive = true
281      // 存起点亮款式参数的id,款式: 款式参数id
282      this.parent.activeAttr[this.items_id] = this.item_id
283      this.$dom.classList.add('active')
284    }
285    // 同款其他参数取消高亮
286    this.brotherAttrList.forEach(_this => {
287      _this.cancelActive()
288    })
289    this.parent.initCheckDisabled()
290    this.parent.getMatchInfo()
291    this.parent.changePrice()
292    this.parent.updataAttrView()
293    if(this.isM){
294      window.app.scriptCaller('ScriptProductDetail', 'setBtnDisable')
295    }
296    // 刷新页面数据,不刷新自己
297    window.app.info.renderDetailView(['ScriptDetailPattern'])
298  }
299  /** 取消高亮 */
300  cancelActive() {
301    this.isActive = false
302    this.$dom.classList.remove('active')
303  }
304  /** 禁止状态 */
305  setDisabled(val) {
306    this.isDisabled = val
307    if (this.isDisabled) this.$dom.classList.add('disabled')
308    else this.$dom.classList.remove('disabled')
309  }
310  // 匹配可选组合
311  checkDisabled(matchList) {
312    // 默认禁用
313    let isDisabled = true
314    // 每个组合中有没有当前属性,任何一个有,就不禁用
315    matchList
316      .forEach(items => {
317        if (items.includes(this.item_id)) isDisabled = false
318      })
319    this.setDisabled(isDisabled)
320  }
321}
322
323class PatternDocument {
324  constructor($main) {
325    this.$main = $main
326    this.productDetail = window.app.info.productDetail
327    this.pattern_attr_list = this.productDetail.pattern_attr_list || []
328    this.utilts = window.app.utilts
329  }
330
331  init() {
332    this.$items = document.createElement('div')
333    this.$items.classList.add('unit-detail-pattern__items')
334    if (this.pattern_attr_list.length === 0) {
335      this.$items.classList.add('d-none')
336      const isDesign = this.utilts.checkDesign()
337      if (this.pattern_attr_list.length === 0 && isDesign) {
338        const $noData = document.createElement('div')
339        $noData.classList.add('alert', 'alert-light', 'border')
340        $noData.innerHTML = '请在后台产品--产品管理--添加/编辑产品管理'
341      }
342    } else {
343      this.pattern_attr_list.forEach(attrItem => {
344        const $patternItem = this.getAttrItem(attrItem)
345        this.$items.append($patternItem)
346      })
347
348      this.$main.append(this.$items)
349      this.$main.append(this.getMobileHtml())
350    }
351  }
352  // 手机端使用
353  getMobileHtml() {
354    const $box = document.createElement('div')
355    const $btn = document.createElement('div')
356    const $attr = document.createElement('div')
357    const $attr_items = document.createElement('div')
358    const $span = document.createElement('span')
359
360    $box.classList.add('unit-detail-pattern__simple')
361    $btn.classList.add('unit-detail-pattern__modal-btn')
362    $attr.classList.add('unit-detail-pattern__attr')
363    $attr.innerHTML = 'Style:'
364    $attr_items.classList.add('unit-detail-pattern__attr-items')
365    $span.classList.add('iconfont', 'icon-action-left-lighter', 'unit-detail-pattern__modal-alert')
366    this.pattern_attr_list.forEach(item => {
367      const span = document.createElement('span')
368      span.innerHTML = item.content
369      $attr_items.appendChild(span)
370    })
371    $attr.appendChild($attr_items)
372    $btn.appendChild($attr)
373    $btn.appendChild($span)
374    $box.appendChild($btn)
375    return $box
376  }
377
378  /** 获取款式信息 */
379  getAttrItem(item) {
380    const $item = document.createElement('div')
381    $item.classList.add('unit-detail-pattern__item')
382
383    const $label = document.createElement('div')
384    $label.classList.add('unit-detail-pattern__label')
385    $label.innerText = item.content
386
387    const $value = document.createElement('div')
388    $value.classList.add('unit-detail-pattern__value')
389
390    const $checkbox = document.createElement('div')
391    $checkbox.classList.add('unit-detail-pattern__checkbox')
392
393    item.attr_value.forEach(child => {
394      const $attrItem = this.getAttrValue(item, child)
395      $checkbox.append($attrItem)
396    })
397    $value.append($checkbox)
398
399    $item.append($label)
400    $item.append($value)
401    return $item
402  }
403
404  /** 获取款式属性信息 */
405  getAttrValue(item, child) {
406    const $attrItem = document.createElement('div')
407    $attrItem.classList.add('unit-detail-pattern__child')
408    $attrItem.setAttribute('attr-items', item.product_attr_id)
409    $attrItem.setAttribute('attr-item', child.id)
410    if (child.thumb_prefix && [2, 3].includes(item.display_mode)) {
411      $attrItem.classList.add('has-img')
412      const $em = document.createElement('em')
413      $em.classList.add('is-dark')
414      $em.innerHTML = child.content
415      $attrItem.append($em)
416
417      let iopcmd = { convert: { quality: 100 }, thumbnail: { type: 4, width: 100 } }
418      let query_arr = child.thumb_prefix.split('?')
419      if (query_arr.length > 1) {
420        let iopcmd_arr = query_arr[1].split('iopcmd=')
421        if (iopcmd_arr.length > 1) {
422          iopcmd_arr.shift()
423          iopcmd.query = 'iopcmd=' + iopcmd_arr.join('iopcmd=')
424        }
425      }
426
427      const $img = document.createElement('img')
428      $img.src = globalThis.Server.getTransferImgUrl(query_arr[0], iopcmd, 'webp', window.app.info.site)
429      $attrItem.append($img)
430    }
431    if (item.display_mode === 2) {
432      $attrItem.classList.add('hide-attr-text')
433    }
434    const $span = document.createElement('span')
435    $span.innerHTML = child.content
436    $attrItem.append($span)
437    return $attrItem
438  }
439}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.