Files
temu-plugin/src/view/ExportSaleData.vue
刘仕伟 3db7e387f1 更新
2023-12-20 22:09:55 +08:00

853 lines
26 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="list" v-loading="isLoading" element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading">
<ai-title
slot="title"
title="销售数据"
isShowBottomBorder>
<template #rightBtn>
<div class="title-right">
<div>
<label style="width:90px">统计类型</label>
<el-select v-model="type" @change="changeDataType" placeholder="请选择" size="small">
<el-option
key="0"
label="销售数据"
value="0">
</el-option>
<el-option
key="1"
label="近30天销售(按SKU)"
value="1">
</el-option>
<el-option
key="2"
label="近30天销售(按SKC)"
value="2">
</el-option>
</el-select>
</div>
<div>
<label style="width:90px">店铺</label>
<el-select v-model="mallId" @change="beforeGetList" 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>
</template>
</ai-title>
<template slot="content" v-if="type === '0'">
<div class="top">
<div class="item">
<h2>今日销量</h2>
<p>{{ todayTotal }}</p>
</div>
<div class="item">
<h2>今日销售额</h2>
<p>{{ todayMoney }}</p>
</div>
<div class="item">
<h2>库存总量</h2>
<p>{{ inventoryTotal }}</p>
</div>
<div class="item">
<h2>库存总额</h2>
<p>{{ inventoryMoeny }}</p>
</div>
<div class="item">
<h2>已收货总量()</h2>
<p>{{ deliveryTotal }}</p>
</div>
<div class="item">
<h2>已收货总货值</h2>
<p>{{ deliveryMoeny }}</p>
</div>
</div>
<ai-card title="数据明细" style="padding-bottom: 40px;">
<template #right>
<json-excel
:data="list"
v-if="type === '0'"
:fields="jsonFields"
:before-generate = "startDownload"
name="销售数据.xls"
worksheet="销售统计">
<el-button type="primary">导出数据</el-button>
</json-excel>
</template>
<ai-table
ref="table0"
:isShowPagination="false"
:tableData="list"
height="500"
:col-configs="colConfigs"
:total="list.length"
style="margin-top: 8px;"
@getList="() => {}">
</ai-table>
</ai-card>
</template>
<template slot="content" v-if="type === '1'">
<ai-card title="数据明细" style="padding-bottom: 40px;">
<template #right>
<json-excel
v-if="type === '1'"
:data="last30DaySkuList"
:fields="last30DaysSkuJsonFields"
:before-generate = "startDownload"
name="近30天销售数据_按SKU.xls"
worksheet="近30天销售统计_按SKU">
<el-button type="primary">导出数据</el-button>
</json-excel>
</template>
<ai-table
ref="table1"
:isShowPagination="false"
:tableData="last30DaySkuList"
height="500"
:col-configs="col30DaysSkuConfigs"
:total="last30DaySkuList.length"
style="margin-top: 8px;"
@getList="() => {}">
</ai-table>
</ai-card>
</template>
<template slot="content" v-if="type === '2'">
<ai-card title="数据明细" style="padding-bottom: 40px;">
<template #right>
<json-excel
v-if="type === '2'"
:data="last30DaySkcList"
:fields="last30DaysSkcJsonFields"
:before-generate = "startDownload"
name="近30天销售数据_按SKC.xls"
worksheet="近30天销售统计_按SKC">
<el-button type="primary">导出数据</el-button>
</json-excel>
</template>
<ai-table
ref="table2"
:isShowPagination="false"
:tableData="last30DaySkcList"
height="500"
:col-configs="col30DaysSkcConfigs"
:total="last30DaySkcList.length"
style="margin-top: 8px;"
@getList="() => {}">
</ai-table>
</ai-card>
</template>
</ai-list>
</template>
<script>
import { mapState } from 'vuex'
import {sendChromeAPIMessage} from '@/api/chromeApi'
import JsonExcel from 'vue-json-excel'
import {formatDate} from '@/utils/date'
import { Message } from 'element-ui'
export default {
name: 'ExportSaleData',
data () {
return {
list: [],
last30DaySkcList: [],
last30DaySkuList: [],
mallId: '',
mallName: '',
type: '0',
isLoading: false,
pageSize: 100,
currentPage: 1,
todayTotal: 0,
todayMoney: 0.0,
inventoryTotal: 0,
inventoryMoeny: 0.0,
deliveryTotal: 0,
deliveryMoeny: 0.0,
allProductList: [],
startDate: '',
endDate: '',
skuIds: [],
jsonFields: {
"商品名称": "productName",
"SPU": "productId",
"SKC": "productSkcId",
"SKU ID": "productSkuId",
"SKU属性": "className",
"SKU货号": "skuExtCode",
"加入站点时长": "onSalesDurationOffline",
"图片链接": "productSkcPicture",
"申报价格(CNY)": {
"field": "supplierPrice",
callback: (value) => {
return value /100;
}
},
"开款核价状态": {
"field": "isVerifyPrice",
callback: (value) => {
return value ? '核价通过': '核价未通过 / 无法备货';
}
},
"缺货数量": "lackQuantity",
"建议备货量": "adviceQuantity",
"可售天数": "availableSaleDays",
"库存可售天数": "availableSaleDaysFromInventory",
"仓内库存可售天数": "warehouseAvailableSaleDays",
"近7日用户加购数量": "inCartNumber7d",
"用户累计加购数量": "inCardNumber",
"已订阅待提醒到货": "nomsgSubsCntCntSth",
"销售数据 - 今日": "todaySaleVolume",
"销售数据 - 近7日": "lastSevenDaysSaleVolume",
"销售数据 - 近30天": "lastThirtyDaysSaleVolume",
"库存数据 - 仓内可用库存": "inventoryNumInfo.warehouseInventoryNum",
"库存数据 - 仓内暂不可用库存": "inventoryNumInfo.unavailableWarehouseInventoryNum",
"库存数据 - 已发货库存": "inventoryNumInfo.waitReceiveNum",
"库存数据 - 已下单待发货库存": "inventoryNumInfo.waitDeliveryInventoryNum",
"库存数据 - 待审核备货库存": "inventoryNumInfo.waitApproveInventoryNum",
"VMI备货单数 - 待发货": "vmiOrderInfo.waitDeliveryNum",
"VMI备货单数 - 在途单数": "vmiOrderInfo.transportationNum",
"VMI备货单数 - 发货延迟": "vmiOrderInfo.deliveryDelayNum",
"VMI备货单数 - 到货延迟": "vmiOrderInfo.arrivalDelayNum",
"非VMI备货单数 - 待发货": "notVmiOrderInfo.waitDeliveryNum",
"非VMI备货单数 - 在途单数": "notVmiOrderInfo.transportationNum",
"非VMI备货单数 - 发货延迟": "notVmiOrderInfo.deliveryDelayNum",
"非VMI备货单数 - 到货延迟": "notVmiOrderInfo.arrivalDelayNum",
"备货逻辑": "purchaseConfig",
"库存货值(CNY)": "productTotalPrice",
"店铺名称": "mallName",
"评分": "mark",
"是否热销": 'hotTag'
}
}
},
computed: {
...mapState(['mallList']),
colConfigs () {
return [
{
prop: 'productName',
label: '商品名称',
"show-overflow-tooltip": true,
width: '280px',
align: 'left',
fixed: 'left'
},
{
prop: 'mallName',
label: '店铺名称',
"show-overflow-tooltip": true,
width: '120px',
align: 'left',
fixed: 'left'
},
{
prop: 'mark',
label: '评分',
"show-overflow-tooltip": true,
width: '80px',
align: 'left'
},
{
prop: 'hotTag',
label: '是否热销',
"show-overflow-tooltip": true,
width: '80px',
align: 'left'
},
{
prop: 'productId',
label: 'SPU',
align: 'center'
},
{
prop: 'productSkcId',
label: 'SKC',
align: 'center'
},
{
prop: 'productSkuId',
label: 'SKU ID',
align: 'center'
},
{
prop: 'className',
label: 'SKU属性',
align: 'center'
},
{
prop: 'skuExtCode',
label: 'SKU货号',
align: 'center'
},
{
prop: 'onSalesDurationOffline',
label: '加入站点时长',
align: 'center'
},
{
prop: 'isVerifyPrice',
label: '开款核价状态',
align: 'center',
format: v => v ? '核价通过': '核价未通过 / 无法备货'
},
{
prop: 'supplierPrice',
label: '申报价格(CNY)',
align: 'center',
format: v => v / 100,
fixed: "right"
},
{
prop: 'warehouseInventoryNum',
label: '仓内可用库存',
align: 'center',
fixed: "right",
sortable: true,
'sort-method': (a, b) => {
return a.warehouseInventoryNum - b.warehouseInventoryNum
}
},
{
prop: 'productTotalPrice',
label: '库存货值(CNY)',
align: 'center',
fixed: "right",
sortable: true,
'sort-method': (a, b) => {
return a.productTotalPrice - b.productTotalPrice
}
},
{
prop: 'purchaseConfig',
label: '备货逻辑',
align: 'center',
fixed: "right",
sortable: true,
'sort-method': (a, b) => {
if (a.purchaseConfig > b.purchaseConfig) {
return 1
} else if (a.purchaseConfig == b.purchaseConfig) {
return 0
} else {
return -1
}
}
},
]
},
col30DaysSkcConfigs () {
let config = [
{
prop: 'productName',
label: '商品名称',
"show-overflow-tooltip": true,
width: '300px',
align: 'left',
fixed: 'left'
},
{
prop: 'mallName',
label: '店铺名称',
"show-overflow-tooltip": true,
width: '120px',
align: 'left',
fixed: 'left'
},
{
prop: 'productId',
label: 'SPU',
width: '120px',
align: 'center',
fixed: 'left'
},
{
prop: 'productSkcId',
label: 'SKC',
width: '120px',
align: 'center',
fixed: 'left'
},
{
prop: 'skcExtCode',
label: 'SKC货号',
width: '120px',
align: 'center',
fixed: 'left'
}
]
let date = new Date()
date.setDate(date.getDate())
for (let i = 0; i < 30; i++) {
date.setDate(date.getDate() - 1)
let dateStr = formatDate(date)
config.push({
prop: dateStr,
label: dateStr,
width: '100px',
align: 'center',
sortable: true,
'sort-method': (a, b) => {
if (a > b) {
return 1
} else if (a == b) {
return 0
} else {
return -1
}
}})
}
return config
},
last30DaysSkcJsonFields () {
let jsonFields = {
"商品名称": "productName",
"店铺名称": "mallName",
"SPU": "productId",
"SKC": "productSkcId",
"SKC货号": "skcExtCode",
"图片链接": "productSkcPicture"
}
let date = new Date()
date.setDate(date.getDate() )
for (let i = 0; i < 30; i++) {
date.setDate(date.getDate() - 1)
let dateStr = formatDate(date)
jsonFields[dateStr] = dateStr
}
return jsonFields
},
col30DaysSkuConfigs () {
let config = [
{
prop: 'productName',
label: '商品名称',
"show-overflow-tooltip": true,
width: '300px',
align: 'left',
fixed: 'left'
},
{
prop: 'mallName',
label: '店铺名称',
"show-overflow-tooltip": true,
width: '120px',
align: 'left',
fixed: 'left'
},
{
prop: 'productId',
label: 'SPU',
width: '120px',
align: 'center',
fixed: 'left'
},
{
prop: 'productSkcId',
label: 'SKC',
width: '120px',
align: 'center',
fixed: 'left'
},
{
prop: 'skcExtCode',
label: 'SKC货号',
width: '120px',
align: 'center',
fixed: 'left'
},
{
prop: 'productSkuId',
label: 'SKU ID',
width: '120px',
align: 'center',
fixed: 'left'
},
{
prop: 'skuExtCode',
label: 'SKU货号',
width: '120px',
align: 'center',
fixed: 'left'
}
]
let date = new Date()
date.setDate(date.getDate())
for (let i = 0; i < 30; i++) {
date.setDate(date.getDate() - 1)
let dateStr = formatDate(date)
config.push({
prop: dateStr,
label: dateStr,
width: '100px',
align: 'center',
sortable: true,
'sort-method': (a, b) => {
if (a > b) {
return 1
} else if (a == b) {
return 0
} else {
return -1
}
}})
}
return config
},
last30DaysSkuJsonFields () {
let jsonFields = {
"商品名称": "productName",
"店铺名称": "mallName",
"SPU": "productId",
"SKC": "productSkcId",
"SKC货号": "skcExtCode",
"SKU ID": "productSkuId",
"SKU货号": "skuExtCode",
"图片链接": "productSkcPicture"
}
let date = new Date()
date.setDate(date.getDate() )
for (let i = 0; i < 30; i++) {
date.setDate(date.getDate() - 1)
let dateStr = formatDate(date)
jsonFields[dateStr] = dateStr
}
return jsonFields
}
},
components: {
JsonExcel
},
created () {
let date = new Date()
date.setDate(date.getDate() - 30)
this.startDate = formatDate(date)
date.setDate(date.getDate() + 29)
this.endDate = formatDate(date)
},
methods: {
changeDataType() {
this.$nextTick(() => { //在数据加载完,重新渲染表格
this.$refs['table'+this.type].doLayout();
})
},
beforeGetList() {
this.list = []
this.currentPage = 1
this.todayMoney = 0.0
this.todayTotal = 0
this.inventoryMoeny = 0.0
this.inventoryTotal = 0
this.deliveryTotal = 0
this.deliveryMoeny = 0.0
this.allProductList = []
if (!this.mallId) {
Message.error("请先选择店铺")
return
}
let mallInfo = this.mallList.filter(item => {
return item.mallId == this.mallId
})
this.mallName = mallInfo[0].mallName
this.isLoading = true
this.$userCheck(this.mallId).then(() => {
this.last30DaySkcList = []
this.getAllProductList()
this.getList()
}).catch((err) => {
console.log(err)
this.isLoading = false
})
},
getAllProductList() {
this.$http.post('/api/deliveryOrder/totalDelivery',null, {
params: {mallId: this.mallId}
}).then(res => {
if (res.code === 0) {
this.deliveryTotal = res.data
}
})
this.$http.post('/api/deliveryOrder/getAllProductList', null, {
params: {mallId: this.mallId}
}).then(res => {
if (res.code == 0) {
this.allProductList = res.data
}
})
},
getList () {
sendChromeAPIMessage({
url: 'marvel-mms/cn/api/kiana/venom/sales/management/list',
needMallId: true,
mallId: this.mallId,
data: {
pageNo: this.currentPage,
pageSize: this.pageSize,
isLack: 0,
priceAdjustRecentDays: 7
}}).then((res) => {
if (res.errorCode == 1000000) {
for(let i = 0;i < res.result.subOrderList.length; i++) {
let item = res.result.subOrderList[i];
let data = {};
data.productName = item.productName;
data.productId = item.productId;
data.productSkcId = item.productSkcId;
data.skcExtCode = item.skcExtCode;
data.purchaseConfig = item.purchaseConfig;
data.productSkcPicture = item.productSkcPicture;
data.mark = item.mark.toFixed(1)
data.hotTag = item.hotTag ? '是': '否'
data.mallName = this.mallName;
this.last30DaySkcList.push({
productName: item.productName,
mallName: this.mallName,
productId: item.productId,
productSkcId: item.productSkcId,
skcExtCode: item.skcExtCode,
productSkcPicture: item.productSkcPicture
})
if (item.onSalesDurationOffline == 0) {
data.onSalesDurationOffline = '-天'
} else {
data.onSalesDurationOffline = item.onSalesDurationOffline + '天'
}
for(let j = 0;j < item.skuQuantityDetailList.length; j++) {
data = {...data, ...item.skuQuantityDetailList[j],
productTotalPrice: ((item.skuQuantityDetailList[j].supplierPrice / 100) * item.skuQuantityDetailList[j].inventoryNumInfo.warehouseInventoryNum).toFixed(2),
warehouseInventoryNum: item.skuQuantityDetailList[j].inventoryNumInfo.warehouseInventoryNum}
this.todayTotal += item.skuQuantityDetailList[j].todaySaleVolume
this.todayMoney += new Number(((item.skuQuantityDetailList[j].supplierPrice / 100) * item.skuQuantityDetailList[j].todaySaleVolume).toFixed(2))
this.todayMoney = new Number(this.todayMoney.toFixed(2))
this.inventoryTotal += item.skuQuantityDetailList[j].inventoryNumInfo.warehouseInventoryNum
this.inventoryMoeny += new Number(((item.skuQuantityDetailList[j].supplierPrice / 100) * item.skuQuantityDetailList[j].inventoryNumInfo.warehouseInventoryNum).toFixed(2))
this.inventoryMoeny = new Number(this.inventoryMoeny.toFixed(2))
this.list.push(data);
// 计算已发货货值
for(let k = 0; k < this.allProductList.length; k++) {
if (this.allProductList[k].product_sku_id == data.productSkuId) {
this.deliveryMoeny += (item.skuQuantityDetailList[j].supplierPrice / 100) * this.allProductList[k].product_sku_number
this.deliveryMoeny = new Number(this.deliveryMoeny.toFixed(2))
}
}
}
}
if (this.pageSize == res.result.subOrderList.length) {
this.currentPage ++
setTimeout(() => {
this.getList()
}, 1500)
} else {
this.isLoading = false
Message.success('销售数据加载完成,可进行导出')
// 初始化SKC数据列表
this.last30DaySkcList = this.last30DaySkcList.map(item => {
let date = new Date()
date.setDate(date.getDate() - 31)
for (let i = 0; i < 30; i++) {
date.setDate(date.getDate() + 1)
let dateStr = formatDate(date)
item[dateStr] = 0
}
return item
})
// 初始化SKU数据列表
this.last30DaySkuList = this.list.map(item => {
let temp = {
productName: item.productName,
mallName: this.mallName,
productId: item.productId,
productSkcId: item.productSkcId,
productSkuId: item.productSkuId,
skuExtCode: item.skuExtCode,
skcExtCode: item.skcExtCode,
productSkcPicture: item.productSkcPicture
}
let date = new Date()
date.setDate(date.getDate() - 31)
for (let i = 0; i < 30; i++) {
date.setDate(date.getDate() + 1)
let dateStr = formatDate(date)
temp[dateStr] = 0
}
return temp
})
this.skuIds = this.list.map(item => {
return item.productSkuId
})
this.getSkuDetailList(0)
}
} else {
setTimeout(() => {
this.getList()
}, 1500)
// Message.error("【拼多多】" + res.errorMsg + ", 请重新尝试加载")
}
}).catch(() => {
this.isLoading = false
})
},
getSkuDetailList (page) {
let tempSkuId = []
let i = page * 500
let j = 0
for (; i < this.skuIds.length; i++) {
tempSkuId.push(this.skuIds[i])
j ++
if (j == 500) break
}
if (tempSkuId.length == 0) return
sendChromeAPIMessage({
url: 'oms/bg/venom/api/supplier/sales/management/querySkuSalesNumber',
needMallId: true,
mallId: this.mallId,
data: {
"productSkuIds": tempSkuId,
"startDate": this.startDate,
// "startDate": '2023-01-17',
"endDate": this.endDate
}}).then((res) => {
if (res.errorCode == 1000000) {
for (let i = 0; i < res.result.length; i++) {
for (let j = 0; j < this.last30DaySkuList.length; j++) {
if (this.last30DaySkuList[j].productSkuId == res.result[i].prodSkuId) {
this.last30DaySkuList[j][res.result[i].date] = res.result[i].salesNumber
for (let k = 0; k < this.last30DaySkcList.length; k++) {
if (this.last30DaySkcList[k].productSkcId == this.last30DaySkuList[j].productSkcId) {
this.last30DaySkcList[k][res.result[i].date] = this.last30DaySkcList[k][res.result[i].date] + res.result[i].salesNumber
break
}
}
break
}
}
}
this.getSkuDetailList(page + 1)
} else {
setTimeout(() => {
this.getSkuDetailList(page)
}, 1500)
}
})
},
startDownload() {
this.$http.post('/api/malluser/info').then(res => {
if (res.code == 0) {
this.$store.commit('setUserInfo', res.data)
if (res.data.flag != 1) {
Message.error('您的账号未激活或已失效,请激活后使用')
this.$store.commit('setActiveDlgShow', true)
return;
}
}
})
}
}
}
</script>
<style scoped lang="scss">
.list {
.title-right {
display: flex;
align-items: center;
& > div:first-child {
margin-right: 20px;
}
}
::v-deep.ai-list {
.ai-list__content--right-wrapper {
background: transparent;
box-shadow: none;
padding: 0!important;
}
}
.top {
display: flex;
justify-content: space-between;
margin-bottom: 24px;
.item {
flex: 1;
margin-right: 20px;
padding: 16px 24px;
background: #FFF;
box-shadow: 0px 4px 6px -2px rgba(15, 15, 21, 0.15);
border-radius: 4px;
&:last-child {
margin-right: 0;
}
&:nth-of-type(1) {
color: #2266ff;
}
&:nth-of-type(2) {
color: #f8b426;
}
&:nth-of-type(3) {
color: #21aa99;
}
&:nth-of-type(4) {
color: #F46;
}
&:nth-of-type(5) {
color: #11A265;
}
h2 {
margin-bottom: 30px;
font-size: 16px;
color: #999;
}
p {
font-weight: 600;
font-size: 28px;
}
}
}
}
.like {
cursor: pointer;
font-size: 25px;
display: inline-block;
}
</style>