import axios from 'axios' import algoliasearch from 'algoliasearch' import cloneDeep from 'lodash.clonedeep' import { times, divide } from 'number-precision' const defaultSortFunc = (a, b) => { if (a.hasBestSeller !== b.hasBestSeller) { return b.hasBestSeller - a.hasBestSeller } if (a.hasNewProduct !== b.hasNewProduct) { return b.hasNewProduct - a.hasNewProduct } if (a.ranking !== b.ranking) { return b.ranking - a.ranking } return b.icon.length - a.icon.length } export default { computed: { computedProductList() { return this.productsList.filter(item => { let moqFlag = true // moq 过滤. 要筛出1到最大输入quantities范围的 if (!item.moq || item.moq > this.quantity) { moqFlag = false } // 筛选基础价格, 只要有一个价格符合价格区间都算符合条件. let priceFlag = true const min = Number(item.price_min) const max = Number(item.price_max) if (max < this.priceMin || min > this.priceMax) { priceFlag = false } // 筛选颜色 let colorFlag = true if (this.checkedColor.length) { if (Array.isArray(item.color) && item.color.length) { colorFlag = item.color.some(i => this.checkedColor.includes(i.id)) } else { colorFlag = false } } // collection let collectionFlag = true if (this.checkedCollection.length) { if (Array.isArray(item.collections) && item.collections.length) { collectionFlag = item.collections.some(i => this.checkedCollection.includes(i) ) } else { collectionFlag = false } } // compliance flag let complianceFlag = true if (this.checkedCompliance.length) { if (Array.isArray(item.compliance) && item.compliance.length) { complianceFlag = item.compliance.some(i => this.checkedCompliance.includes(i.id) ) } else { complianceFlag = false } } let featureFlag = true if (this.checkedFeature.length) { if (Array.isArray(item.filter) && item.filter.length) { featureFlag = item.filter.some(i => this.checkedFeature.includes(i.id) ) } else { featureFlag = false } } const decorationFlag = true // if (this.checkedDecoration.length && item.pricePoint.length) { // decorationFlag = item.pricePoint.some(i => { // return i.decorationList.some(j => // this.checkedDecoration.includes(j.id) // ) // }) // } let leadtimeFlag = true if (this.checkedLeadtime) { const cycleMap = Array.from( new Set(item.cycle.map(i => Number(i.id))) ) const currentLeadtimeValue = this.leadTimeList.filter( i => i.id === this.checkedLeadtime )[0].value leadtimeFlag = currentLeadtimeValue.some(id => cycleMap.includes(id)) } // console.log(moqFlag) // console.log(priceFlag) // console.log(leadtimeFlag) // console.log(colorFlag) // console.log(collectionFlag) // console.log(featureFlag) return ( moqFlag && priceFlag && leadtimeFlag && colorFlag && collectionFlag && complianceFlag && featureFlag && decorationFlag ) }) }, }, watch: { $route: { immediate: true, handler(to) { if (to.query.lead_time) { const temp = Number(to.query.lead_time) this.checkedLeadtime = typeof temp === 'number' ? temp : '' } else { this.checkedLeadtime = '' } if (to.query.pricetype) { this.priceId = Number(to.query.pricetype) } else { this.priceId = '' } if (to.query.feature) { this.checkedFeature = [Number(to.query.feature)] } else { this.checkedFeature = [] } if (to.query.qty) { const qty = Number(to.query.qty) if (typeof qty === 'number') { this.quantity = qty } } else { this.quantity = 1000 } // 刷新浏览器, 获得的参数是字符串类型, 点页面上的链接, 跳转过来取到的参数是数字类型... if (to.query.feature && Number(to.query.feature) === 54) { this.order_name = '' this.order_type = '' // this.command = 'Released: Newest to Oldest' } if (this.index) { this.getList() } }, }, computedProductList: { immediate: true, handler() { this.featureList = this.featureList.map(item => { return { ...item, count: 0 } }) this.collectionsList = this.collectionsList.map(item => { return { ...item, count: 0 } }) this.colourList = this.colourList.map(item => { return { ...item, count: 0 } }) this.decorationList = this.decorationList.map(item => { return { ...item, count: 0 } }) if (!this.leadtimeComputedFlag) { this.leadTimeList.forEach(item => { item.count = 0 }) } this.computedProductList.forEach(item => { // color 颜色 // console.log(item.color, 'color') item.color.forEach(color => { const idx = this.colourList.findIndex(i => i.id === color.id) if (idx > -1) { this.colourList[idx].count++ } }) // collection item.collections.forEach(collection => { const idx = this.collectionsList.findIndex( i => i.name === collection ) if (idx > -1) { this.collectionsList[idx].count++ } }) // filter / feature item.filter.forEach(filter => { const idx = this.featureList.findIndex(i => i.id === filter.id) if (idx > -1) { this.featureList[idx].count++ } }) // 打印服务 item.pricePoint.forEach(decoration => { const idx = this.decorationList.findIndex( i => i.id === decoration.id ) if (idx > -1) { this.decorationList[idx].count++ } }) // 周期 / leadtime if (!this.leadtimeComputedFlag) { const cycleMap = Array.from( new Set(item.cycle.map(i => Number(i.id))) ) for (const i of this.leadTimeList) { if (i.id === 0) i.count++ let flag = false cycleMap.forEach(cycle => { if (i.value.includes(cycle)) { flag = true } }) if (flag) { i.count++ } } } }) this.cardList = this.computedProductList.slice(0, 12) this.total = this.computedProductList.length this.listQuery = { page: 1, limit: 12, } this.leadtimeComputedFlag = true }, }, categoryList() { const keyword = this.$route.params.secondCategory || this.$route.params.firstCategory || '' if ( !this.$route.fullPath.includes('searchResult') && !this.$route.params.thirdCategory && this.categoryList.length && keyword.length ) { const currentCate = this.getCategoryFromTree( { name: keyword }, this.categoryList ) this.cate = Array.isArray(currentCate.child) ? currentCate.child : [] } }, }, async fetch() { const res = await this.$axios.post('/common/shopProductList') this.categoryList = res.code === 1 ? res.result : [] return true }, data() { return { command: 'Default', commandList: [ { label: 'Default', value: 'Default', }, { label: 'Price: Low to high', value: 'Price: Low to high', }, { label: 'Price: High to low', value: 'Price: High to low', }, { label: 'Name: A to Z', value: 'Product Name:A-Z', }, { label: 'Name: Z to A', value: 'Product Name:Z-A', }, { label: 'Released: Newest to Oldest', value: 'create_time', }, ], total: 0, listQuery: { page: 1, limit: 12, }, blackList: ['111', '111.00', '999', '999.00', 111, 999], // 筛选器数据 leadTimeList: [], // 非实体数据. 里面包含虚拟字段, 可能不会跟数据表一一对应, 用来做lead times集合筛选的. fakeLeadTimeList: [ { name: '1 Week or less', id: 1, value: [34, 40, 24, 22, 41, 28], }, { name: '2 Weeks or less', id: 2, value: [34, 40, 24, 22, 41, 16, 35, 28], }, { name: '3 Weeks or less', id: 3, value: [34, 40, 24, 22, 41, 16, 35, 23, 42, 28], }, { name: '4 Weeks or less', id: 4, value: [34, 40, 24, 22, 41, 16, 35, 23, 42, 18, 33, 28], }, { name: '5 Weeks or less', id: 5, value: [34, 40, 24, 22, 41, 16, 35, 23, 42, 18, 33, 26, 28], }, { name: '6 Weeks or less', id: 6, value: [34, 40, 24, 22, 41, 16, 35, 23, 42, 18, 33, 26, 21, 28], }, { name: '6 Weeks or more', id: 7, value: [20, 19, 29, 31, 30, 32, 36, 37, 38, 39, 25], }, ], colourList: [], collectionsList: [], featureList: [], decorationList: [], complianceList: [], // 筛选器数据 end productsList: [], cardList: [], // 顶部分类数据总和, 未筛选过的 cate: [], order_name: '', order_type: '', quantityMin: 1, quantity: 1000, priceMin: 1000, priceMax: 0.01, priceRange1: 1, priceRange2: 1, checkedLeadtime: '', checkedColor: [], checkedDecoration: [], checkedFeature: [], checkedCollection: [], checkedCompliance: [], priceId: '', cancelSource: null, loaded: false, // leadtime的计数仅每次请求接口后重新计算一次 leadtimeComputedFlag: false, index: null, categoryList: [], } }, beforeMount() { const CancelToken = axios.CancelToken this.cancelSource = CancelToken.source() const client = algoliasearch( 'OGR0RAPKVN', '19f480c6a3000b14705a78beadf19ab6' ) this.index = client.initIndex('product_ca') this.getList() }, methods: { multiplyWithMargin(o, m) { return divide(times(o, 100 + parseFloat(m)), 100) }, getList() { let sortFunc = defaultSortFunc let keyword = this.$route.params.thirdCategory || this.$route.params.secondCategory || this.$route.params.firstCategory || '' const config = { hitsPerPage: 1000, // attributesToRetrieve: ['cat_name'], // 限定只返回某个属性 } if ( this.$route.query.keyword && this.$route.fullPath.includes('searchResult') ) { keyword = decodeURIComponent(this.$route.query.keyword).trim() || '' } this.listLoading = true this.productsList = [] if (!this.$route.fullPath.includes('searchResult')) { const t = this.fakeLeadTimeList.slice() t.pop() t.push({ // 用id 0 是会导致 checkedLeadtime 勾选all时值为0, 巧妙避开 computedProductList 中 leadTime相关的计算, // 让 leadtimeFlag 永远都是true, 从而顺利筛选出所有商品(筛选全部通过). id: 0, name: 'All', value: [], }) this.leadTimeList = t if ( this.$route.query.feature && Number(this.$route.query.feature) === 54 ) { keyword = 'new product' // config.facetFilters = ['filter:New product'] config.restrictSearchableAttributes = 'filter' sortFunc = (a, b) => { if (a.hasNewProduct !== b.hasNewProduct) { return b.hasNewProduct - a.hasNewProduct } if (a.ranking !== b.ranking) { return b.ranking - a.ranking } } } else { config.restrictSearchableAttributes = 'cat_name' } } else { this.leadTimeList = this.fakeLeadTimeList.slice() } Promise.all([ this.index.search(keyword, config), this.$store.dispatch('getShopInfo'), ]) .then(([{ hits }, shopInfo]) => { const margin = parseFloat(shopInfo.margin) || 0 this.leadtimeComputedFlag = false this.priceMin = 1000 this.priceMax = 0.01 // 这几个是临时数据 const temp = [] const filterList = [] const colorList = [] const collectionsList = [] // 临时数据 end if (hits.length) { hits .filter(i => i.status === 1 || i.status === '1' || i.status) .forEach(item => { let colorImg = '' item.colour_imgs = JSON.parse(item.colour_imgs) item.colour_imgs.sort((a, b) => a.name.length - b.name.length) if ( this.$route.fullPath.includes('searchResult') && Array.isArray(item.colour_imgs) && item.colour_imgs.length ) { item.colour_imgs.forEach(colorItem => { if (colorItem.name && colorItem.name.length) { if ( new RegExp(colorItem.name.toLowerCase(), 'i').test( keyword ) ) { console.log( item.product_code, colorItem.name, 'replace' ) colorImg = colorItem.img } else if ( /\s/.test(colorItem.name) && colorItem.name .split(' ') .filter(a => a.length > 0) .some(b => new RegExp(b.toLowerCase(), 'i').test(keyword) ) ) { console.log( item.product_code, colorItem.name, 'advance replace' ) colorImg = colorItem.img } } }) item.colour_imgs.forEach(i => { if (i.name && i.name.length) { if ( keyword.toLowerCase().includes(i.name.toLowerCase()) ) { console.log( item.product_code, i.name, 'complete replace' ) colorImg = i.img } } }) console.log('---') } const result = { // img: item.img || '', // colour_imgs: item.colour_imgs, price_min: this.multiplyWithMargin( parseFloat(item.price_min), margin ), price_max: this.multiplyWithMargin( parseFloat(item.price_max), margin ), product_code: item.product_code, icon: Array.isArray(item.icon) ? item.icon.filter(i => i.id) : typeof item.icon === 'string' ? JSON.parse(item.icon) : [], moq: item.moq, cycle: (typeof item.cycle === 'string' ? JSON.parse(item.cycle) : item.cycle) || [], cycle_icon: (typeof item.cycle === 'string' ? JSON.parse(item.cycle) : item.cycle) || [], collection_detail: (typeof item.collection_detail === 'string' ? JSON.parse(item.collection_detail) : item.collection_detail) || [], product_name: item.name || '', info: { image: colorImg || item.img || '', id: item.id, }, main: { image: colorImg || item.img || '', id: item.id, }, color: [], filter: [], // 不一定有 collections: [], pricePoint: [], compliance: [], create_time: item.create_time ? new Date(item.create_time).getTime() : new Date().getTime(), ranking: item.rank ? parseInt(item.rank) : Math.floor(Math.random() * 10000), } const min = Number(result.price_min) const max = Number(result.price_max) if (min < this.priceMin && min !== 0) { this.priceMin = min } if (max > this.priceMax) { this.priceMax = max } this.priceRange1 = this.priceMin this.priceRange2 = this.priceMax if (item.colour) { try { item.colour_detail = JSON.parse(item.colour_detail) } catch (e) { console.log('解析 colour_detail 出错') } if (!Array.isArray(item.colour_detail)) { // 部分数据异常, 额外添加处理逻辑 item.colour_detail = [item.colour_detail] } result.color = item.colour_detail item.colour_detail.forEach(a => { if (!colorList.some(i => i.id === a.id)) { colorList.push(a) } }) } if (item.filter.length) { try { item.filter_detail = JSON.parse(item.filter_detail) } catch (e) { console.log('解析 filter_detail 出错') } result.filter = item.filter_detail item.filter_detail.forEach(a => { if (!filterList.some(i => i.id === a.id)) { filterList.push(a) } }) } if (item.collection.length) { try { item.collection = JSON.parse(item.collection) } catch (e) { console.log('解析 collection 出错') } item.collection.forEach(a => { result.collections.push(a) if (!collectionsList.some(i => a === i.name)) { collectionsList.push({ name: a }) } }) } result.hasNewProduct = result.filter.some(i => i.id === 54) result.hasBestSeller = result.collection_detail.some( i => i.id === 209 ) temp.push(result) }) } this.featureList = filterList this.collectionsList = collectionsList this.colourList = colorList // this.decorationList = [] // this.complianceList = [] if (this.$route.fullPath.includes('searchResult')) { temp.sort((a, b) => { if (a.hasBestSeller !== b.hasBestSeller) { return b.hasBestSeller - a.hasBestSeller } }) } else { temp.sort(sortFunc) } this.productsList = temp this.loaded = true this.listLoading = false }) .catch(() => { this.listLoading = false this.productsList = [] }) }, getListold() { const params = { order_name: this.order_name, order_type: this.order_type, first_cat: this.$route.params.firstCategory, second_cat: this.$route.params.secondCategory, third_cat: this.$route.params.thirdCategory, } if (this.loaded || this.$route.query.qty) { params.moq = this.quantity } if (this.priceId) { params.price_id = this.priceId } if (this.checkedLeadtime) { params.filter_id = this.checkedLeadtime } if ( this.$route.query.keyword && this.$route.fullPath.includes('searchResult') ) { params.keyword = decodeURIComponent(this.$route.query.keyword).trim() || '' } const config = {} if (this.cancelSource) { this.cancelSource.cancel() config.cancelToken = this.cancelSource?.token } this.listLoading = true this.productsList = [] if (!this.$route.fullPath.includes('searchResult')) { const t = this.fakeLeadTimeList.slice() t.pop() t.push({ // 用id 0 是会导致 checkedLeadtime 勾选all时值为0, 巧妙避开 computedProductList 中 leadTime相关的计算, // 让 leadtimeFlag 永远都是true, 从而顺利筛选出所有商品(筛选全部通过). id: 0, name: 'All', value: [], }) this.leadTimeList = t } else { this.leadTimeList = this.fakeLeadTimeList.slice() } this.$axios .get( '/goods_au/list', { params, }, config ) .then(res => { if (res.code === 1) { this.leadtimeComputedFlag = false this.priceMin = 1000 this.priceMax = 0.01 this.featureList = res.result.filter || [] this.collectionsList = res.result.collections || [] this.complianceList = res.result.compliance || [] this.colourList = res.result.color || [] this.decorationList = res.result.decorations || [] // this.leadTimeList = // res.result.lead_time // .sort((a, b) => a.rank - b.rank) // .map(i => { // return { // ...i, // value: this.fakeLeadTimeList.filter(f => f.id === i.id)[0] // .value, // } // }) || [] this.productsList = res.result.list.map(item => { const min = Number(item.price_min) const max = Number(item.price_max) if (min < this.priceMin && min !== 0) { this.priceMin = min } if (max > this.priceMax) { this.priceMax = max } this.priceRange1 = this.priceMin this.priceRange2 = this.priceMax return { ...item, cycle_icon: item.cycle, lead_time_id: item.lead_time_id || '', info: { image: item.main?.image, id: item.main?.id, }, } }) || [] // 排序是为了保证一级分类考前. 分类页面直接就取他的child展示, 即列出二级分类. // 二级分类页面大概率搜不出三级子分类, 一言难尽. this.cate = res.result.cate.sort((a, b) => a.pid - b.pid) // console.log(this.cate, 'this.cate') this.listLoading = false } // this.$nextTick(() => { // window.scroll(0, 0); // }); this.loaded = true }) .catch(() => { this.listLoading = false this.productsList = [] }) }, handleChangeQuantity(value) { if (value) { this.quantity = value // this.$router.replace({ // path: this.$route.path, // query: { // ...this.$route.query, // qty: value, // t: new Date().getTime(), // }, // }) this.getList() } }, handleChangePrice(arr) { console.log('price change', arr) this.priceMin = arr[0] this.priceMax = arr[1] }, handleChangeLeadtime(value) { this.checkedLeadtime = value // this.$router.replace({ // path: this.$route.path, // query: { // ...this.$route.query, // lead_time: value, // t: new Date().getTime(), // }, // }) }, handleChangeColor(value) { this.checkedColor = value }, handleChangeFeature(value) { this.checkedFeature = value }, handleChangeCollection(value) { this.checkedCollection = value }, handleChangeDecoration(value) { this.checkedDecoration = value }, handleChangeCompliance(value) { this.checkedCompliance = value }, handleCommand(command) { let func = defaultSortFunc if (command === 'Default') { this.order_name = '' this.order_type = '' this.command = command } else if (command === 'Product Name:A-Z') { this.order_name = 'product_name' this.order_type = 'asc' func = (a, b) => a.product_name.localeCompare(b.product_name) this.command = command } else if (command === 'Product Name:Z-A') { this.order_name = 'product_name' this.order_type = 'desc' func = (a, b) => b.product_name.localeCompare(a.product_name) this.command = command } else if (command === 'Price: Low to high') { this.order_name = 'price_min' this.order_type = 'asc' func = (a, b) => a.price_min - b.price_min this.command = command } else if (command === 'Price: High to low') { this.order_name = 'price_min' this.order_type = 'desc' func = (a, b) => b.price_max - a.price_max this.command = command } else if (command === 'create_time') { this.order_name = 'create_time' this.order_type = 'desc' func = (a, b) => b.create_time - a.create_time this.command = command } this.productsList.sort(func) }, reset() { if (!this.$route.query.qty) { this.quantity = 1000 } this.priceMin = 1000 this.priceMax = 0.01 this.checkedCollection = [] this.checkedCompliance = [] this.checkedLeadtime = '' this.checkedColor = [] this.checkedDecoraiton = [] this.checkedFeature = [] this.getList() }, infiniteHandler($state) { if (this.total > 12) { const temp = this.computedProductList.slice( this.listQuery.page * this.listQuery.limit, this.listQuery.page * this.listQuery.limit + this.listQuery.limit ) this.listQuery.page += 1 // 下一页 setTimeout(() => { if (temp.length) { this.cardList = this.cardList.concat(temp) $state.loaded() } else { $state.loaded() $state.complete() } }, 800) } else { $state.loaded() $state.complete() } }, transName(str) { return str.replace(/\s+/g, '-').replace('-&', '').toLowerCase() }, // 从分类树数据里面找到对应分类 getCategoryFromTree(p, tree) { let result = {} if (!tree.length) return result tree.forEach(i => { if (i.id === p.id || this.transName(i.name) === p.name) { result = cloneDeep(i) result.lev = 1 } if (!Array.isArray(i.child)) return i.child.forEach(secondCate => { if ( secondCate.id === p.id || this.transName(secondCate.name) === p.name ) { result = cloneDeep(secondCate) result.parentName = i.name result.lev = 2 result.root = { name: i.name, id: i.id, pid: 0, } } }) }) return result }, }, }