Files
temu-plugin/src/view/lables/SkuManage.vue
2024-10-28 22:10:07 +08:00

569 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<ai-list class="Template">
<ai-title
slot="title"
title="管理SKU"
isShowBack
isShowBottomBorder
@onBackClick="$router.go(-1)">
</ai-title>
<template slot="content">
<ai-search-bar>
<template #left>
<el-button type="primary" size="small" @click="chooseSkuList = [], isShow = true">添加</el-button>
<el-upload
action
:limit="1"
:show-file-list="false"
accept=".xls,.xlsx"
:auto-upload="false"
:file-list="fileList"
:on-change="onExcelChange">
<el-button size="small" type="danger" :disabled="!skuList.length">Excel导入</el-button>
</el-upload>
<json-excel
:data="skuList"
:fields="jsonFields"
name="SKU列表.xls"
worksheet="SKU列表">
<el-button size="small" type="warning" :disabled="!skuList.length">Excel导出</el-button>
</json-excel>
</template>
</ai-search-bar>
<ai-search-bar>
<template #left>
<div class="search-item" style="margin-bottom: 0;">
<label>SKU</label>
<el-input
v-model="search.productSkuId"
style="width: 250px"
size="small"
clearable
placeholder="请输入SKU"
suffix-icon="iconfont iconSearch"
@clear="getList">
</el-input>
</div>
<div class="search-item" style="margin-bottom: 0;">
<label>SKC</label>
<el-input
v-model="search.productSkcId"
style="width: 250px"
size="small"
placeholder="请输入SKC"
clearable
suffix-icon="iconfont iconSearch"
@clear="getList">
</el-input>
</div>
<div class="search-item" style="margin-bottom: 0;">
<label style="width: 100px;">仅显示未填写</label>
<el-select v-model="search.isShowWhite" placeholder="请选择" clearable size="small">
<el-option label="是" value="1"></el-option>
<el-option label="否" value="0"></el-option>
</el-select>
</div>
<el-button style="margin-left: 10px;" @click="getList" size="small" :loading="pageShow">查询</el-button>
</template>
</ai-search-bar>
<ai-table
:tableData="list"
:col-configs="colConfigs"
style="margin-top: 8px;"
@getList="getList"
@selection-change="handleSelectionChange"
v-loading="pageShow"
:isShowPagination="false">
<el-table-column
v-for="(item, index) in relationList"
:key="index"
:prop="item.field"
:show-overflow-tooltip="true"
:label="item.name"
align="center">
</el-table-column>
<el-table-column slot="options" label="操作" align="center" fixed="right" width="120px">
<template v-slot="{ row }">
<div class="table-options">
<el-button type="text" @click="remove(row.id)">删除</el-button>
</div>
</template>
</el-table-column>
</ai-table>
<ai-dialog
:visible.sync="isShow"
title="添加SKU"
width="1400px"
customFooter
@confirm="onConfirm">
<div class="search-item__wrapper">
<div class="left">
<div class="search-item">
<label>添加方式</label>
<el-radio-group v-model="addType" size="small" @change="onSearchRest">
<el-radio-button label="1">按类目添加</el-radio-button>
<el-radio-button label="2">按SKC添加</el-radio-button>
<el-radio-button label="3">按SKU添加</el-radio-button>
</el-radio-group>
</div>
<div class="search-item">
<label>店铺</label>
<el-select v-model="lableSearch.mallId" placeholder="请选择店铺" size="small">
<el-option
v-for="item in $store.state.mallList"
:key="item.mallId"
:label="item.mallName"
:value="item.mallId">
</el-option>
</el-select>
</div>
</div>
<div class="right"></div>
</div>
<div class="search-item__wrapper">
<div class="left">
<div class="search-item" v-show="addType === '1'">
<label>商品分类</label>
<el-cascader
style="width: 280px;"
v-model="targetCatId"
:props="props"
size="small"
filterable
:show-all-levels="false"
collapse-tags
clearable>
</el-cascader>
<el-button style="margin-left: 10px;" @click="onCateChange" size="small" :disabled="!lableSearch.mallId" :loading="isLoading">查询</el-button>
</div>
<div class="search-item" v-show="addType === '2'">
<label>SKC</label>
<el-input
v-model="skuReqParams.SKC"
style="width: 250px"
size="small"
placeholder="多个查询请用户逗号分割"
clearable
@clear="getSkuList()"
suffix-icon="iconfont iconSearch">
</el-input>
<el-button style="margin-left: 10px;" @click="getSkuList" size="small" :disabled="!lableSearch.mallId" :loading="isLoading">查询</el-button>
</div>
<div class="search-item" v-show="addType === '3'">
<label>SKU</label>
<el-input
v-if="addType === '3'"
v-model="skuReqParams.SKU"
style="width: 250px"
size="small"
placeholder="多个查询请用户逗号分割"
clearable
@clear="getSkuList()"
suffix-icon="iconfont iconSearch">
</el-input>
<el-button style="margin-left: 10px;" @click="getSkuList" size="small" :disabled="!lableSearch.mallId" :loading="isLoading">查询</el-button>
</div>
</div>
</div>
<ai-table
height="370"
:tableData="lableList"
:col-configs="colConfigs"
:total="lableTotal"
:current.sync="lableSearch.current"
:size.sync="lableSearch.size"
style="margin-top: 8px;"
:pageSizes="[10, 20, 50, 100, 500, 1000]"
v-loading="isLoading"
:isShowPagination="false"
@getList="() => {}"
@selection-change="handleSelectionChange">
<el-table-column slot="productName" width="300px" :show-overflow-tooltip="true" label="商品名称" fixed="left">
<template slot-scope="scope">
<div>
<el-image :src="scope.row.mainImageUrl" style="width: 40px; height: 40px" class="image" :preview-src-list="[scope.row.mainImageUrl]" />
{{ scope.row.productName }}
</div>
</template>
</el-table-column>
</ai-table>
<template #footer>
<el-button @click="isShow = false">取消</el-button>
<el-button @click="onConfirm" type="primary" :loading="btnLoading">确认</el-button>
</template>
</ai-dialog>
</template>
</ai-list>
</template>
<script>
import { sendChromeAPIMessage } from '@/api/chromeApi'
import * as XLSX from 'xlsx'
import JsonExcel from 'vue-json-excel'
export default {
name: 'SkuManage',
components: {
JsonExcel
},
data () {
return {
total: 0,
search: {
current: 1,
size: -1,
productSkuId: '',
productSkcId: '',
isShowWhite: ''
},
lableSearch: {
current: 1,
size: 100,
mallId: ''
},
lableTotal: 0,
lableList: [],
isShow: false,
skuReqParams: {
page: 1,
pageSize: 100,
SKC: '',
SKU: ''
},
isLoading: false,
addType: '1',
props: {
value: 'catId',
label: 'catName',
multiple: true,
checkStrictly: true,
lazy: true,
lazyLoad (value, resolve) {
sendChromeAPIMessage({
url: 'bg-anniston-mms/category/children/list',
needMallId: true,
data: {
parentCatId: value.level === 0 ? '' : value.value
}
}).then(res => {
if (res.errorCode === 1000000) {
resolve(res.result.categoryNodeVOS.map(v => {
return {
...v,
leaf: v.isLeaf
}
}))
}
})
}
},
targetCatId: [],
skuList: [],
chooseSkuList: [],
id: '',
fileList: [],
pageShow: false,
relationList: [],
btnLoading: false
}
},
computed: {
list () {
if (!this.skuList.length) {
return []
}
if (this.search.isShowWhite !== '1') {
return this.skuList
}
const keys = this.relationList.map(v => v.field)
return this.skuList.filter(v => keys.some(e => !v[e]))
},
currMall () {
if (!this.$store.state.mallList.length) {
return {}
}
return this.$store.state.mallList.filter(v => v.mallId === this.lableSearch.mallId)[0]
},
colConfigs () {
const fields = this.isShow ? [] : this.relationList.map(v => {
return {
prop: v.field,
label: v.name,
align: 'center'
}
})
return [
{ type: 'selection' },
{ prop: 'mallName', label: '店铺名称', align: 'left' },
{ prop: 'productName', label: '商品名称', align: 'center' },
{ prop: 'labelCode', label: '条码编码', align: 'center' },
{ prop: 'productSkcId', label: 'SKC', align: 'center' },
{ prop: 'productSkuId', label: 'SKU', align: 'center' },
{ prop: 'skuExtCode', label: 'SKU货号', align: 'center' },
{ prop: 'skuSpecName', label: '次销售属性', align: 'center' },
...fields
]
},
jsonFields () {
const obj = {}
this.colConfigs.filter(v => !!v.prop).forEach(v => {
obj[v.label] = v.prop
})
return {
...obj
}
}
},
created () {
this.id = this.$route.query.id || ''
this.getRelation()
this.getList()
},
methods: {
toAdd () {
this.$router.push('/addLabelsTemplate')
},
getRelation () {
this.$http.post(`/api/templateRelation/getRelation?templateId=${this.$route.query.id}`).then(res => {
if (res.code === 0) {
this.relationList = res.data
}
})
},
readXLSX(file) {
return new Promise(resolve => {
const reader = new FileReader()
reader.readAsBinaryString(file)
reader.onload = evt => {
const data = evt.target.result
const workbook = XLSX.read(data, { type: 'binary' })
const ws = workbook.Sheets[workbook.SheetNames[0]]
const jsonData = XLSX.utils.sheet_to_json(ws)
resolve(jsonData)
this.fileList = []
}
})
},
onExcelChange (file) {
this.pageShow = true
this.readXLSX(file.raw).then(res => {
this.$http.post(`/api/templateSku/updateBatchSku`, res.map(v => {
const result = {
templateId: this.id
}
Object.keys(this.jsonFields).forEach(item => {
result[this.jsonFields[item]] = v[item]
})
return result
})).then(res => {
if (res.code === 0) {
this.$message.success('导入成功')
this.isShow = false
this.getList()
}
this.pageShow = false
})
})
},
onSearchRest() {
this.skuReqParams.SKC = ''
this.skuReqParams.SKU = ''
},
handleSelectionChange(e) {
this.chooseSkuList = e
},
getSKCList(catIds, page) {
return new Promise(resolve => {
sendChromeAPIMessage({
url: 'bg-visage-mms/product/skc/pageQuery',
needMallId: true,
mallId: this.lableSearch.mallId,
anti: true,
data: {
page,
pageSize: 200,
catIds: catIds
}
}).then(res => {
if (res.errorCode == 1000000) {
resolve({
list: res.result.pageItems.map(v => v.productSkcId),
isHasNext: res.result.total && res.result.pageItems.length && (res.result.pageItems.length < 200 && res.result.total > 200)
})
} else {
resolve({ list: [], isHasNext: false })
}
}).catch(() => {
resolve({ list: [], isHasNext: false })
})
})
},
async onCateChange() {
let page = 1
let list = []
let isHasNext = true
this.lableList = []
this.isLoading = true
while (isHasNext) {
const result = await this.getSKCList([].concat(this.targetCatId.flat()), page)
page = page + 1
isHasNext = result.isHasNext ? true : false
list.push(...result.list)
await this.$sleepSync(1000)
}
const skcList = [...new Set(list)]
const len = Math.ceil(skcList.length / 100)
for (let i = 0; i < len; i++) {
this.skuReqParams.page = 1
this.skuReqParams.SKC = [...new Set(list)].slice(i * 100, i * 100 + 100).join(',')
await this.requestSKUList(true)
await this.$sleepSync(500)
}
},
requestSKUList(flag) {
return sendChromeAPIMessage({
url: 'bg-visage-mms/labelcode/pageQuery',
needMallId: true,
mallId: this.lableSearch.mallId,
anti: true,
data: {
page: this.skuReqParams.page,
pageSize: 200,
productSkcIdList: (['2', '1'].includes(this.addType)) ? this.skuReqParams.SKC.split(',') : [],
productSkuIdList: this.addType === '3' ? this.skuReqParams.SKU.split(',') : []
}
}).then(async (res) => {
if (res.errorCode == 1000000) {
const list = res.result.pageItems.map(v => {
return {
mallId: this.lableSearch.mallId,
mallName: this.currMall.mallName,
productName: v.productName,
productSkcId: v.labelCodeVO.productSkcId,
productSkuId: v.labelCodeVO.productSkuId,
labelCode: v.labelCodeVO.labelCode,
skuExtCode: v.labelCodeVO.skuExtCode,
skuSpecName: v.productSkuSpecList.map(item => {
return item.specName
}).join(',')
}
})
this.lableTotal = res.result.total
this.lableList.push(...list)
if (res.result.total > this.lableList.length) {
this.skuReqParams.page++
await this.$sleepSync(500)
await this.requestSKUList()
} else {
!flag && (this.isLoading = false)
}
}
})
},
getSkuList () {
if (!this.lableSearch.mallId) {
return this.$message.error('请选择店铺')
}
this.lableList = []
this.skuReqParams.page = 1
this.isLoading = true
this.requestSKUList()
},
getList () {
this.pageShow = true
this.$http.post(`/api/templateSku/getPage`, null, {
params: {
...this.search,
templateId: this.id
}
}).then(res => {
if (res.code === 0) {
this.skuList = res.data.records
}
this.pageShow = false
})
},
onConfirm () {
if (!this.chooseSkuList.length) {
return this.$message.error('请选择SKU')
}
this.btnLoading = true
this.$http.post(`/api/templateSku/addBatchSku`, this.chooseSkuList.map(v => {
return {
...v,
templateId: this.id
}
})).then(res => {
if (res.code === 0) {
this.$message.success('添加成功')
this.isShow = false
this.getList()
}
this.btnLoading = false
})
},
remove (id) {
this.$confirm('确定删除该数据?', '温馨提示', {
type: 'warning'
}).then(() => {
this.$http.post(`/api/templateSku/removeById?id=${id}`).then(res => {
if (res.code == 0) {
this.$message.success('删除成功')
this.getList()
}
})
})
},
}
}
</script>
<style scoped lang="scss">
.Template {
.search-item__wrapper {
display: flex;
align-items: center;
justify-content: space-between;
&>div {
display: flex;
align-items: center;
}
}
}
</style>