Files
temu-plugin/src/view/lables/SkuManage.vue

571 lines
17 KiB
Vue
Raw Normal View History

2024-10-18 13:37:05 +08:00
<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>
2024-10-25 15:31:01 +08:00
<el-button type="primary" size="small" @click="chooseSkuList = [], isShow = true">添加</el-button>
2024-10-27 18:29:16 +08:00
<el-upload
action
:limit="1"
:show-file-list="false"
accept=".xls,.xlsx"
:auto-upload="false"
:file-list="fileList"
:on-change="onExcelChange">
2024-10-28 18:30:26 +08:00
<el-button size="small" type="danger" :disabled="!skuList.length">Excel导入</el-button>
2024-10-27 18:29:16 +08:00
</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>
2024-10-18 13:37:05 +08:00
</template>
2024-10-28 18:30:26 +08:00
</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>
2024-10-28 22:09:57 +08:00
<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>
2024-10-28 18:30:26 +08:00
<el-button style="margin-left: 10px;" @click="getList" size="small" :loading="pageShow">查询</el-button>
2024-10-18 13:37:05 +08:00
</template>
</ai-search-bar>
<ai-table
2024-10-28 22:09:57 +08:00
:tableData="list"
2024-10-18 13:37:05 +08:00
:col-configs="colConfigs"
style="margin-top: 8px;"
@getList="getList"
2024-10-27 18:29:16 +08:00
@selection-change="handleSelectionChange"
2024-10-28 18:30:26 +08:00
v-loading="pageShow"
:isShowPagination="false">
2024-10-27 18:29:16 +08:00
<el-table-column
v-for="(item, index) in relationList"
:key="index"
:prop="item.field"
:show-overflow-tooltip="true"
:label="item.name"
align="center">
2024-10-18 13:37:05 +08:00
</el-table-column>
2024-10-28 18:30:26 +08:00
<el-table-column slot="options" label="操作" align="center" fixed="right" width="120px">
2024-10-18 13:37:05 +08:00
<template v-slot="{ row }">
<div class="table-options">
2024-10-28 18:30:26 +08:00
<el-button type="text" @click="remove(row.id)">删除</el-button>
2024-10-18 13:37:05 +08:00
</div>
</template>
2024-10-28 18:30:26 +08:00
</el-table-column>
2024-10-18 13:37:05 +08:00
</ai-table>
<ai-dialog
:visible.sync="isShow"
title="添加SKU"
width="1400px"
2024-10-28 18:30:26 +08:00
customFooter
2024-10-18 13:37:05 +08:00
@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>
2024-10-25 15:31:01 +08:00
<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>
2024-10-25 15:31:01 +08:00
<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>
2024-10-25 15:31:01 +08:00
<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>
2024-10-25 15:31:01 +08:00
<el-button style="margin-left: 10px;" @click="getSkuList" size="small" :disabled="!lableSearch.mallId" :loading="isLoading">查询</el-button>
</div>
</div>
</div>
2024-10-18 13:37:05 +08:00
<ai-table
height="370"
2024-10-25 15:31:01 +08:00
:tableData="lableList"
2024-10-18 13:37:05 +08:00
:col-configs="colConfigs"
2024-10-25 15:31:01 +08:00
:total="lableTotal"
:current.sync="lableSearch.current"
:size.sync="lableSearch.size"
2024-10-18 13:37:05 +08:00
style="margin-top: 8px;"
:pageSizes="[10, 20, 50, 100, 500, 1000]"
v-loading="isLoading"
2024-10-25 15:31:01 +08:00
:isShowPagination="false"
@getList="() => {}"
@selection-change="handleSelectionChange">
2024-10-18 13:37:05 +08:00
<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>
2024-10-28 18:30:26 +08:00
<template #footer>
<el-button @click="isShow = false">取消</el-button>
<el-button @click="onConfirm" type="primary" :loading="btnLoading">确认</el-button>
</template>
2024-10-18 13:37:05 +08:00
</ai-dialog>
</template>
</ai-list>
</template>
<script>
import { sendChromeAPIMessage } from '@/api/chromeApi'
2024-10-27 18:29:16 +08:00
import * as XLSX from 'xlsx'
import JsonExcel from 'vue-json-excel'
2024-10-18 13:37:05 +08:00
export default {
name: 'SkuManage',
2024-10-18 13:37:05 +08:00
2024-10-27 18:29:16 +08:00
components: {
JsonExcel
},
2024-10-18 13:37:05 +08:00
data () {
return {
total: 0,
search: {
current: 1,
2024-10-28 18:30:26 +08:00
size: -1,
productSkuId: '',
2024-10-28 22:09:57 +08:00
productSkcId: '',
isShowWhite: ''
2024-10-18 13:37:05 +08:00
},
2024-10-25 15:31:01 +08:00
lableSearch: {
2024-10-18 13:37:05 +08:00
current: 1,
size: 100,
mallId: ''
},
2024-10-25 15:31:01 +08:00
lableTotal: 0,
lableList: [],
2024-10-18 13:37:05 +08:00
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
}
}))
}
})
}
2024-10-18 13:37:05 +08:00
},
2024-10-25 15:31:01 +08:00
targetCatId: [],
skuList: [],
2024-10-27 18:29:16 +08:00
chooseSkuList: [],
id: '',
fileList: [],
pageShow: false,
2024-10-28 18:30:26 +08:00
relationList: [],
btnLoading: false
2024-10-18 13:37:05 +08:00
}
},
computed: {
2024-10-28 22:09:57 +08:00
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]))
},
2024-10-18 13:37:05 +08:00
currMall () {
if (!this.$store.state.mallList.length) {
return {}
}
2024-10-25 15:31:01 +08:00
return this.$store.state.mallList.filter(v => v.mallId === this.lableSearch.mallId)[0]
2024-10-27 18:29:16 +08:00
},
colConfigs () {
2024-10-28 18:30:26 +08:00
const fields = this.isShow ? [] : this.relationList.map(v => {
2024-10-27 18:29:16 +08:00
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' },
2024-10-28 18:30:26 +08:00
...fields
2024-10-27 18:29:16 +08:00
]
},
jsonFields () {
const obj = {}
this.colConfigs.filter(v => !!v.prop).forEach(v => {
obj[v.label] = v.prop
})
return {
...obj
}
2024-10-18 13:37:05 +08:00
}
},
created () {
2024-10-27 18:29:16 +08:00
this.id = this.$route.query.id || ''
this.getRelation()
2024-10-18 13:37:05 +08:00
this.getList()
},
methods: {
toAdd () {
this.$router.push('/addLabelsTemplate')
},
2024-10-27 18:29:16 +08:00
getRelation () {
this.$http.post(`/api/templateRelation/getRelation?templateId=${this.$route.query.id}`).then(res => {
if (res.code === 0) {
this.relationList = res.data
}
})
},
readXLSX(file) {
2024-10-28 22:09:57 +08:00
return new Promise(resolve => {
2024-10-27 18:29:16 +08:00
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) {
2024-10-25 15:31:01 +08:00
this.chooseSkuList = e
},
getSKCList(catIds, page) {
return new Promise(resolve => {
sendChromeAPIMessage({
url: 'bg-visage-mms/product/skc/pageQuery',
needMallId: true,
2024-10-25 15:31:01 +08:00
mallId: this.lableSearch.mallId,
anti: true,
data: {
2024-10-28 22:09:57 +08:00
page,
pageSize: 200,
catIds: catIds
}
}).then(res => {
if (res.errorCode == 1000000) {
resolve({
list: res.result.pageItems.map(v => v.productSkcId),
2024-10-28 22:09:57 +08:00
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() {
2024-10-28 18:30:26 +08:00
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)
}
2024-10-25 15:31:01 +08:00
2024-10-28 18:30:26 +08:00
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)
}
},
2024-10-25 15:31:01 +08:00
requestSKUList(flag) {
return sendChromeAPIMessage({
2024-10-18 13:37:05 +08:00
url: 'bg-visage-mms/labelcode/pageQuery',
needMallId: true,
2024-10-25 15:31:01 +08:00
mallId: this.lableSearch.mallId,
2024-10-18 13:37:05 +08:00
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(',') : []
2024-10-18 13:37:05 +08:00
}
}).then(async (res) => {
if (res.errorCode == 1000000) {
const list = res.result.pageItems.map(v => {
return {
2024-10-25 15:31:01 +08:00
mallId: this.lableSearch.mallId,
2024-10-18 13:37:05 +08:00
mallName: this.currMall.mallName,
productName: v.productName,
2024-10-27 18:29:16 +08:00
productSkcId: v.labelCodeVO.productSkcId,
2024-10-18 13:37:05 +08:00
productSkuId: v.labelCodeVO.productSkuId,
labelCode: v.labelCodeVO.labelCode,
skuExtCode: v.labelCodeVO.skuExtCode,
skuSpecName: v.productSkuSpecList.map(item => {
return item.specName
}).join(',')
}
})
2024-10-25 15:31:01 +08:00
this.lableTotal = res.result.total
this.lableList.push(...list)
2024-10-18 13:37:05 +08:00
2024-10-25 15:31:01 +08:00
if (res.result.total > this.lableList.length) {
2024-10-18 13:37:05 +08:00
this.skuReqParams.page++
2024-10-28 18:30:26 +08:00
await this.$sleepSync(500)
await this.requestSKUList()
2024-10-18 13:37:05 +08:00
} else {
2024-10-25 15:31:01 +08:00
!flag && (this.isLoading = false)
2024-10-18 13:37:05 +08:00
}
}
})
},
getSkuList () {
2024-10-25 15:31:01 +08:00
if (!this.lableSearch.mallId) {
2024-10-18 13:37:05 +08:00
return this.$message.error('请选择店铺')
}
2024-10-28 18:30:26 +08:00
this.lableList = []
this.skuReqParams.page = 1
this.isLoading = true
this.requestSKUList()
2024-10-18 13:37:05 +08:00
},
getList () {
2024-10-28 18:30:26 +08:00
this.pageShow = true
this.$http.post(`/api/templateSku/getPage`, null, {
params: {
...this.search,
templateId: this.id
}
}).then(res => {
2024-10-18 13:37:05 +08:00
if (res.code === 0) {
2024-10-27 18:29:16 +08:00
this.skuList = res.data.records
2024-10-18 13:37:05 +08:00
}
2024-10-28 18:30:26 +08:00
this.pageShow = false
2024-10-18 13:37:05 +08:00
})
},
onConfirm () {
2024-10-27 18:29:16 +08:00
if (!this.chooseSkuList.length) {
return this.$message.error('请选择SKU')
}
2024-10-28 18:30:26 +08:00
this.btnLoading = true
2024-10-27 18:29:16 +08:00
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()
}
2024-10-28 18:30:26 +08:00
2024-10-30 22:01:46 +08:00
this.btnLoading = false
}).catch(() => {
2024-10-28 18:30:26 +08:00
this.btnLoading = false
2024-10-27 18:29:16 +08:00
})
2024-10-28 18:30:26 +08:00
},
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()
}
})
})
},
2024-10-18 13:37:05 +08:00
}
}
</script>
<style scoped lang="scss">
.Template {
.search-item__wrapper {
display: flex;
align-items: center;
justify-content: space-between;
&>div {
display: flex;
align-items: center;
}
}
}
2024-10-18 13:37:05 +08:00
</style>