This commit is contained in:
liushiwei
2024-06-29 11:51:35 +08:00
parent 9253427bcc
commit 6356004d74
29 changed files with 3905 additions and 501 deletions

View File

@@ -13,6 +13,8 @@
"crypto-js": "^4.0.0",
"dayjs": "^1.11.9",
"element-ui": "^2.15.13",
"file-saver": "^2.0.5",
"html2canvas": "^1.4.1",
"query-string": "^9.0.0",
"spark-md5": "^3.0.2",
"v-viewer": "^1.6.4",
@@ -22,7 +24,8 @@
"vue-qr": "^4.0.9",
"vue-router": "^3.2.0",
"vuex": "^3.4.0",
"vuex-persistedstate": "^4.1.0"
"vuex-persistedstate": "^4.1.0",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@babel/core": "^7.12.16",

View File

@@ -1,3 +1,8 @@
function matchSheinDomain(url) {
const urlPattern = /https:\/\/([\da-z\.-]+)\.shein\.com\/([\S.-]+)-p-([\S.-]+)/
return urlPattern.test(url);
}
function init() {
if (window.location.href.startsWith('https://www.aliexpress.com/item/')) {
const popup = document.createElement("div")
@@ -187,6 +192,111 @@ function init() {
})
document.body.appendChild(popup)
}
} else if (matchSheinDomain(window.location.href)) {
const popup = document.createElement("div")
popup.innerText = "下载图片"
const styles = {
position: "fixed",
right: '10px',
top: '60px',
zIndex: 9999,
padding: "8px",
background: "#409EFF",
color: "#fff",
borderRadius: "8px",
cursor: "pointer"
}
for (const e in styles) {
popup.style[e] = styles[e]
}
popup.addEventListener('click', async () => {
var baseList = [];
var downloadList = []
let bannerIdx = 1, detailIdx = 1
baseList.push({type: 0, index: bannerIdx++, src: window.gbRawData.productIntroData.goods_imgs.main_image.origin_image})
var detailImages = window.gbRawData.productIntroData.goods_imgs.detail_image
for (var i = 0; i < detailImages.length; i++) {
if (!(detailImages[i].isMoreDetail)) {
baseList.push({type: 0, index: bannerIdx++, src: detailImages[i].origin_image})
} else {
baseList.push({type: 1, index: detailIdx++, src: detailImages[i].origin_image})
}
}
var video = document.querySelector('video')
if (window.gbRawData.productIntroData.goods_imgs.video_url) {
baseList.push({type: 2, index: 1, src: window.gbRawData.productIntroData.goods_imgs.video_url})
}
var zip = new JSZip();
var imgsBanner = zip.folder("轮播图");
var imgsDetail = zip.folder("详情图");
var videos = zip.folder("视频");
for (var k = 0; k < baseList.length; k++) {
let type = baseList[k].type
let index = baseList[k].index
if (type == 2) {
let x = new XMLHttpRequest()
x.open('GET', baseList[k].src, true)
x.responseType = 'blob'
x.onload = (e) => {
downloadList.push({type: type, index: index, data: x.response});
if (downloadList.length === baseList.length && downloadList.length > 0) {
for (let l = 0; l < downloadList.length; l++) {
if (downloadList[l].type == '0') {
imgsDetail.file(`详情图${downloadList[l].index}.png`, downloadList[l].data, { base64: true });
} else if (downloadList[l].type == '1') {
imgsBanner.file(`轮播图${downloadList[l].index}.png`, downloadList[l].data, { base64: true });
} else if (downloadList[l].type == '2') {
videos.file(`视频.mp4`, downloadList[l].data, { Blob: true });
}
}
zip.generateAsync({ type: "blob" }).then(function (content) {
// see FileSaver.js
saveAs(content, "shein_" + id + ".zip");
});
}
}
x.send()
} else {
let image = new Image();
// 解决跨域 Canvas 污染问题
image.setAttribute("crossOrigin", "anonymous");
image.onload = function () {
let canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
let context = canvas.getContext("2d");
context.drawImage(image, 0, 0, image.width, image.height);
let url = canvas.toDataURL(); // 得到图片的base64编码数据
canvas.toDataURL("image/png");
downloadList.push({type: type, index: index, data: url.substring(22)}); // 去掉base64编码前的 data:image/png;base64,
if (downloadList.length === baseList.length && downloadList.length > 0) {
for (let l = 0; l < downloadList.length; l++) {
if (downloadList[l].type == '0') {
imgsDetail.file(`详情图${downloadList[l].index}.png`, downloadList[l].data, { base64: true });
} else if (downloadList[l].type == '1') {
imgsBanner.file(`轮播图${downloadList[l].index}.png`, downloadList[l].data, { base64: true });
} else if (downloadList[l].type == '2') {
videos.file(`视频.mp4`, downloadList[l].data, { Blob: true });
}
}
zip.generateAsync({ type: "blob" }).then(function (content) {
// see FileSaver.js
saveAs(content, "shein_" + id + ".zip");
});
}
};
image.src = baseList[k].src;
}
}
})
document.body.appendChild(popup)
}
}

29
public/rules_7.json Normal file
View File

@@ -0,0 +1,29 @@
[
{
"id": 18,
"priority": 1,
"action": {
"type": "modifyHeaders",
"requestHeaders": [
{
"header": "Origin",
"operation": "set",
"value": "https://img.ltwebstatic.com"
},
{
"header": "Referer",
"operation": "set",
"value": "https://img.ltwebstatic.com"
},
{
"header": "Sec-Fetch-Mode",
"operation": "set",
"value": "no-cors"
}
]
},
"condition": {
"urlFilter": "||img.ltwebstatic.com"
}
}
]

24
public/rules_8.json Normal file
View File

@@ -0,0 +1,24 @@
[
{
"id": 19,
"priority": 1,
"action": {
"type": "modifyHeaders",
"requestHeaders": [
{
"header": "Origin",
"operation": "set",
"value": "https://www.geiwohuo.com/"
},
{
"header": "Referer",
"operation": "set",
"value": "https://www.geiwohuo.com/"
}
]
},
"condition": {
"urlFilter": "||www.geiwohuo.com"
}
}
]

24
public/rules_9.json Normal file
View File

@@ -0,0 +1,24 @@
[
{
"id": 20,
"priority": 1,
"action": {
"type": "modifyHeaders",
"requestHeaders": [
{
"header": "Origin",
"operation": "set",
"value": "https://agentseller.temu.com/"
},
{
"header": "Referer",
"operation": "set",
"value": "https://agentseller.temu.com/"
}
]
},
"condition": {
"urlFilter": "||agentseller.temu.com"
}
}
]

View File

@@ -26,6 +26,32 @@ export async function sendChromeAPIMessage(message) {
})
}
/**
* 向Chrome发送消息
* @param message 消息
*/
export async function sendTemuSellerAgentMessage(message) {
message.type = 'api'
if (!message.url.startsWith('http')) {
message.url = "https://agentseller.temu.com/" + message.url;
}
message.anti = message.anti || false
if (message.needMallId) {
// 如果参数中没有携带MallId则从state中获取
if (!message.mallId) {
message.mallId = store.state.mallId;
}
}
if (message.anti) {
message.anti = await genAnti.a()
}
return new Promise((resolve) => {
// @ts-ignore
chrome.runtime.sendMessage(message, resolve)
})
}
/**
* 向Chrome发送消息
* @param message 消息
@@ -74,7 +100,11 @@ export async function sendSheinAPIMessage(message) {
*/
export async function sendGeiwohuoAPIMessage(message) {
message.type = 'geiwohuoApi'
message.url = "https://sso.geiwohuo.com/" + message.url;
if (message.isWWW) {
message.url = "https://www.geiwohuo.com/" + message.url;
} else {
message.url = "https://sso.geiwohuo.com/" + message.url;
}
return new Promise((resolve) => {
// @ts-ignore
chrome.runtime.sendMessage(message, resolve)

View File

@@ -368,13 +368,17 @@ export default {
let content = data
let i = 0
if (this.form.isSameCategory) {
let res2 = await this.$http.post('/api/innerCategory/fullById',null , {
/*let res2 = await this.$http.post('/api/innerCategory/fullById',null , {
params: {
id: reqData.catId
}
})
for (; i < res2.data.length; i++) {
content['cat' + (i+1) + 'Id'] = res2.data[i]
})*/
for (; i < 10; i++) {
if (content['cat' + (i+1) + 'Id']) {
continue
}else {
break
}
}
let res3 = await sendChromeAPIMessage({
@@ -422,6 +426,9 @@ export default {
content['cat' + (i+1) + 'Id'] = this.form.targetCatId[i]
}
}
if (content['cat'+(i+1)+'Id'] != reqData.catId) {
content['cat'+(++i)+'Id'] = reqData.catId
}
for (; i < 10; i++) {
content['cat' + (i+1) + 'Id'] = ''
}

View File

@@ -21,7 +21,8 @@
"*://*.alicdn.com/",
"*://*.amazon.com/",
"*://*.shein.com/",
"*://*.geiwohuo.com/"
"*://*.geiwohuo.com/",
"*://*.ltwebstatic.com/"
],
"permissions": [
"cookies",
@@ -51,13 +52,26 @@
"id": "6",
"enabled": true,
"path": "rules_6.json"
},{
"id": "7",
"enabled": true,
"path": "rules_7.json"
},{
"id": "8",
"enabled": true,
"path": "rules_8.json"
},{
"id": "9",
"enabled": true,
"path": "rules_9.json"
}]
},
"content_scripts": [
{
"matches": [
"*://*.aliexpress.com/item/*",
"*://*.amazon.com/*"
"*://*.amazon.com/*",
"*://*.shein.com/*"
],
"js": [
"/content.js"
@@ -67,7 +81,7 @@
"web_accessible_resources": [
{
"resources": [ "js/download.js","js/jszip.min.js","js/FileSaver.js" ],
"matches": [ "*://*.aliexpress.com/*", "*://*.amazon.com/*" ]
"matches": [ "*://*.aliexpress.com/*", "*://*.amazon.com/*", "*://*.shein.com/*" ]
}
]
}

View File

@@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "TEMU助手",
"description": "TEMU助手 - 自动化提高生产效率",
"version": "3.2.1",
"version": "3.2.3",
"background": {
"service_worker": "/background.js"
},
@@ -21,7 +21,8 @@
"*://*.alicdn.com/",
"*://*.amazon.com/",
"*://*.shein.com/",
"*://*.geiwohuo.com/"
"*://*.geiwohuo.com/",
"*://*.ltwebstatic.com/"
],
"permissions": [
"cookies",
@@ -55,13 +56,26 @@
"id": "6",
"enabled": true,
"path": "rules_6.json"
},{
"id": "7",
"enabled": true,
"path": "rules_7.json"
},{
"id": "8",
"enabled": true,
"path": "rules_8.json"
},{
"id": "9",
"enabled": true,
"path": "rules_9.json"
}]
},
"content_scripts": [
{
"matches": [
"*://*.aliexpress.com/item/*",
"*://*.amazon.com/*"
"*://*.amazon.com/*",
"*://*.shein.com/*"
],
"js": [
"/content.js"
@@ -71,7 +85,7 @@
"web_accessible_resources": [
{
"resources": [ "js/download.js","js/jszip.min.js","js/FileSaver.js" ],
"matches": [ "*://*.aliexpress.com/*", "*://*.amazon.com/*" ]
"matches": [ "*://*.aliexpress.com/*", "*://*.amazon.com/*", "*://*.shein.com/*" ]
}
]
}

View File

@@ -85,15 +85,20 @@ const router = new VueRouter({
name: 'copyProduct',
component: () => import('../view/product/CopyProduct.vue')
},
{
path: 'sellerSelect',
name: 'sellerSelect',
component: () => import('../view/product/SellerSelect.vue')
},
{
path: 'draft',
name: 'draft',
component: () => import('../view/product/Draft.vue')
},
{
path: 'copyProductShein',
name: 'copyProductShein',
component: () => import('../view/product/CopyProductShein.vue')
path: 'findSeller',
name: 'findSeller',
component: () => import('../view/product/FindSeller.vue')
},
{
path: 'copyProductAliExpress',
@@ -172,21 +177,56 @@ const router = new VueRouter({
component: () => import('../view/CoinFlow.vue')
},
{
path: 'costManageTemu',
name: 'costManageTemu',
component: () => import('../view/sale/CostManageTemu.vue')
},
{
path: 'saleData',
name: 'saleData',
component: () => import('../view/sale/ExportSaleData.vue')
},
{
path: 'saleDataShein',
name: 'saleDataShein',
component: () => import('../view/sale/ExportSaleDataShein.vue')
},
{
path: 'saleOut',
name: 'saleOut',
component: () => import('../view/sale/ExportSaleOutData.vue')
},
{
path: 'afterSaleStat',
name: 'afterSaleStat',
component: () => import('../view/sale/AfterSaleStat.vue')
},
{
path: 'afterSaleDeductStat',
name: 'afterSaleDeductStat',
component: () => import('../view/sale/AfterSaleDeductStat.vue')
},
{
path: 'costManageShein',
name: 'costManageShein',
component: () => import('../view/shein/CostManageShein.vue')
},
{
path: 'certCenterShein',
name: 'certCenterShein',
component: () => import('../view/shein/CertCenterShein.vue')
},
{
path: 'saleDataShein',
name: 'saleDataShein',
component: () => import('../view/shein/ExportSaleDataShein.vue')
},
{
path: 'saleStatShein',
name: 'saleStatShein',
component: () => import('../view/shein/ExportSaleStatShein.vue')
},
{
path: 'copyProductShein',
name: 'copyProductShein',
component: () => import('../view/shein/CopyProductShein.vue')
},
// {
// path: 'statistics',

View File

@@ -13,6 +13,8 @@ export default new Vuex.Store({
mallName: '',
mallList: [],
activeDlgShow: false,
showSheinAlert: false,
showTemuAlert: false,
userInfo: {}
},
@@ -46,6 +48,12 @@ export default new Vuex.Store({
},
setActiveDlgShow(state, flag) {
state.activeDlgShow = flag
},
setSheinAlertShow(state, flag) {
state.showSheinAlert = flag
},
setTemuAlertShow(state, flag) {
state.showTemuAlert = flag
}
},

View File

@@ -18,8 +18,9 @@ export function transform(leftData) {
rightData.materialMultiLanguages = leftData.productLocalExtAttr.materialMultiLanguages;
rightData.productI18nReqs = leftData.productI18nList;
rightData.productPropertyReqs = [];
let flag = false
for (let i = 0; i < leftData.productPropertyList.length; i++) {
rightData.productPropertyReqs.push({
let val = {
valueUnit: leftData.productPropertyList[i].valueUnit,
propValue: leftData.productPropertyList[i].propValue,
propName: leftData.productPropertyList[i].propName,
@@ -29,9 +30,31 @@ export function transform(leftData) {
pid: leftData.productPropertyList[i].pid,
templatePid: leftData.productPropertyList[i].templatePid,
valueExtendInfo: leftData.productPropertyList[i].valueExtendInfo
});
}
/*if (leftData.productPropertyList[i].vid == 41500) {
if (config.brandId) {
flag = true
val.pid = 41500
val.propName = config.brandName
}
}*/
rightData.productPropertyReqs.push(val);
}
/*if (config.brandId && !flag) {
rightData.productPropertyReqs.push({
"valueUnit": "",
"propValue": config.brandName,
"propName": "品牌名",
"refPid": 1960,
"vid": 41500,
"controlType": 1,
"pid": config.brandId,
"templatePid": 1151553,
"valueExtendInfo": ""
})
}*/
// SKC
let rightSkc = [];
let leftSkc = leftData.productSkcList;
@@ -159,6 +182,7 @@ export function transformSubmitForHalf(leftData, config, draftId) {
rightData.materialMultiLanguages = leftData.productLocalExtAttr.materialMultiLanguages;
rightData.productI18nReqs = leftData.productI18nList;
rightData.productPropertyReqs = [];
if (!leftData.productPropertyList) return rightData
for (let i = 0; i < leftData.productPropertyList.length; i++) {
rightData.productPropertyReqs.push({
valueUnit: leftData.productPropertyList[i].valueUnit,
@@ -240,6 +264,7 @@ export function transformSubmitForHalf(leftData, config, draftId) {
}
rightSkuItem.productSkuStockQuantityReq = productSkuStockQuantityReq
rightSkuItem.currencyType = leftSkuItem.currencyType;
rightSkuItem.supplierPrice = leftSkuItem.supplierPrice + config.upMoney * 100;
rightSkcItem.productSkuReqs.push(rightSkuItem);
}
@@ -255,6 +280,14 @@ export function transformSubmitForHalf(leftData, config, draftId) {
rightData.carouselImageI18nReqs = leftData.carouselImageI18nVOList;
rightData.materialImgUrl = leftData.materialImgUrl;
rightData.goodsLayerDecorationReqs = leftData.goodsLayerDecorationVOList;
rightData.goodsLayerDecorationReqs.map(item => {
if (item.type == 'image') {
item.contentList.map(item1 => {
delete item1.text
delete item1.textModuleDetails
})
}
})
rightData.sizeTemplateIds = !leftData.sizeTemplateIds ? []: leftData.sizeTemplateIds;
rightData.showSizeTemplateIds = !leftData.showSizeTemplateIds ? []: leftData.showSizeTemplateIds;
rightData.goodsModelReqs = !leftData.goodsModelList ? []: leftData.goodsModelList;

View File

@@ -70,8 +70,11 @@
</template>
<el-menu-item index="/productList">商品列表</el-menu-item>
<el-menu-item index="/copyProduct">商品复制</el-menu-item>
<el-menu-item index="/draft">草稿箱管理</el-menu-item>
<el-menu-item index="/copyProductShein">商品复制(希音)</el-menu-item>
<el-menu-item index="/findSeller">查找买手</el-menu-item>
<el-menu-item v-if="$store.state.userInfo.phone == '18610967550' || $store.state.userInfo.phone == '18571466720'" index="/draft">
草稿箱管理
</el-menu-item>
<el-menu-item index="/sellerSelect">上新生命周期管理</el-menu-item>
<!--<el-menu-item index="/copyProductAliExpress">商品复制(速卖通)</el-menu-item>-->
</el-submenu>
<el-submenu index="/stock">
@@ -126,18 +129,31 @@
<i class="el-icon-s-data"></i>
<span slot="title">销售管理</span>
</template>
<el-menu-item index="/saleData">销售管理(TEMU)</el-menu-item>
<el-menu-item index="/saleDataShein">销售数据(SHEIN)</el-menu-item>
<el-menu-item index="/costManageTemu">成本管理</el-menu-item>
<el-menu-item index="/saleData">销售管理</el-menu-item>
<el-menu-item index="/saleOut">售罄看板</el-menu-item>
<el-menu-item index="/afterSaleStat">售后统计</el-menu-item>
<el-menu-item index="/afterSaleDeductStat">售后赔付统计</el-menu-item>
</el-submenu>
<el-submenu index="/shein">
<template slot="title">
<i class="el-icon-s-goods"></i>
<span slot="title">SHEIN希音</span>
</template>
<el-menu-item index="/costManageShein">成本管理</el-menu-item>
<el-menu-item index="/saleDataShein">销售数据</el-menu-item>
<el-menu-item index="/certCenterShein">证书中心</el-menu-item>
<el-menu-item index="/copyProductShein">商品复制</el-menu-item>
<el-menu-item index="/saleStatShein">商家账单统计</el-menu-item>
</el-submenu>
<el-menu-item index="/info">
<i class="el-icon-info"></i>
<span slot="title">弹窗消息</span>
</el-menu-item>
<el-menu-item index="/priceFollow">
<!--<el-menu-item index="/priceFollow">
<i class="el-icon-money"></i>
<span slot="title">调价管理</span>
</el-menu-item>
</el-menu-item>-->
<el-menu-item index="/learning">
<i class="el-icon-eleme"></i>
<span slot="title">新手园地</span>
@@ -164,6 +180,27 @@
:before-close="handleClose">
<ai-payment/>
</el-dialog>
<el-dialog
title="温馨提示"
:visible="$store.state.showSheinAlert"
:close-on-click-modal="false"
width="1200">
<span style="font-size: large">1检查SHEIN商家后台是否登录如没登录请先登录之后再刷新助手<br></span>
<span style="font-size: large">2如果SHEIN商家后台已经登录仍然弹出当前窗口则需要SHEIN进行二次授权二次授权可在菜单商品管理->商品列表任意选择一个商品价格一栏点击修改在新打开的页面中可看到正在鉴权的字样即可完成二次授权</span>
<span slot="footer" class="dialog-footer">
<el-button @click="closeSheinAlert"> </el-button>
</span>
</el-dialog>
<el-dialog
title="温馨提示"
:visible="$store.state.showTemuAlert"
:close-on-click-modal="false"
width="1200">
<span style="font-size: large">请先打开卖家中心结算数据->售后管理页面进行二次授权<a target="_blank" style="text-decoration: underline" href="https://seller.kuajingmaihuo.com/main/aftersales/information">去打开</a><br></span>
<span slot="footer" class="dialog-footer">
<el-button @click="closeTemuAlert"> </el-button>
</span>
</el-dialog>
<div id="kefu" @click="gotoKefu">
<label slot="reference" class="topBtn" title="联系客服"></label>
@@ -174,7 +211,7 @@
<script>
import {mapMutations, mapState} from 'vuex'
import AiPayment from "@/components/AiPayment.vue";
import {sendAliexpressAPIMessage} from "@/api/chromeApi";
import {sendAliexpressAPIMessage, sendTemuSellerAgentMessage} from "@/api/chromeApi";
export default {
components: {AiPayment},
@@ -222,6 +259,7 @@ export default {
const prodVersion = require('../manifest.production.json').version
this.version = process.env.NODE_ENV === 'production' ? prodVersion : devVersion
this.activePath = this.$route.fullPath
//this.getAfterSalesList()
},
methods: {
@@ -293,6 +331,12 @@ export default {
gotoKefu() {
window.open('https://work.weixin.qq.com/kfid/kfcaa4208f661131eba', '_blank')
},
closeSheinAlert() {
this.$store.commit('setSheinAlertShow', false)
},
closeTemuAlert() {
this.$store.commit('setTemuAlertShow', false)
},
getAliexpressGoodsList() {
let url = "https://seller-acs.aliexpress.com/h5/mtop.global.merchant.self.product.manager.render.list/1.0/?jsv=2.7.2&appKey=30267743&t=1713978403051&sign=ba2bda69b4a2695c7279d4bc05f51741&v=1.0&timeout=15000&H5Request=true&url=mtop.global.merchant.self.product.manager.render.list&__channel-id__=701301&api=mtop.global.merchant.self.product.manager.render.list&type=originaljson&dataType=json&valueType=original&x-i18n-regionID=AE"
url = url + "&data=" + encodeURIComponent(
@@ -322,7 +366,7 @@ export default {
}).then(res => {
//console.log(res)
})
},
}
},
mounted() {
// this.getAliexpressGoodsList()

View File

@@ -106,6 +106,7 @@
productName: item.productName,
skcId: item.skcId,
popUpType: 1,
versionId: res.result.popUpAutoPass12.versionId,
newSupplyPrice: item.newSupplyPrice / 100,
}
item.skuInfoItemList.map(item1 => {
@@ -118,6 +119,7 @@
productName: item.productName,
skcId: item.skcId,
popUpType: 2,
versionId: res.result.popUpAutoPass24.versionId,
newSupplyPrice: item.newSupplyPrice / 100,
}
item.skuInfoItemList.map(item1 => {
@@ -130,6 +132,7 @@
productName: item.productName,
skcId: item.skcId,
popUpType: 3,
versionId: res.result.popUpOthers.versionId,
newSupplyPrice: item.newSupplyPrice / 100,
}
item.skuInfoItemList.map(item1 => {
@@ -160,11 +163,14 @@
Message.success("该店铺无调价通知,无需处理")
return
}
let maxOrderId = 0
let ids = this.priceList.map(item => {
let popUpType1VersionId = ''
let popUpType2VersionId = ''
let popUpType3VersionId = ''
let ids = []
this.priceList.map(item => {
if (item.popUpType == 1) {
if (maxOrderId < item.id) maxOrderId = item.id
return item.id
popUpType1VersionId = item.versionId
ids.push(item.id)
}
})
let res = null
@@ -175,16 +181,16 @@
mallId: this.currentMallId,
data: {
"rejectOrderIdList": ids,
"maxOrderId": maxOrderId,
"maxOrderId": popUpType1VersionId,
"popUpType": 1,
"createTime": new Date().getTime() * 1000
"createTime": new Date().getTime()
}})
}
maxOrderId = 0
ids = this.priceList.map(item => {
ids = []
this.priceList.map(item => {
if (item.popUpType == 2) {
if (maxOrderId < item.id) maxOrderId = item.id
return item.id
popUpType2VersionId = item.versionId
ids.push(item.id)
}
})
if (ids.length > 0) {
@@ -194,16 +200,16 @@
mallId: this.currentMallId,
data: {
"rejectOrderIdList": ids,
"maxOrderId": maxOrderId,
"maxOrderId": popUpType2VersionId,
"popUpType": 2,
"createTime": new Date().getTime() * 1000
"createTime": new Date().getTime()
}})
}
maxOrderId = 0
ids = this.priceList.map(item => {
ids = []
this.priceList.map(item => {
if (item.popUpType == 3) {
if (maxOrderId < item.id) maxOrderId = item.id
return item.id
popUpType3VersionId = item.versionId
ids.push(item.id)
}
})
if (ids.length > 0) {
@@ -213,9 +219,9 @@
mallId: this.currentMallId,
data: {
"rejectOrderIdList": ids,
"maxOrderId": maxOrderId,
"maxOrderId": popUpType3VersionId,
"popUpType": 3,
"createTime": new Date().getTime() * 1000
"createTime": new Date().getTime()
}})
}
if (res.success && res.errorCode == 1000000) {

View File

@@ -18,6 +18,15 @@
:value="item.mallId">
</el-option>
</el-select>
<label style="width:90px">站点</label>
<el-select v-model="siteId" placeholder="请选择" size="small">
<el-option
v-for="item in siteList"
:key="item.siteId"
:label="item.siteName"
:value="item.siteId">
</el-option>
</el-select>
</div>
</div>
</template>
@@ -29,6 +38,10 @@
<el-button v-if="$store.state.mallName" type="button" :class="'el-button el-button--primary'" @click="batchSubmitConfig()">批量提交</el-button>
</template>
<template #right>
<label style="width:140px; text-align: right;">起始页</label>
<el-input size="small" placeholder="请输入起始页" style="width: 100px; display: inline" type="number" v-model="search.startPage"></el-input>
<label style="width:140px; text-align: right;">结束页</label>
<el-input size="small" placeholder="请输入起始页" style="width: 100px; display: inline" type="number" v-model="search.endPage"></el-input>
<label style="width:120px">商品名称</label>
<el-input clearable size="small" style="display: inline" placeholder="请输入商品名称" v-model="search.productName"></el-input>
<el-button type="primary" @click="toLoad">查询</el-button>
@@ -37,7 +50,6 @@
<ai-table
:tableData="tableData"
:col-configs="colConfigs"
:total="total"
style="margin-top: 8px;"
@selection-change="handleSelectionChange"
:isShowPagination="false">
@@ -52,7 +64,12 @@
:close-on-click-modal="false"
width="790px"
customFooter
@close="handleClose">
@close="dlgShow = false">
<el-alert
title="默认“承诺发货时效”为“2个工作日内发货”"
type="success"
:closable="false">
</el-alert>
<el-form class="ai-form" :model="configForm" label-width="160px" ref="configForm">
<el-form-item label="运费模板" style="width: 100%;" prop="freightTemplateId" :rules="[{ required: true, message: '请选择运费模板', trigger: 'blur' }]">
<el-select style="width: 380px" v-model="configForm.freightTemplateId" placeholder="请选择运费模板">
@@ -64,12 +81,6 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item
prop="stockNumber"
label="库存数量:"
:rules="[{ required: true, message: '请输入库存数量', trigger: 'blur' }]">
<el-input size="small" placeholder="请输入库存数量" type="number" v-model="configForm.stockNumber"></el-input>
</el-form-item>
<el-form-item label="发货仓" style="width: 100%;" prop="wareHouseList" :rules="[{ required: true, message: '请选择发货仓', trigger: 'blur' }]">
<el-select style="width: 380px" multiple v-model="configForm.wareHouseList" placeholder="请选择发货仓">
<el-option
@@ -80,6 +91,30 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item
prop="stockNumber"
label="库存数量:"
:rules="[{ required: true, message: '请输入库存数量', trigger: 'blur' }]">
<el-input size="small" placeholder="请输入库存数量" type="number" v-model="configForm.stockNumber"></el-input>
</el-form-item>
<el-form-item
prop="upMoney"
label="上浮金额:"
:rules="[{ required: true, message: '请输入上浮金额', trigger: 'blur' }]">
<el-input size="small" placeholder="请输入上浮金额" type="number" v-model="configForm.upMoney"></el-input>
</el-form-item>
<el-form-item
prop="brand"
label="品牌:">
<el-select style="width: 380px" clearable v-model="configForm.brandId" placeholder="请选择品牌">
<el-option
v-for="item in brandList"
:key="item.vid"
:label="item.brandNameEn"
:value="item.vid">
</el-option>
</el-select>
</el-form-item>
</el-form>
<div class="dialog-footer" slot="footer">
<el-button @click="dlgShow = false"> </el-button>
@@ -92,15 +127,19 @@
<script>
import {sendChromeAPIMessage} from '@/api/chromeApi'
import {timestampToTime} from '@/utils/date'
import {transform, transformSubmitForHalf} from '@/utils/product'
import { Message } from 'element-ui'
import {transformSubmitForHalf} from '@/utils/product'
import { Message, MessageBox } from 'element-ui'
export default {
name: 'CopyProduct',
name: 'Draft',
data () {
return {
mallId: '',
siteId: '',
siteList: [],
search: {
startPage: 1,
endPage: 10,
productName: ''
},
colConfigs: [
@@ -115,7 +154,6 @@ import { Message } from 'element-ui'
page: 1,
pageSize: 100,
tableData: [],
total: 0,
dlgShow: false,
@@ -124,7 +162,10 @@ import { Message } from 'element-ui'
freightTemplateId: '',
stockNumber: 5,
siteList: [],
wareHouseList: []
upMoney: 0.0,
wareHouseList: [],
brandId: '',
brandName: ''
},
wareHouseList: [],
@@ -133,11 +174,16 @@ import { Message } from 'element-ui'
ids: [],
sites: [],
loadingText: '拼命加载中……'
loadingText: '拼命加载中……',
successNumber: 0,
errorNumber: 0,
brandList: [],
selectBrand: {}
}
},
created () {
// this.getDraftList()
this.getSiteList()
},
methods: {
@@ -146,11 +192,33 @@ import { Message } from 'element-ui'
Message.error("请选择店铺")
return
}
if (!this.siteId) {
Message.error("请选择站点")
return
}
if (!this.search.startPage || (this.search.startPage < 1)) {
Message.error("起始页不能为空且不能小于1")
return
}
if (!this.search.endPage || (this.search.startPage < 1)) {
Message.error("结束页不能为空")
return
}
if (this.search.startPage > this.search.endPage) {
Message.error("起始页不能大于结束页")
return
}
this.isLoading = true
this.loadingText = '拼命加载中……'
this.tableData = []
this.page = this.search.startPage
this.getDraftList()
},
async getDraftList () {
let sites = []
if (this.siteId) {
sites.push(this.siteId)
}
let res = await sendChromeAPIMessage({
url: 'bg-visage-mms/product/draft/pageQuery',
needMallId: true,
@@ -158,7 +226,8 @@ import { Message } from 'element-ui'
data: {
page: this.page,
pageSize: this.pageSize,
...this.search
...this.search,
bindSiteIds: sites
}})
if (res.success && res.errorCode == 1000000) {
@@ -191,7 +260,7 @@ import { Message } from 'element-ui'
if (this.page == 1 && res.result.pageItems.length == 0) {
this.isLoading = false
}
else if (res.result.pageItems.length == this.pageSize) {
else if (res.result.pageItems.length == this.pageSize && this.page < this.search.endPage) {
this.page ++
this.getDraftList()
} else {
@@ -205,32 +274,51 @@ import { Message } from 'element-ui'
return;
}
let id = this.ids[0]
this.sites = []
for (let i = 0; i < this.tableData.length; i++) {
if (this.tableData[i].spuId == id) {
this.sites = this.tableData[i].bindSiteIds.split(',')
}
}
this.sites.push(this.siteId)
this.configForm.siteList = this.sites
this.freightTmplList = []
this.wareHouseList = []
this.configForm.freightTemplateId = ''
this.configForm.wareHouseList = []
await this.getFreightTmplList(this.sites)
await this.getWareahouseList(this.sites)
await this.getBrandList()
this.dlgShow = true
},
async toBtachSubmit() {
this.$refs.configForm.validate((valid) => {
if (valid) {
if (this.configForm.brandId) {
this.brandList.map(item => {
if (item.vid == this.configForm.brandId) {
this.selectBrand = item
}
})
}
this.isLoading = true
this.dlgShow = false
for(let i = 0; i < this.ids.length; i++) {
this.loadingText = '正在提交第' + (i+1) + '/' + this.ids.length + '个商品'
this.submitSpu(this.ids[i])
}
this.isLoading = false
this.successNumber = 0
this.errorNumber = 0
this.submitSpu(0)
}
})
},
async submitSpu(spuId) {
async submitSpu(idx) {
if (idx == this.ids.length) {
this.isLoading = false
MessageBox.alert('总共提交' + this.ids.length + '个商品,成功' + this.successNumber + '个,失败' + this.errorNumber + '个', '提示', {
confirmButtonText: '确定',
callback: action => {
this.toLoad()
}
})
return
} else {
this.loadingText = '正在提交第' + (idx+1) + '/' + this.ids.length + '个商品,成功' + this.successNumber + '个,失败' + this.errorNumber + '个'
}
let spuId = this.ids[idx]
let res = await sendChromeAPIMessage({
url: 'bg-visage-mms/product/draft/query',
needMallId: true,
@@ -239,15 +327,53 @@ import { Message } from 'element-ui'
productDraftId: spuId
}})
if (res.success && res.errorCode == 1000000) {
let catId = this.getCategoryId(res.result)
let brandProperty = await this.queryProperty(catId)
let content = transformSubmitForHalf(res.result, this.configForm, spuId)
if (this.configForm.brandId && brandProperty) {
let flag = false
content.productPropertyReqs.map(item => {
if (item.propName == '品牌名') {
flag = true
item.pid = brandProperty.pid
item.refPid = brandProperty.refPid
item.templatePid = brandProperty.templatePid
item.propValue = this.selectBrand.brandNameEn
item.vid = this.selectBrand.vid
}
})
if (!flag) {
content.productPropertyReqs.push({
"templatePid": brandProperty.templatePid,
"pid": brandProperty.pid,
"refPid": brandProperty.refPid,
"propName": "品牌名",
"vid": this.selectBrand.vid,
"propValue": this.selectBrand.brandNameEn,
"valueUnit": "",
"valueExtendInfo": "",
"numberInputValue": ""
})
}
}
let res1 = await sendChromeAPIMessage({
url: 'bg-visage-mms/product/add',
needMallId: true,
mallId: this.mallId,
data: content})
console.log(res1)
if (res1.success && res1.errorCode == 1000000) {
this.successNumber += 1
} else {
this.errorNumber += 1
}
setTimeout(() => {
idx ++
this.submitSpu(idx)
}, 1000)
}
},
async getFreightTmplList(siteId) {
@@ -294,39 +420,6 @@ import { Message } from 'element-ui'
this.ids.push(e.spuId);
});
},
saveDraftTemplate(spu, index) {
sendChromeAPIMessage({
url: 'bg-visage-mms/product/draft/query',
needMallId: true,
mallId: this.productPage.mallId,
data: {
productDraftId: spu
}}).then((res) => {
if (res.errorCode == 1000000) {
let content = transform(res.result)
let mallInfo = this.$store.state.mallList.filter(item => {
return item.mallId == this.productPage.mallId
})
this.$http.post('/api/product/add', {
mallId: mallInfo[0].mallId,
mallName: mallInfo[0].mallName,
productSpu: spu,
productName: res.result.productName,
type: 0,
content: content
}).then(res1 => {
if (res1.code == 0) {
Message.success("商品【" + res.result.productName + "】成功添加为商品模板")
if (index == this.productIds.length - 1) {
this.getList()
}
}
})
} else {
this.saveDraftTemplate(spu, index)
}
})
},
async getSiteList() {
let res = await sendChromeAPIMessage({
url: 'bg-visage-mms/config/common/site/query',
@@ -339,6 +432,52 @@ import { Message } from 'element-ui'
})
}
},
async getBrandList() {
let res = await sendChromeAPIMessage({
url: 'bg-anniston-mms/category/brand/property/value/pageQuery',
needMallId: true,
mallId: this.mallId,
data: {
"page": 1,
"pageSize": 50
}})
if (res.success && res.errorCode == 1000000) {
this.brandList = res.result.pageItems
}
},
async queryProperty(catId) {
let res = await sendChromeAPIMessage({
url: 'bg-anniston-mms/category/template/query',
needMallId: true,
mallId: this.mallId,
data: {
"catId": catId,
"productCreateTime": null,
"langList": [
"en"
]
}})
if (res.success && res.errorCode == 1000000) {
let properties = res.result.properties.filter(item => {
return item.name == '品牌名'
})
if (properties && properties.length > 0) {
return properties[0]
} else {
return null
}
}
},
getCategoryId(obj) {
let i = 1
while(true) {
if (!obj.categories['cat'+i].catId) {
break
}
i++
}
return obj.categories['cat'+(i-1)].catId
}
}
}
</script>

View File

@@ -0,0 +1,118 @@
<template>
<ai-list class="list" v-loading="isLoading" element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading">
<ai-title
slot="title"
title="查找买手"
isShowBottomBorder>
</ai-title>
<template slot="content">
<div class="content">
<ai-card title="查找买手" style="padding-bottom: 40px;">
<div style="text-align: center; display: flex; flex-wrap: nowrap;justify-content: center;">
<label style="width:90px; height: 25px; font-size: 25px">店铺</label>
<el-select style="width:180px;" v-model="form.mallId" placeholder="请选择" clearable size="small">
<el-option
v-for="item in $store.state.mallList"
:key="item.mallId"
:label="item.mallName"
:value="item.mallId">
</el-option>
</el-select>
<label style="width:120px; height: 25px; font-size: 25px">SKC ID</label>
<el-input style="width:250px; " clearable size="small" placeholder="请输入SKC ID" v-model="form.skc"></el-input>
<el-button style="margin-left: 5px" type="primary" @click="beforeFind">查找</el-button>
</div>
</ai-card>
</div>
</template>
</ai-list>
</template>
<script>
import { Message } from 'element-ui'
import {sendChromeAPIMessage} from '@/api/chromeApi'
export default {
name: 'FindSeller',
data () {
return {
form: {
mallId: '',
skc: ''
},
isLoading: false,
}
},
created () {
},
methods: {
beforeFind() {
this.$userCheck(this.form.mallId).then(() => {
this.toFind()
}).catch((err) => {
this.form.mallId = ''
})
},
async toFind() {
if (!this.form.mallId) {
Message.error("请选择店铺")
return
}
if (!this.form.skc || (this.form.skc < 1)) {
Message.error("请输入SKC")
return
}
this.isLoading = true
let res = await sendChromeAPIMessage({
url: 'marvel-mms/cn/api/kiana/venom/sales/management/listWarehouse',
needMallId: true,
mallId: this.form.mallId,
data: {
pageNo: 1,
pageSize: 10,
isLack: 0,
priceAdjustRecentDays: 7,
productSkcIdList: [this.form.skc]
}})
if (res.errorCode == 1000000) {
if (res.result.subOrderList.length == 0) {
Message.error("没有查询结果请检查店铺和SKC是否正确")
} else {
this.$confirm(res.result.subOrderList[0].buyerName, '查询结果', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
center: true
}).then(() => {
})
}
}
this.isLoading = false
}
}
}
</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;
}
}
}
</style>

View File

@@ -3,12 +3,13 @@
<ai-title
slot="title"
title="商品列表"
tips="每页为100条商品数据"
isShowBottomBorder>
<template #rightBtn>
<div class="title-right">
<div>
<label style="width:90px">店铺</label>
<el-select v-model="form.mallId" @change="beforeGetList" placeholder="请选择" size="small">
<el-select v-model="form.mallId" placeholder="请选择" size="small">
<el-option
v-for="item in $store.state.mallList"
:key="item.mallId"
@@ -21,47 +22,108 @@
</template>
</ai-title>
<template slot="content">
<ai-card title="数据明细" style="padding-bottom: 40px;">
<template #right>
<label style="width:140px">是否在售</label>
<el-select clearable v-model="reqData.skcSiteStatus" placeholder="请选择">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
<label style="width:140px">商品名称</label>
<el-input clearable size="small" style="display: inline" placeholder="请输入商品名称" v-model="reqData.productName"></el-input>
<el-button type="primary" @click="toLoad">加载</el-button>&nbsp;
<json-excel
:data="tableData"
:fields="jsonFields"
name="商品列表.xls"
worksheet="商品列表">
<el-button type="primary" :disabled="tableData.length == 0">下载数据</el-button>
</json-excel>
</template>
<ai-table
:isShowPagination="false"
:tableData="tableData"
:col-configs="colConfigs"
:total="tableData.length"
height="500"
style="margin-top: 8px;"
@getList="() => {}">
<div class="content">
<ai-search-bar>
<template #left>
<div class="search-item">
<label>起始页</label>
<el-input size="small" placeholder="请输入起始页" type="number" v-model="startPage"></el-input>
</div>
<div class="search-item">
<label>结束页</label>
<el-input size="small" placeholder="请输入起始页" type="number" v-model="endPage"></el-input>
</div>
<div class="search-item">
<label>是否在售</label>
<el-select clearable v-model="reqData.skcSiteStatus" placeholder="请选择">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<div class="search-item">
<label>站点</label>
<el-select v-model="reqData.bindSiteId" placeholder="请选择" size="small">
<el-option
v-for="item in siteList"
:key="item.siteId"
:label="item.siteName"
:value="item.siteId">
</el-option>
</el-select>
</div>
<div class="search-item">
<label>合规下架风险</label>
<el-select v-model="reqData.certPunishTypes" multiple placeholder="请选择" size="small">
<el-option
v-for="item in certPunishTypes"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</div>
<div class="search-item">
<label>资质上传状态</label>
<el-select v-model="reqData.productCertAuditStatuses" multiple placeholder="请选择" size="small">
<el-option
v-for="item in productCertAuditStatusList"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</div>
<div class="search-item">
<label>商品名称</label>
<el-input clearable size="small" placeholder="请输入商品名称" v-model="reqData.productName"></el-input>
</div>
<div class="search-item">
<label>库存起始值</label>
<el-input size="small" placeholder="请输入库存起始值" type="number" v-model="reqData.jitStockQuantitySection.leftValue"></el-input>
</div>
<div class="search-item">
<label>库存结束值</label>
<el-input size="small" placeholder="请输入库存结束值" type="number" v-model="reqData.jitStockQuantitySection.rightValue"></el-input>
</div>
</template>
<template #right>
<el-button type="primary" @click="toLoad">加载</el-button>
</template>
</ai-search-bar>
<ai-card title="数据明细" style="padding-bottom: 40px;">
<template #right>
<json-excel
:data="tableData"
:fields="jsonFields"
name="商品列表.xls"
worksheet="商品列表">
<el-button type="primary" :disabled="tableData.length == 0">下载数据</el-button>
</json-excel>
</template>
<ai-table
:isShowPagination="false"
:tableData="tableData"
:col-configs="colConfigs"
:total="tableData.length"
height="500"
style="margin-top: 8px;"
@getList="() => {}">
<el-table-column slot="productName" width="200px" :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>
</ai-card>
<el-table-column slot="productName" width="200px" :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>
</ai-card>
</div>
</template>
</ai-list>
</template>
@@ -80,11 +142,20 @@ import JsonExcel from 'vue-json-excel'
form: {
mallId: ''
},
startPage: 1,
endPage: 10,
reqData: {
productName: '',
page: 1,
pageSize: 100,
skcSiteStatus: ''
skcSiteStatus: '',
bindSiteId: '',
certPunishTypes: [],
productCertAuditStatuses: [],
jitStockQuantitySection: {
leftValue: '',
rightValue: ''
}
},
options: [
{label: '是', value: 1},
@@ -100,6 +171,9 @@ import JsonExcel from 'vue-json-excel'
{ prop: 'extCode', label: 'SKC货号', width: '100px', align: 'left' },
{ prop: 'skuExtCode', label: 'SKU货号', width: '160px', align: 'left' },
{ prop: 'specName', label: 'SKU属性', width: '100px', align: 'left' },
{ prop: 'skuStockQuantity', label: '库存', width: '100px', align: 'left' },
{ prop: 'productCertAuditStatus', label: '资质上传状态', width: '120px', align: 'left' },
{ prop: 'certPunishType', label: '合规下架风险', width: '120px', align: 'left' },
{ prop: 'supplierPrice', label: '申报价格(CNY)', width: '180px', align: 'left' },
{ prop: 'todaySalesVolume', label: '今日销量', width: '100px', align: 'left' },
{ prop: 'createdAt', label: '上架时间', width: '160px', align: 'left' }
@@ -117,17 +191,47 @@ import JsonExcel from 'vue-json-excel'
"SKC货号": "extCode",
"SKU货号": "skuExtCode",
"SKU属性": "specName",
"库存": "skuStockQuantity",
"资质上传状态": "productCertAuditStatus",
"合规下架风险": "certPunishType",
"申报价格(CNY)": "supplierPrice",
"今日销量": "todaySalesVolume",
"上架时间": "createdAt"
},
currentIndex: 0
certPunishTypes: [
{id: 1, name: '已经处罚'},
{id: 2, name: '即将下架'},
{id: 3, name: '下架风险预警'},
],
productCertAuditStatusList: [
{id: 1, name: '待上传'},
{id: 2, name: '上传中'},
{id: 3, name: '上传失败'},
{id: 4, name: '上传成功'},
],
currentIndex: 0,
siteList: []
}
},
components: {
JsonExcel
},
created () {
this.getSiteList()
},
methods: {
async getSiteList() {
let res = await sendChromeAPIMessage({
url: 'bg-visage-mms/config/common/site/query',
needMallId: true,
mallId: this.$store.state.mallList[0].mallId,
data: {}})
if (res.success && res.errorCode == 1000000) {
this.siteList = res.result.siteBaseList.filter(item => {
return item.matchSemiManaged
})
}
},
beforeGetList() {
this.$userCheck(this.form.mallId).then(() => {
this.toLoad()
@@ -140,7 +244,19 @@ import JsonExcel from 'vue-json-excel'
Message.error("请选择店铺")
return
}
this.reqData.page = 1
if (!this.startPage || (this.startPage < 1)) {
Message.error("起始页不能为空且不能小于1")
return
}
if (!this.endPage || (this.startPage < 1)) {
Message.error("结束页不能为空")
return
}
if (this.startPage > this.endPage) {
Message.error("起始页不能大于结束页")
return
}
this.reqData.page = this.startPage
this.tableData = []
this.currentIndex = 0
this.isLoading = true
@@ -164,6 +280,24 @@ import JsonExcel from 'vue-json-excel'
data.skcSiteStatus = item.skcSiteStatus == '1'? '在售': ''
data.createdAt = timestampToTime(item.createdAt)
data.extCode = item.extCode
data.productCertAuditStatus = '-'
if (item.productCertAuditStatus == 1) {
data.productCertAuditStatus = '待上传'
} else if (item.productCertAuditStatus == 2) {
data.productCertAuditStatus = '上传中'
} else if (item.productCertAuditStatus == 3) {
data.productCertAuditStatus = '上传失败'
} else if (item.productCertAuditStatus == 4) {
data.productCertAuditStatus = '上传成功'
}
data.certPunishType = '-'
if (item.productCertPunish?.certPunishType == 1) {
data.certPunishType = '已经处罚'
} else if (item.productCertPunish?.certPunishType == 2) {
data.certPunishType = '即将下架'
} else if (item.productCertPunish?.certPunishType == 3) {
data.certPunishType = '下架风险预警'
}
data.category = ''
for (let i = 1; i < 11; i++) {
@@ -179,21 +313,23 @@ import JsonExcel from 'vue-json-excel'
let temp = item.productSkuSummaries[k].productSkuSpecList.map(item2 => {
return item2.parentSpecName + "" + item2.specName
})
let skuStockQuantity = '-'
if (item.productSkuSummaries[k].productSkuSemiManagedStock) {
skuStockQuantity = item.productSkuSummaries[k].productSkuSemiManagedStock.skuStockQuantity
}
data = {...data,
productSkuId: item.productSkuSummaries[k].productSkuId,
skuExtCode: item.productSkuSummaries[k].extCode,
supplierPrice: item.productSkuSummaries[k].supplierPrice / 100,
specName: temp.join(""),
skuStockQuantity,
todaySalesVolume: item.productSkuSummaries[k].todaySalesVolume}
this.tableData.push(data)
}
}
if (this.reqData.page == 1 && res.result.pageItems.length == 0) {
this.isLoading = false
}
else if (res.result.pageItems.length == 100) {
if (res.result.pageItems.length == this.reqData.pageSize && this.reqData.page < this.endPage) {
this.reqData.page ++
this.load()
} else {

View File

@@ -0,0 +1,272 @@
<template>
<ai-list class="list" v-loading="isLoading" element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading">
<ai-title
slot="title"
title="上新生命周期管理"
tips="请先在当前浏览器登录“拼多多跨境卖家中心”期间保持登录状态。每页为100条商品数据"
isShowBottomBorder>
<template #rightBtn>
<div class="title-right">
<div>
<label style="width:90px">店铺</label>
<el-select v-model="form.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>
</template>
</ai-title>
<template slot="content">
<div class="content">
<ai-search-bar>
<template #left>
<div class="search-item">
<label>起始页</label>
<el-input size="small" placeholder="请输入起始页" type="number" v-model="startPage"></el-input>
</div>
<div class="search-item">
<label>结束页</label>
<el-input size="small" placeholder="请输入起始页" type="number" v-model="endPage"></el-input>
</div>
<div class="search-item">
<label>状态</label>
<el-select v-model="status" placeholder="请选择" size="small">
<el-option
v-for="item in statusList"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</div>
<div class="search-item">
<label>是否合规待办</label>
<el-select v-model="isViolationInfo" placeholder="请选择" size="small">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
</template>
<template #right>
<el-button type="primary" @click="toLoad">加载</el-button>
</template>
</ai-search-bar>
<ai-card title="数据明细" style="padding-bottom: 40px;">
<template #right>
<json-excel
:data="tableData"
:fields="jsonFields"
name="商品列表.xls"
worksheet="商品列表">
<el-button type="primary" :disabled="tableData.length == 0">下载数据</el-button>
</json-excel>
</template>
<ai-table
:isShowPagination="false"
:tableData="tableData"
:col-configs="colConfigs"
:total="tableData.length"
height="800"
style="margin-top: 8px;"
@getList="() => {}">
<el-table-column slot="productName" width="200px" :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>
</ai-card>
</div>
</template>
</ai-list>
</template>
<script>
import { Message } from 'element-ui'
import {sendChromeAPIMessage} from '@/api/chromeApi'
import {timestampToTime} from '@/utils/date'
import JsonExcel from 'vue-json-excel'
export default {
name: 'SellerSelect',
data () {
return {
form: {
mallId: ''
},
startPage: 1,
endPage: 10,
reqData: {
pageNum: 1,
pageSize: 100,
secondarySelectStatusList: []
},
colConfigs: [
{ slot: 'productName', label: '商品名称', width: '180px', align: 'left', fixed: 'left' },
{ prop: 'category', label: '分类', width: '220px', align: 'left', fixed: 'left' },
{ prop: 'siteName', label: '站点', width: '140px', align: 'left', fixed: 'left' },
{ prop: 'productId', label: 'SPU ID', width: '120px', align: 'left' },
{ prop: 'productSkcId', label: 'SKC ID', width: '120px', align: 'left' },
{ prop: 'extCode', label: 'SKC货号', width: '120px', align: 'left' },
{ prop: 'punishInfo', label: '违规信息', width: '120px', align: 'left' },
{ prop: 'violationInfo', label: '合规待办项', width: '200px', align: 'left' },
{ prop: 'supplierPrice', label: '申报价格(CNY)', width: '180px', align: 'left' },
{ prop: 'createdAt', label: '创建时间', width: '160px', align: 'left' }
],
isLoading: false,
tableData: [],
jsonFields: {
"商品名称": "productName",
"图片": "mainImageUrl",
"分类": "category",
"站点": "siteName",
"SPU ID": "productId",
"SKC ID": "productSkcId",
"SKC货号": "extCode",
"违规信息": "punishInfo",
"合规待办项": "violationInfo",
"申报价格(CNY)": "supplierPrice",
"创建时间": "createdAt"
},
statusList: [
{id: 1, name: '未发布到站点'},
{id: 2, name: '已下架/终止'}
],
options: [
{label: '是', value: 1},
{label: '否', value: 0}
],
status: 1,
isViolationInfo: ''
}
},
components: {
JsonExcel
},
methods: {
beforeGetList() {
this.$userCheck(this.form.mallId).then(() => {
this.toLoad()
}).catch((err) => {
this.form.mallId = ''
})
},
toLoad() {
if (!this.form.mallId) {
Message.error("请选择店铺")
return
}
if (!this.startPage || (this.startPage < 1)) {
Message.error("起始页不能为空且不能小于1")
return
}
if (!this.endPage || (this.startPage < 1)) {
Message.error("结束页不能为空")
return
}
if (this.startPage > this.endPage) {
Message.error("起始页不能大于结束页")
return
}
if (this.status == 1) {
this.reqData.secondarySelectStatusList = [10, 11]
} else if (this.status == 2) {
this.reqData.secondarySelectStatusList = [13]
}
if (this.isViolationInfo == 0) {
this.reqData.supplierTodoTypeList = []
} else if (this.isViolationInfo == 1) {
this.reqData.supplierTodoTypeList = [7]
}
this.reqData.pageNum = this.startPage
this.tableData = []
this.isLoading = true
this.load()
},
load() {
sendChromeAPIMessage({
url: 'marvel-supplier/api/xmen/select/search',
needMallId: true,
mallId: this.form.mallId,
anti: true,
data: this.reqData}).then((res) => {
if (res.errorCode == 1000000) {
for(let i = 0;i < res.result.dataList.length; i++) {
let item = res.result.dataList[i];
let data = {};
data.productName = item.productName
data.mainImageUrl = item.carouselImageUrlList[0]
data.productId = item.productId
data.productSkcId = item.productSkcId
data.siteName = item.siteName
data.createdAt = timestampToTime(item.productCreatedAt)
data.category = item.fullCategoryName.join('->')
for (let k = 0; k < item.skcList.length; k++) {
data = {...data, productSkcId: item.skcList[k].skcId, punishInfo: item.skcList[k].punishInfo, extCode: item.skcList[k].extCode, supplierPrice: item.skcList[k].supplierPrice}
if (item.skcList[k].addSiteViolationInfoVO && item.skcList[k].addSiteViolationInfoVO.violationCodeList?.length > 0) {
let temp = ''
for (let j = 0; j < item.skcList[k].addSiteViolationInfoVO.violationCodeList.length; j++) {
if (item.skcList[k].addSiteViolationInfoVO.violationCodeList[j] == 1) {
temp += ',商品资质'
}
if (item.skcList[k].addSiteViolationInfoVO.violationCodeList[j] == 2) {
temp += ',商品标签实拍图'
}
}
temp = temp.substring(1)
data.violationInfo = temp
}
this.tableData.push(data)
}
}
if (res.result.dataList.length == this.reqData.pageSize && this.reqData.pageNum < this.endPage) {
this.reqData.pageNum ++
this.load()
} else {
this.isLoading = false
}
}
})
}
}
}
</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;
}
}
}
</style>

View File

@@ -0,0 +1,270 @@
<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="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>
</template>
</ai-title>
<template slot="content">
<div class="content">
<ai-search-bar>
<template #left>
<div class="search-item">
<label>账务时间</label>
<el-date-picker
v-model="searchDate"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</div>
</template>
<template #right>
<el-button type="primary" @click="beforeGetList">加载</el-button>
</template>
</ai-search-bar>
<ai-card title="数据明细" style="padding-bottom: 40px;">
<template #right>
<json-excel
:data="tableData"
:fields="jsonFields"
name="售后赔付统计列表.xls"
worksheet="售后赔付统计列表">
<el-button type="primary" :disabled="tableData.length == 0">下载数据</el-button>
</json-excel>
</template>
<ai-table
:isShowPagination="false"
:tableData="tableData"
:col-configs="colConfigs"
:total="tableData.length"
height="700"
style="margin-top: 8px;"
@getList="() => {}">
<el-table-column slot="productName" width="400px" :show-overflow-tooltip='true' label="商品名称" fixed="left">
<template slot-scope="scope">
<div>
<el-image :src="scope.row.productUrl" style="width: 40px; height: 40px" class="image" :preview-src-list="[scope.row.productUrl]" />
{{ scope.row.productName }}
</div>
</template>
</el-table-column>
</ai-table>
</ai-card>
</div>
</template>
</ai-list>
</template>
<script>
import { Message } from 'element-ui'
import {sendChromeAPIMessage } from '@/api/chromeApi'
import {timestampToTime} from '@/utils/date'
import JsonExcel from 'vue-json-excel'
export default {
name: 'AfterSaleStat',
data () {
return {
mallId: '',
searchDate: [],
reqData: {
pageNum: 1,
pageSize: 100
},
colConfigs: [
{ prop: 'productName', label: '商品名称', width: '400px', align: 'left', fixed: 'left' },
{ prop: 'productSkuId', label: 'SKU ID', align: 'left' },
{ prop: 'skuCode', label: 'SKU货号', align: 'left' },
{ prop: 'deductionAmount', label: '扣款总金额', align: 'left', sortable: true, 'sort-method': (a, b) => a.deductionAmount - b.deductionAmount },
{ prop: 'times', label: '扣款次数', align: 'left', sortable: true, 'sort-method': (a, b) => a.times - b.times },
/*{ prop: 'supplierPrice', label: '供货价', align: 'left', sortable: true, 'sort-method': (a, b) => a.supplierPrice - b.supplierPrice },
{ prop: 'cost', label: '成本', align: 'left', sortable: true, 'sort-method': (a, b) => a.cost - b.cost },
{ prop: 'profitTimes', label: '相当于X件白卖', align: 'left', sortable: true, 'sort-method': (a, b) => a.profitTimes - b.profitTimes }*/
],
isLoading: false,
tableData: [],
jsonFields: {
"商品名称": "productName",
"SKU ID": "productSkuId",
"SKU货号": "skuCode",
"扣款总金额": "deductionAmount",
"扣款次数": "times"
},
costList: []
}
},
components: {
JsonExcel
},
methods: {
beforeGetList() {
this.$userCheck(this.mallId).then(() => {
this.toLoad()
}).catch((err) => {
this.mallId = ''
})
},
async toLoad() {
if (!this.mallId) {
Message.error("请选择店铺")
return
}
if (!this.searchDate) {
Message.error("请选择时间范围")
return
}
this.reqData.pageNum = 1
this.tableData = []
this.isLoading = true
let startTime = this.searchDate[0].getTime()
let endTime = this.searchDate[1].getTime()
while(true) {
if ((endTime - startTime) > 2592000000) {
this.reqData.pageNum = 1
await this.getAfterSaleDeductList(startTime, startTime + 2592000000)
startTime = startTime + 2592000000
} else {
this.reqData.pageNum = 1
await this.getAfterSaleDeductList(startTime, endTime)
break
}
}
this.isLoading = false
},
async getAfterSaleDeductList(startTime, endTime) {
let res = await sendChromeAPIMessage({
url: `api/merchant/fund/deduction/after-sales-compensation/query`,
needMallId: true,
mallId: this.mallId,
data: {
...this.reqData,
beginFundTime: startTime,
endFundTime: endTime
}})
if (res.errorCode == 1000000) {
for (let i = 0; i < res.result.resultList.length; i++) {
let item = res.result.resultList[i]
let flag = false
for (let j = 0; j < item.productSkuIdList.length; j++) {
for ( let k = 0; k < this.tableData.length; k++) {
if (item.productSkuIdList[j] == this.tableData[k].productSkuId) {
flag = true
this.tableData[k].times++
this.tableData[k].deductionAmount = Math.round((this.tableData[k].deductionAmount + new Number(item.deductionAmount.digitalText))*100)/100
if (!this.tableData[k].skuCode) {
this.tableData[k].skuCode = item.skuExtCodeList[j]
}
break
}
}
if (!flag) {
this.tableData.push({
productName: item.goodsNameList[j],
productSkuId: item.productSkuIdList[j],
deductionAmount: new Number(item.deductionAmount.digitalText),
skuCode: item.skuExtCodeList[j],
times: 1,
supplierPrice: ''
})
flag = true
}
}
}
if ((this.reqData.pageSize * this.reqData.pageNum) < res.result.total) {
this.reqData.pageNum ++
await this.getAfterSaleDeductList(startTime, endTime)
}
}
},
async getProductList(params) {
let res = await sendChromeAPIMessage({
url: 'bg-visage-mms/product/skc/pageQuery',
needMallId: true,
mallId: this.mallId,
data: {
...params
}})
if (res.errorCode == 1000000) {
res.result.pageItems.map((item) => {
item.productSkuSummaries.map(item1 => {
for (let i = 0; i < this.tableData.length; i++) {
if (this.tableData[i].productSkuId == item1.productSkuId) {
this.tableData[i].supplierPrice = item1.supplierPrice / 100
this.tableData[i].productUrl = item1.thumbUrl
this.tableData[i].skcCode = item.extCode
this.tableData[i].skuCode = item1.extCode
break
}
}
})
})
if ((params.page * params.pageSize) < res.result.total) {
params.page ++
await this.getProductList(params)
}
}
},
async getSkuCostList() {
this.$http.post(`/api/skuCost/listAll`, null, {
params: {
mallId: this.mallId
}
}).then(res => {
if (res.code == 0) {
this.costList = res.data
this.tableData.map(item => {
for (let i = 0; i < this.costList.length; i++) {
if (item.productSkuId == this.costList[i].sku) {
item.cost = this.costList[i].costPrice
item.profitTimes = Math.round((item.supplierPrice * item.punishRatio + item.cost * item.times) / (item.supplierPrice - item.cost) * 100) / 100
}
}
})
}
})
},
}
}
</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;
}
}
}
</style>

View File

@@ -0,0 +1,301 @@
<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="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>
</template>
</ai-title>
<template slot="content">
<div class="content">
<ai-search-bar>
<template #left>
<div class="search-item">
<label>售后申请年份</label>
<el-date-picker
v-model="reqData.afsApplyYear"
type="year"
value-format='yyyy'
placeholder="选择年">
</el-date-picker>
</div>
<div class="search-item">
<label>扣罚倍数</label>
<el-select v-model="reqData.punishRatioList" multiple placeholder="请选择" size="small">
<el-option
v-for="item in punishRatioOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
</template>
<template #right>
<el-button type="primary" @click="beforeGetList">加载</el-button>
</template>
</ai-search-bar>
<ai-card title="数据明细" style="padding-bottom: 40px;">
<template #right>
<json-excel
:data="tableData"
:fields="jsonFields"
name="售后统计列表.xls"
worksheet="售后统计列表">
<el-button type="primary" :disabled="tableData.length == 0">下载数据</el-button>
</json-excel>
</template>
<ai-table
:isShowPagination="false"
:tableData="tableData"
:col-configs="colConfigs"
:total="tableData.length"
height="700"
style="margin-top: 8px;"
@getList="() => {}">
<el-table-column slot="productName" width="400px" :show-overflow-tooltip='true' label="商品名称" fixed="left">
<template slot-scope="scope">
<div>
<el-image :src="scope.row.productUrl" style="width: 40px; height: 40px" class="image" :preview-src-list="[scope.row.productUrl]" />
{{ scope.row.productName }}
</div>
</template>
</el-table-column>
</ai-table>
</ai-card>
</div>
</template>
</ai-list>
</template>
<script>
import { Message } from 'element-ui'
import {sendChromeAPIMessage, sendTemuSellerAgentMessage } from '@/api/chromeApi'
import {timestampToTime} from '@/utils/date'
import JsonExcel from 'vue-json-excel'
export default {
name: 'AfterSaleStat',
data () {
return {
mallId: '',
reqData: {
pageNo: 1,
pageSize: 100,
afsApplyYear: '2024',
punishRatioList: []
},
punishRatioOptions: [
{label: '0', value: '0'},
{label: '1', value: '1'},
{label: '1.5', value: '1.5'},
{label: '2.5', value: '2.5'},
{label: '5', value: '5'},
],
colConfigs: [
{ slot: 'productName', label: '商品名称', width: '400px', align: 'left', fixed: 'left' },
{ prop: 'skcCode', label: 'SKC货号', align: 'left', height: '40px', fixed: 'left' },
{ prop: 'productSkuId', label: 'SKU ID', align: 'left' },
{ prop: 'skuCode', label: 'SKU货号', align: 'left' },
{ prop: 'rawQualityScore', label: '品质分', align: 'left', sortable: true, 'sort-method': (a, b) => a.rawQualityScore - b.rawQualityScore },
{ prop: 'punishRatio', label: '扣罚总倍数', align: 'left', sortable: true, 'sort-method': (a, b) => a.punishRatio - b.punishRatio },
{ prop: 'times', label: '扣罚次数', align: 'left', sortable: true, 'sort-method': (a, b) => a.times - b.times },
{ prop: 'supplierPrice', label: '供货价', align: 'left', sortable: true, 'sort-method': (a, b) => a.supplierPrice - b.supplierPrice },
{ prop: 'deductionAmount', label: '预计扣款', align: 'left', sortable: true, 'sort-method': (a, b) => a.deductionAmount - b.deductionAmount },
{ prop: 'cost', label: '成本', align: 'left', sortable: true, 'sort-method': (a, b) => a.cost - b.cost },
{ prop: 'profitTimes', label: '相当于X件白卖', align: 'left', sortable: true, 'sort-method': (a, b) => a.profitTimes - b.profitTimes }
],
isLoading: false,
tableData: [],
jsonFields: {
"商品名称": "productName",
"图片URL": "productUrl",
"SKC货号": "skcCode",
"SKU ID": "productSkuId",
"SKU货号": "skuCode",
"品质分": "rawQualityScore",
"扣罚总倍数": "punishRatio",
"扣罚次数": "times",
"供货价": "supplierPrice",
"预计扣款": "deductionAmount",
"成本": "cost",
"相当于X件白卖": "profitTimes"
},
costList: []
}
},
components: {
JsonExcel
},
methods: {
beforeGetList() {
this.$userCheck(this.mallId).then(() => {
this.toLoad()
}).catch((err) => {
this.mallId = ''
})
},
async toLoad() {
if (!this.mallId) {
Message.error("请选择店铺")
return
}
this.reqData.pageNo = 1
this.tableData = []
this.currentIndex = 0
this.isLoading = true
await this.getAfterSalesList('101')
this.reqData.pageNo = 1
await this.getAfterSalesList('202')
this.reqData.pageNo = 1
await this.getAfterSalesList('102')
let skuIds = []
this.tableData.map(item => {
skuIds.push(item.productSkuId)
})
this.reqData.pageNo = 1
await this.getProductList({page: this.reqData.pageNo,
pageSize: this.reqData.pageSize,
productSkuIds: skuIds})
this.costList = []
await this.getSkuCostList()
this.isLoading = false
},
async getAfterSalesList(zone) {
this.reqData.bizDrType = zone
let res = await sendTemuSellerAgentMessage({
url: `mms/api/appalachian/afs/queryPageV2`,
needMallId: true,
mallId: this.mallId,
data: this.reqData})
if (res.errorCode == 1000000) {
for (let i = 0; i < res.result.list.length; i++) {
let item = res.result.list[i]
let flag = false
for (let j = 0; j < item.productSkuId.length; j++) {
for ( let k = 0; k < this.tableData.length; k++) {
if (item.productSkuId[j] == this.tableData[k].productSkuId) {
flag = true
this.tableData[k].times++
this.tableData[k].punishRatio = this.tableData[k].punishRatio + new Number(item.punishRatio)
break
}
}
if (!flag) {
this.tableData.push({
productName: item.goodsName,
productUrl: '',
skcCode: '',
productSkuId: item.productSkuId[j],
punishRatio: new Number(item.punishRatio),
rawQualityScore: item.rawQualityScore,
skuCode: '',
times: 1,
cost: '',
supplierPrice: '',
deductionAmount: null,
profitTimes: ''
})
flag = true
}
}
}
if ((this.reqData.pageSize * this.reqData.pageNo) < res.result.total) {
this.reqData.pageNo ++
await this.getAfterSalesList(zone)
}
} else if (res.error_code == 40001) {
this.isLoading = false
this.$store.commit("setTemuAlertShow", true)
}
},
async getProductList(params) {
let res = await sendChromeAPIMessage({
url: 'bg-visage-mms/product/skc/pageQuery',
needMallId: true,
mallId: this.mallId,
data: {
...params
}})
if (res.errorCode == 1000000) {
res.result.pageItems.map((item) => {
item.productSkuSummaries.map(item1 => {
for (let i = 0; i < this.tableData.length; i++) {
if (this.tableData[i].productSkuId == item1.productSkuId) {
this.tableData[i].supplierPrice = item1.supplierPrice / 100
this.tableData[i].deductionAmount = Math.round((item1.supplierPrice / 100 * this.tableData[i].punishRatio) * 100)/ 100
this.tableData[i].productUrl = item1.thumbUrl
this.tableData[i].skcCode = item.extCode
this.tableData[i].skuCode = item1.extCode
break
}
}
})
})
if ((params.page * params.pageSize) < res.result.total) {
params.page ++
await this.getProductList(params)
}
}
},
async getSkuCostList() {
this.$http.post(`/api/skuCost/listAll`, null, {
params: {
mallId: this.mallId
}
}).then(res => {
if (res.code == 0) {
this.costList = res.data
this.tableData.map(item => {
for (let i = 0; i < this.costList.length; i++) {
if (item.productSkuId == this.costList[i].sku) {
item.cost = this.costList[i].costPrice
item.profitTimes = Math.round((item.supplierPrice * item.punishRatio + item.cost * item.times) / (item.supplierPrice - item.cost) * 100) / 100
}
}
})
}
})
},
}
}
</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;
}
}
}
</style>

View File

@@ -0,0 +1,361 @@
<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="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">
<div style="margin-bottom: 5px">
<el-alert
title="郑重承诺由于业务逻辑需要TEMU助手将会以最小范围内存储SKU成本信息平台将会做好保密不泄露、不出售任何数据。使用与成本管理以及利润计算等相关功能将视为授权TEMU助手存储相关信息。"
type="error"
:closable="false">
</el-alert>
</div>
<ai-search-bar>
<template #left>
<div class="search-item">
<el-checkbox v-model="search.unSet">只显示未填写</el-checkbox>
</div>
<div class="search-item">
<label>SKC ID</label>
<el-input size="small" clearable placeholder="请输入SKC ID" v-model="search.skc"></el-input>
</div>
<div class="search-item">
<label>SKU ID</label>
<el-input size="small" clearable placeholder="请输入SKU ID" v-model="search.sku"></el-input>
</div>
<div class="search-item">
<label>SKC货号</label>
<el-input size="small" clearable placeholder="请输入SKC货号" v-model="search.skcCode"></el-input>
</div>
</template>
<template #right>
<!--<el-button type="primary" @click="toLoad">加载</el-button>-->
</template>
</ai-search-bar>
<ai-card title="SKU明细" style="padding-bottom: 40px;">
<template #right>
<el-button type="primary" @click="exportToExcel">导出</el-button>
<el-button type="primary" @click="toUpload">导入</el-button>
</template>
<ai-table
:isShowPagination="false"
:tableData="filteredData"
:col-configs="colConfigs"
height="600"
style="margin-top: 8px;"
@getList="() => {}">
<el-table-column slot="costPrice" label="成本价格" :sortable="true" :sort-method="(a, b) => a.costPrice - b.costPrice">
<template slot-scope="{ row }">
<el-input
v-if="row.edit"
v-model="row.editValue"
type="number"
size="small"
></el-input>
<span v-else>{{ row.costPrice }}</span>
</template>
</el-table-column>
<el-table-column slot="options" label="操作" width="80px" show-overflow-tooltip align="center" fixed="right">
<template slot-scope="{ row }">
<el-button
v-if="!row.edit"
size="small"
icon="el-icon-edit"
@click="row.edit = true"
></el-button>
<el-button
v-if="row.edit"
size="small"
type="success"
icon="el-icon-circle-check"
@click="handleSave(row)"
></el-button>
</template>
</el-table-column>
</ai-table>
</ai-card>
<AiDialog
title="成本导入"
:visible.sync="costDlgShow"
:close-on-click-modal="false"
customFooter
width="1290px">
<el-form :model="costForm" ref="costForm" label-width="180px" class="form">
<el-form-item label="上传excel" prop="file" :rules="[{ required: true, message: '请上传文件', trigger: 'blur' }]" style="width: 100%;">
<ai-uploader isImport v-model="costForm.file" fileType="file" :limit="1"
acceptType=".xlsx," :clearable="false">
<template #trigger>
<el-button icon="iconfont iconfangda">选择文件</el-button>
</template>
<template #tips>最多上传1个文件,单个文件最大10MB仅支持Excel格式</template>
</ai-uploader>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="costDlgShow = false">关 闭</el-button>
<el-button type="primary" @click="importConfirm">确 定</el-button>
</span>
</AiDialog>
</template>
</ai-list>
</template>
<script>
import {sendChromeAPIMessage} from '@/api/chromeApi'
import {timestampToTime} from '@/utils/date'
import { Message } from 'element-ui'
import * as XLSX from 'xlsx'
import { saveAs } from 'file-saver'
export default {
name: 'CostManageTemu',
data () {
return {
isLoading: false,
list: [],
mallId: '',
colConfigs: [
{ prop: 'skc', label: 'SKC ID', align: 'left' },
{ prop: 'skcCode', label: 'SKC货号', align: 'left' },
{ prop: 'sku', label: 'SKU ID', align: 'left' },
{ prop: 'skuCode', label: 'SKU货号', align: 'left' },
{ prop: 'price', label: '申报价格', align: 'left', sortable: true, 'sort-method': (a, b) => a.price - b.price },
{ slot: 'costPrice', label: '成本价格', align: 'left' },
{ prop: 'profitPercent', label: '利润率(%)', align: 'left', sortable: true, 'sort-method': (a, b) => a.profitPercent - b.profitPercent }
],
search: {
unSet: false,
skc: '',
sku: '',
skcCode: ''
},
tableData: [],
currentPage: 1,
costList: [],
costDlgShow: false,
costForm: {
file: null
}
}
},
computed: {
filteredData() {
const filteredData = this.list.filter(item => {
let flag1 = true, flag2 = true, flag3 = true, flag4 = true
if (this.search.unSet) {
if (item.costPrice) flag1 = false
}
if (this.search.skc) {
if (!(item.skc == this.search.skc)) {
flag2 = false
}
}
if (this.search.sku) {
if (!(item.sku == this.search.sku)) {
flag3 = false
}
}
if (this.search.skcCode) {
if (!(item.skcCode == this.search.skcCode)) {
flag5 = false
}
}
return flag1 && flag2 && flag3 && flag4
})
return filteredData
}
},
methods: {
beforeGetList() {
this.list = []
this.currentPage = 1
this.costList = []
if (!this.mallId) {
Message.error("请先选择店铺")
return
}
this.$userCheck(this.mallId).then(() => {
this.isLoading = true
this.getList()
}).catch((err) => {
this.isLoading = false
})
},
async getList () {
let res = await sendChromeAPIMessage({
url: `bg-visage-mms/product/skc/pageQuery`,
needMallId: true,
mallId: this.mallId,
data: {
page: this.currentPage,
pageSize: 100
}})
if (res.errorCode == 1000000) {
for(let i = 0;i < res.result.pageItems.length; i++) {
let item = res.result.pageItems[i];
let data = {skc: item.productSkcId,
skcCode: item.extCode
}
for (let j = 0; j < item.productSkuSummaries.length; j++) {
let sku = item.productSkuSummaries[j]
data = {...data, sku: sku.productSkuId,
skuAttr: null,
skuCode: sku.extCode,
price: sku.supplierPrice / 100,
costPrice: '',
profitPercent: null,
edit: false,
editValue: null
}
this.list.push(data)
}
}
if (res.result.pageItems.length == 100 && (res.result.total > 100*this.currentPage)) {
this.currentPage++
await this.sleepSync(200)
await this.getList()
} else {
this.getSkuCostList()
}
}
},
sleepSync(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds));
},
getSkuCostList() {
this.$http.post(`/api/skuCost/listAll`, null, {
params: {
mallId: this.mallId
}
}).then(res => {
if (res.code == 0) {
this.costList = res.data
for (let i = 0; i < this.costList.length; i++) {
for (let j = 0; j < this.list.length; j++) {
if (this.costList[i].sku == this.list[j].sku) {
this.list[j].costPrice = this.costList[i].costPrice
this.list[j].editValue = this.costList[i].costPrice
this.list[j].profitPercent = Math.round((this.list[j].price - this.list[j].costPrice) / this.list[j].price * 10000) /100
}
}
}
this.isLoading = false
} else {
this.isLoading = false
}
})
},
handleSave(row) {
this.$http.post(`/api/skuCost/addOrUpdate`, [{
mallId: this.mallId,
costPrice: row.editValue,
skc: row.skc,
sku: row.sku
}]).then((res) => {
if (res.code == 0) {
row.edit = false
row.costPrice = row.editValue
row.profitPercent = Math.round((row.price - row.costPrice) / row.price * 10000) / 100
Message.success("修改成功")
}
})
},
toUpload() {
this.costDlgShow = true
},
importConfirm() {
this.$refs.costForm.validate((valid) => {
const data = new FormData()
data.append('file', this.costForm.file[0].raw);
this.$http.post(`/api/skuCost/importStock`, data).then(res => {
if (res.code === 0) {
this.costDlgShow = false
this.$message.success('导入成功')
this.getList()
}
})
})
},
exportToExcel() {
// 假设你有一个表格数据的数组
const data = [
["店铺ID", "SKC ID", "SKC货号", "SKU ID", "属性集", "SKU货号", "申报价格", "成本价格"]
]
this.filteredData.map(item => {
data.push([this.mallId, item.skc, item.skcCode, item.sku, item.skuAttr, item.skuCode, item.price, item.costPrice])
})
// 将数据转换为工作表
const worksheet = XLSX.utils.aoa_to_sheet(data);
// 创建工作簿并添加工作表
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
// 生成Excel文件
const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
// 使用blob和FileReader创建一个Blob URL
const dataBlob = new Blob([excelBuffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8' });
const blobUrl = window.URL.createObjectURL(dataBlob);
// 使用saveAs下载文件
saveAs(dataBlob, 'SKU列表.xlsx');
// 清理
window.URL.revokeObjectURL(blobUrl);
}
}
}
</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;
}
}
}
</style>

View File

@@ -68,12 +68,12 @@
<template slot="content" v-if="type === '0'">
<div class="top">
<div class="item">
<h2>今日销量</h2>
<p>{{ todayTotal }}</p>
<h2>今日销量/销售额</h2>
<p>{{ todayTotal }}/{{ todayMoney }}</p>
</div>
<div class="item">
<h2>今日销售额</h2>
<p>{{ todayMoney }}</p>
<h2>今日利润/利润率</h2>
<p>{{ profitMoney }}/{{ profitPercent }}</p>
</div>
<div class="item">
<h2>可用库存总量()</h2>
@@ -103,17 +103,12 @@
</el-tooltip></span></h2>
<p>{{ inroadTotalMoney }}</p>
</div>
<div class="item">
<h2>已收货总量()</h2>
<p>{{ deliveryTotal }}</p>
</div>
<div class="item">
<h2>已收货总货值</h2>
<p>{{ deliveryMoney }}</p>
</div>
</div>
<ai-card title="数据明细" style="padding-bottom: 40px;">
<template #right>
<div class="search-item" style="width: 160px;">
<el-checkbox v-model="showTodaySale">只显示今日有销量</el-checkbox>
</div>
<json-excel
:data="list"
v-if="type === '0'"
@@ -137,10 +132,10 @@
<ai-table
ref="table0"
:isShowPagination="false"
:tableData="list"
:tableData="filteredData"
height="500"
:col-configs="colConfigs"
:total="list.length"
:total="filteredData.length"
style="margin-top: 8px;"
@getList="() => {}">
</ai-table>
@@ -224,13 +219,13 @@ import { Message } from 'element-ui'
currentPage: 1,
todayTotal: 0,
todayMoney: 0.0,
profitMoney: 0.0,
profitPercent: 0,
inventoryTotal: 0,
inventoryMoney: 0.0,
inroadTotal: 0,
inroadTotalMoney: 0.0,
deliveryTotal: 0,
deliveryMoney: 0.0,
allProductList: [],
costList: [],
startDate: '',
endDate: '',
skuIds: [],
@@ -264,6 +259,9 @@ import { Message } from 'element-ui'
"用户累计加购数量": "inCardNumber",
"已订阅待提醒到货": "nomsgSubsCntCntSth",
"销售数据 - 今日": "todaySaleVolume",
"销售数据 - 今日销售额": "saleMoney",
"销售数据 - 利润": "profitMoney",
"销售数据 - 利润率": "profitPercent",
"销售数据 - 近7日": "lastSevenDaysSaleVolume",
"销售数据 - 近30天": "lastThirtyDaysSaleVolume",
"库存数据 - 仓内可用库存": "inventoryNumInfo.warehouseInventoryNum",
@@ -296,11 +294,22 @@ import { Message } from 'element-ui'
skuDownloadForm: {
date: ''
},
showTodaySale: false
}
},
computed: {
...mapState(['mallList']),
filteredData() {
const filteredData = this.list.filter(item => {
if (this.showTodaySale) {
return item.todaySaleVolume > 0
}
return true
})
return filteredData
},
colConfigs () {
return [
{
@@ -362,6 +371,38 @@ import { Message } from 'element-ui'
width: '120px',
align: 'center'
},
{
prop: 'todaySaleVolume',
label: '今日销量',
width: '120px',
align: 'center',
sortable: true,
'sort-method': (a, b) => {
return a.todaySaleVolume - b.todaySaleVolume
}
},
{
prop: 'saleMoney',
label: '销售额',
width: '120px',
align: 'center'
},
{
prop: 'profitMoney',
label: '利润',
width: '120px',
align: 'center',
sortable: true,
'sort-method': (a, b) => {
return a.profitMoney - b.profitMoney
}
},
{
prop: 'profitPercent',
label: '利润率(%)',
width: '120px',
align: 'center'
},
{
prop: 'onSalesDurationOffline',
label: '加入站点时长',
@@ -388,7 +429,6 @@ import { Message } from 'element-ui'
label: '仓内可用库存',
width: '160px',
align: 'center',
fixed: "right",
sortable: true,
'sort-method': (a, b) => {
return a.warehouseInventoryNum - b.warehouseInventoryNum
@@ -550,6 +590,13 @@ import { Message } from 'element-ui'
align: 'center',
fixed: 'left'
},
{
prop: 'supplierPrice',
label: '申报价格',
width: '120px',
align: 'center',
fixed: 'left'
},
{
prop: 'productSkuId',
label: 'SKU ID',
@@ -599,6 +646,7 @@ import { Message } from 'element-ui'
"SKC货号": "skcExtCode",
"SKU ID": "productSkuId",
"SKU货号": "skuExtCode",
"申报价格": "supplierPrice",
"图片链接": "productSkcPicture"
}
@@ -637,15 +685,13 @@ import { Message } from 'element-ui'
this.currentPage = 1
this.todayMoney = 0.0
this.todayTotal = 0
this.profitMoney = 0.0
this.profitPercent = 0
this.inventoryMoney = 0.0
this.inventoryTotal = 0
this.inroadTotalMoney = 0.0
this.inroadTotal = 0
this.deliveryTotal = 0
this.deliveryMoney = 0.0
this.allProductList = []
if (!this.mallId) {
Message.error("请先选择店铺")
return
}
@@ -657,33 +703,27 @@ import { Message } from 'element-ui'
this.isLoading = true
this.$userCheck(this.mallId).then(() => {
this.last30DaySkcList = []
this.getAllProductList()
this.getSkuCostList()
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
getSkuCostList() {
this.$http.post(`/api/skuCost/listAll`, null, {
params: {
mallId: this.mallId
}
})
this.$http.post('/api/deliveryOrder/getAllProductList', null, {
params: {mallId: this.mallId}
}).then(res => {
if (res.code == 0) {
this.allProductList = res.data
this.costList = res.data
}
})
},
getList () {
sendChromeAPIMessage({
url: 'marvel-mms/cn/api/kiana/venom/sales/management/list',
url: 'marvel-mms/cn/api/kiana/venom/sales/management/listWarehouse',
needMallId: true,
mallId: this.mallId,
data: {
@@ -724,32 +764,30 @@ import { Message } from 'element-ui'
for(let j = 0;j < item.skuQuantityDetailList.length; j++) {
let costPrice = this.getCostPrice(item.skuQuantityDetailList[j].productSkuId)
let saleMoney = Math.round(item.skuQuantityDetailList[j].todaySaleVolume * item.skuQuantityDetailList[j].supplierPrice) /100
let profitMoney = Math.round(item.skuQuantityDetailList[j].todaySaleVolume * (item.skuQuantityDetailList[j].supplierPrice / 100 - costPrice) * 100) /100
let profitPercent = Math.round(profitMoney / saleMoney * 10000) /100
data = {...data, ...item.skuQuantityDetailList[j],
saleMoney: saleMoney,
profitMoney: profitMoney,
profitPercent: profitPercent,
productTotalPrice: ((item.skuQuantityDetailList[j].supplierPrice / 100) * item.skuQuantityDetailList[j].inventoryNumInfo.warehouseInventoryNum).toFixed(2),
inroadTotalPrice: ((item.skuQuantityDetailList[j].supplierPrice / 100) * (item.skuQuantityDetailList[j].inventoryNumInfo.waitOnShelfNum + item.skuQuantityDetailList[j].inventoryNumInfo.waitReceiveNum)).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.inventoryMoney += new Number(((item.skuQuantityDetailList[j].supplierPrice / 100) * item.skuQuantityDetailList[j].inventoryNumInfo.warehouseInventoryNum).toFixed(2))
this.inventoryMoney = new Number(this.inventoryMoney.toFixed(2))
this.inroadTotal += (item.skuQuantityDetailList[j].inventoryNumInfo.waitOnShelfNum + item.skuQuantityDetailList[j].inventoryNumInfo.waitReceiveNum)
this.inroadTotalMoney += new Number(((item.skuQuantityDetailList[j].supplierPrice / 100) * (item.skuQuantityDetailList[j].inventoryNumInfo.waitOnShelfNum + item.skuQuantityDetailList[j].inventoryNumInfo.waitReceiveNum)).toFixed(2))
this.inroadTotalMoney = new Number(this.inroadTotalMoney.toFixed(2))
this.adviceProduceNum = item.skuQuantityDetailList[j].adviceProduceNum || '-'
this.availableProduceNum = item.skuQuantityDetailList[j].availableProduceNum || '-'
this.list.push(data);
// 计算已发货货值
for(let k = 0; k < this.allProductList.length; k++) {
if (this.allProductList[k].product_sku_id == data.productSkuId) {
this.deliveryMoney += (item.skuQuantityDetailList[j].supplierPrice / 100) * this.allProductList[k].product_sku_number
this.deliveryMoney = new Number(this.deliveryMoney.toFixed(2))
}
}
this.todayTotal += item.skuQuantityDetailList[j].todaySaleVolume
this.profitMoney += item.skuQuantityDetailList[j].todaySaleVolume * (item.skuQuantityDetailList[j].supplierPrice / 100 - costPrice)
this.todayMoney += (item.skuQuantityDetailList[j].supplierPrice / 100) * item.skuQuantityDetailList[j].todaySaleVolume
this.inventoryTotal += item.skuQuantityDetailList[j].inventoryNumInfo.warehouseInventoryNum
this.inventoryMoney += (item.skuQuantityDetailList[j].supplierPrice / 100) * item.skuQuantityDetailList[j].inventoryNumInfo.warehouseInventoryNum
this.inroadTotal += (item.skuQuantityDetailList[j].inventoryNumInfo.waitOnShelfNum + item.skuQuantityDetailList[j].inventoryNumInfo.waitReceiveNum)
this.inroadTotalMoney += (item.skuQuantityDetailList[j].supplierPrice / 100) * (item.skuQuantityDetailList[j].inventoryNumInfo.waitOnShelfNum + item.skuQuantityDetailList[j].inventoryNumInfo.waitReceiveNum)
this.adviceProduceNum = item.skuQuantityDetailList[j].adviceProduceNum || '-'
this.availableProduceNum = item.skuQuantityDetailList[j].availableProduceNum || '-'
this.list.push(data);
}
}
if (this.pageSize == res.result.subOrderList.length) {
this.currentPage ++
@@ -757,6 +795,12 @@ import { Message } from 'element-ui'
this.getList()
}, 1500)
} else {
this.profitMoney = Math.round(this.profitMoney * 100) / 100
this.todayMoney = Math.round(this.todayMoney * 100) / 100
this.profitPercent = Math.round((this.profitMoney / this.todayMoney) * 10000) /100 + '%'
this.inventoryMoney = Math.round(this.inventoryMoney * 100) / 100
this.inroadTotalMoney = Math.round(this.inroadTotalMoney * 100) / 100
this.isLoading = false
Message.success('销售数据加载完成,可进行导出')
@@ -782,7 +826,8 @@ import { Message } from 'element-ui'
productSkuId: item.productSkuId,
skuExtCode: item.skuExtCode,
skcExtCode: item.skcExtCode,
productSkcPicture: item.productSkcPicture
productSkcPicture: item.productSkcPicture,
supplierPrice: this.getSupplierPrice(item.productSkuId)
}
let date = new Date()
date.setDate(date.getDate() - 31)
@@ -888,7 +933,8 @@ import { Message } from 'element-ui'
temp = {
'SKC': 'skc',
'SKU': 'sku',
'SKU货号': 'skuExtCode'
'SKU货号': 'skuExtCode',
'申报价格': 'supplierPrice'
}
let beginTime = new Date(this.skuDownloadForm.date[0])
let endTime = new Date(this.skuDownloadForm.date[1])
@@ -914,7 +960,8 @@ import { Message } from 'element-ui'
let temp = {
sku: item.productSkuId,
skc: item.productSkcId,
skuExtCode: item.skuExtCode
skuExtCode: item.skuExtCode,
supplierPrice: this.getSupplierPrice(item.productSkuId)
}
beginTime = new Date(this.skuDownloadForm.date[0])
endTime = new Date(this.skuDownloadForm.date[1])
@@ -966,6 +1013,22 @@ import { Message } from 'element-ui'
reqSkusIds = []
}
}
},
getSupplierPrice(productSkuId) {
for (let i = 0; i < this.list.length; i++) {
if (this.list[i].productSkuId == productSkuId) {
return this.list[i].supplierPrice / 100
}
}
return 0
},
getCostPrice(productSkuId) {
for (let i = 0; i < this.costList.length; i++) {
if (this.costList[i].sku == productSkuId) {
return this.costList[i].costPrice
}
}
return 0
}
}
}

View File

@@ -0,0 +1,156 @@
<template>
<ai-list class="list" v-loading="isLoading" element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading">
<ai-title
slot="title"
title="证书中心"
isShowBottomBorder>
</ai-title>
<template slot="content">
<ai-card title="待补充证书列表" style="padding-bottom: 40px;">
<template #right>
<el-button type="primary" @click="exportToExcel">导出</el-button>
</template>
<ai-table
:isShowPagination="false"
:tableData="list"
:col-configs="colConfigs"
height="600"
style="margin-top: 8px;"
@getList="() => {}">
</ai-table>
</ai-card>
</template>
</ai-list>
</template>
<script>
import {sendGeiwohuoAPIMessage} from '@/api/chromeApi'
import {timestampToTime} from '@/utils/date'
import { Message } from 'element-ui'
import * as XLSX from 'xlsx'
import { saveAs } from 'file-saver'
export default {
name: 'CertCenterShein',
data () {
return {
isLoading: false,
list: [],
mallId: '',
colConfigs: [
{ prop: 'spu', label: 'SPU', align: 'left' },
{ prop: 'productName', label: '商品标题', align: 'left' },
{ prop: 'skc', label: 'SKC ID', align: 'left' },
{ prop: 'mainAttrName', label: '主规格', align: 'left' },
{ prop: 'certificateTypeName', label: '证书类型', align: 'left' },
{ prop: 'mergeSiteName', label: '管控地区', align: 'left' },
{ prop: 'subSite', label: '管控子站点', align: 'left' },
{ slot: 'uploadEndTime', label: '上传截止时间', align: 'left' }
],
tableData: [],
currentPage: 1,
}
},
mounted () {
this.isLoading = true
this.getList()
},
methods: {
async getList () {
let res = await sendGeiwohuoAPIMessage({
url: `spmp-api-prefix/spmp/certificate/get_skc_certificate_miss_list?page_num=${this.currentPage}&page_size=200`,
method: 'POST',
data: {}})
if (res.code == '0') {
for(let i = 0;i < res.info.data.length; i++) {
let item = res.info.data[i];
let mainSite = []
let subSite = []
item.merge_site_info_list.map(e => {
mainSite.push(e.merge_site_name)
subSite = subSite.concat(e.sub_site_list)
})
let data = {spu: item.spu_name,
productName: item.skc_name_cn,
skc: item.skc_name,
mainAttrName: item.main_attribute_name,
uploadEndTime: item.upload_end_time,
mergeSiteName: mainSite.join(','),
certificateTypeName: item.certificate_type_name,
subSite: subSite.join(',')
}
this.list.push(data)
}
if (res.info.data.length == 200 && (res.info.meta.count > 200*this.currentPage)) {
this.currentPage++
await this.sleepSync(200)
await this.getList()
} else {
this.isLoading = false
}
} else if (res.code == 100004 || res.code == 20302) {
this.isLoading = false
this.$store.commit("setSheinAlertShow", true)
}
},
sleepSync(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds));
},
exportToExcel() {
// 假设你有一个表格数据的数组
const data = [
["SPU", "商品标题", "SKC ID", "主规格", "证书类型", "管控地区", "管控子站点", "上传截止时间"]
]
this.list.map(item => {
data.push([item.spu, item.productName, item.skc, item.mainAttrName, item.certificateTypeName, item.mergeSiteName, item.subSite, item.uploadEndTime])
})
// 将数据转换为工作表
const worksheet = XLSX.utils.aoa_to_sheet(data);
// 创建工作簿并添加工作表
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
// 生成Excel文件
const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
// 使用blob和FileReader创建一个Blob URL
const dataBlob = new Blob([excelBuffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8' });
const blobUrl = window.URL.createObjectURL(dataBlob);
// 使用saveAs下载文件
saveAs(dataBlob, '待补充证书列表.xlsx');
// 清理
window.URL.revokeObjectURL(blobUrl);
}
}
}
</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;
}
}
}
</style>

View File

@@ -51,11 +51,11 @@
label="商品列表"
value="0">
</el-option>
<el-option
<!--<el-option
key="1"
label="草稿箱列表"
value="1">
</el-option>
</el-option>-->
</el-select>
</div>
<div class="search-item">
@@ -256,6 +256,9 @@ import { Message } from 'element-ui'
createTime: item.create_time
};
})
} else if (res.code == 100004 || res.code == 20302) {
this.isLoading = false
this.$store.commit("setSheinAlertShow", true)
}
},
productHandleSelectionChange(val) {

View File

@@ -0,0 +1,354 @@
<template>
<ai-list class="list" v-loading="isLoading" element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading">
<ai-title
slot="title"
title="成本管理"
isShowBottomBorder>
</ai-title>
<template slot="content">
<div style="margin-bottom: 5px">
<el-alert
title="郑重承诺由于业务逻辑需要TEMU助手将会以最小范围内存储SKU成本信息平台将会做好保密不泄露、不出售任何数据。使用与成本管理以及利润计算等相关功能将视为授权TEMU助手存储相关信息。"
type="error"
:closable="false">
</el-alert>
</div>
<ai-search-bar>
<template #left>
<div class="search-item">
<el-checkbox v-model="search.unSet">只显示未填写</el-checkbox>
</div>
<div class="search-item">
<label>SKC ID</label>
<el-input size="small" clearable placeholder="请输入SKC ID" v-model="search.skc"></el-input>
</div>
<div class="search-item">
<label>SKU ID</label>
<el-input size="small" clearable placeholder="请输入SKU ID" v-model="search.sku"></el-input>
</div>
<div class="search-item">
<label>SKC货号</label>
<el-input size="small" clearable placeholder="请输入SKC货号" v-model="search.skcCode"></el-input>
</div>
</template>
<template #right>
<!--<el-button type="primary" @click="toLoad">加载</el-button>-->
</template>
</ai-search-bar>
<ai-card title="SKU明细" style="padding-bottom: 40px;">
<template #right>
<el-button type="primary" @click="exportToExcel">导出</el-button>
<el-button type="primary" @click="toUpload">导入</el-button>
</template>
<ai-table
:isShowPagination="false"
:tableData="filteredData"
:col-configs="colConfigs"
height="600"
style="margin-top: 8px;"
@getList="() => {}">
<el-table-column slot="costPrice" label="成本价格" :sortable="true" :sort-method="(a, b) => a.costPrice - b.costPrice">
<template slot-scope="{ row }">
<el-input
v-if="row.edit"
v-model="row.editValue"
type="number"
size="small"
></el-input>
<span v-else>{{ row.costPrice }}</span>
</template>
</el-table-column>
<el-table-column slot="options" label="操作" width="80px" show-overflow-tooltip align="center" fixed="right">
<template slot-scope="{ row }">
<el-button
v-if="!row.edit"
size="small"
icon="el-icon-edit"
@click="row.edit = true"
></el-button>
<el-button
v-if="row.edit"
size="small"
type="success"
icon="el-icon-circle-check"
@click="handleSave(row)"
></el-button>
</template>
</el-table-column>
</ai-table>
</ai-card>
<AiDialog
title="成本导入"
:visible.sync="costDlgShow"
:close-on-click-modal="false"
customFooter
width="1290px">
<el-form :model="costForm" ref="costForm" label-width="180px" class="form">
<el-form-item label="上传excel" prop="file" :rules="[{ required: true, message: '请上传文件', trigger: 'blur' }]" style="width: 100%;">
<ai-uploader isImport v-model="costForm.file" fileType="file" :limit="1"
acceptType=".xlsx," :clearable="false">
<template #trigger>
<el-button icon="iconfont iconfangda">选择文件</el-button>
</template>
<template #tips>最多上传1个文件,单个文件最大10MB仅支持Excel格式</template>
</ai-uploader>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="costDlgShow = false">关 闭</el-button>
<el-button type="primary" @click="importConfirm">确 定</el-button>
</span>
</AiDialog>
</template>
</ai-list>
</template>
<script>
import {sendGeiwohuoAPIMessage} from '@/api/chromeApi'
import {timestampToTime} from '@/utils/date'
import { Message } from 'element-ui'
import * as XLSX from 'xlsx'
import { saveAs } from 'file-saver'
export default {
name: 'CostManageShein',
data () {
return {
isLoading: false,
list: [],
mallId: '',
colConfigs: [
{ prop: 'skc', label: 'SKC ID', align: 'left' },
{ prop: 'skcCode', label: 'SKC货号', align: 'left' },
{ prop: 'sku', label: 'SKU ID', align: 'left' },
{ prop: 'skuAttr', label: '属性集', align: 'left' },
{ prop: 'skuCode', label: 'SKU货号', align: 'left' },
{ prop: 'price', label: '申报价格', align: 'left', sortable: true, 'sort-method': (a, b) => a.price - b.price },
{ slot: 'costPrice', label: '成本价格', align: 'left' },
{ prop: 'profitPercent', label: '利润率(%)', align: 'left', sortable: true, 'sort-method': (a, b) => a.profitPercent - b.profitPercent }
],
search: {
unSet: false,
skc: '',
sku: '',
skcCode: ''
},
tableData: [],
currentPage: 1,
costList: [],
costDlgShow: false,
costForm: {
file: null
}
}
},
computed: {
filteredData() {
const filteredData = this.list.filter(item => {
let flag1 = true, flag2 = true, flag3 = true, flag4 = true
if (this.search.unSet) {
if (item.costPrice) flag1 = false
}
if (this.search.skc) {
if (!item.skc.toLowerCase().includes(this.search.skc.toLowerCase())) {
flag2 = false
}
}
if (this.search.sku) {
if (!item.sku.toLowerCase().includes(this.search.sku.toLowerCase())) {
flag3 = false
}
}
if (this.search.skcCode) {
if (!item.skcCode.toLowerCase().includes(this.search.skcCode.toLowerCase())) {
flag4 = false
}
}
return flag1 && flag2 && flag3 && flag4
})
return filteredData
}
},
mounted () {
this.getUserInfo()
this.isLoading = true
this.getList()
},
methods: {
async getUserInfo() {
let res = await sendGeiwohuoAPIMessage({
url: 'sso-prefix/auth/getUser?uuid=' + Date.now(),
method: 'GET'
})
if (res.code == '0' && res.msg == "OK") {
this.mallId = res.info.merchantCode
} else if (res.code == 100004 || res.code == 20302) {
this.isLoading = false
this.$store.commit("setSheinAlertShow", true)
}
},
async getList () {
let res = await sendGeiwohuoAPIMessage({
url: `idms/goods-skc/list`,
method: 'POST',
data: {
pageNumber: this.currentPage,
pageSize: 100,
sortBy7dSaleCnt: 2
}})
if (res.code == '0') {
for(let i = 0;i < res.info.list.length; i++) {
let item = res.info.list[i];
let data = {skc: item.skc,
skcCode: item.supplierCode
}
for (let j = 0; j < item.skuList.length; j++) {
let sku = item.skuList[j]
if (sku.attr == '合计') continue
data = {...data, sku: sku.skuCode,
skuAttr: sku.attr,
skuCode: sku.supplierSku,
price: sku.price,
costPrice: '',
profitPercent: null,
edit: false,
editValue: null
}
this.list.push(data)
}
}
if (res.info.list.length == 100 && (res.info.count > 100*this.currentPage)) {
this.currentPage++
await this.sleepSync(200)
await this.getList()
} else {
this.getSkuCostList()
}
} else if (res.code == 100004 || res.code == 20302) {
this.isLoading = false
this.$store.commit("setSheinAlertShow", true)
}
},
sleepSync(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds));
},
getSkuCostList() {
this.$http.post(`/api/skuCost/listAll`, null, {
params: {
mallId: this.mallId
}
}).then(res => {
if (res.code == 0) {
this.costList = res.data
for (let i = 0; i < this.costList.length; i++) {
for (let j = 0; j < this.list.length; j++) {
if (this.costList[i].sku == this.list[j].sku) {
this.list[j].costPrice = this.costList[i].costPrice
this.list[j].editValue = this.costList[i].costPrice
this.list[j].profitPercent = Math.round((this.list[j].price - this.list[j].costPrice) / this.list[j].price * 10000) /100
}
}
}
this.isLoading = false
}
})
},
handleSave(row) {
this.$http.post(`/api/skuCost/addOrUpdate`, [{
mallId: this.mallId,
costPrice: row.editValue,
skc: row.skc,
sku: row.sku
}]).then((res) => {
if (res.code == 0) {
row.edit = false
row.costPrice = row.editValue
row.profitPercent = Math.round((row.price - row.costPrice) / row.price * 10000) / 100
Message.success("修改成功")
}
})
},
toUpload() {
this.costDlgShow = true
},
importConfirm() {
this.$refs.costForm.validate((valid) => {
const data = new FormData()
data.append('file', this.costForm.file[0].raw);
this.$http.post(`/api/skuCost/importStock`, data).then(res => {
if (res.code === 0) {
this.costDlgShow = false
this.$message.success('导入成功')
this.currentPage = 1
this.list = []
this.isLoading = true
this.getList()
}
})
})
},
exportToExcel() {
// 假设你有一个表格数据的数组
const data = [
["店铺ID", "SKC ID", "SKC货号", "SKU ID", "属性集", "SKU货号", "申报价格", "成本价格"]
]
this.filteredData.map(item => {
data.push([this.mallId, item.skc, item.skcCode, item.sku, item.skuAttr, item.skuCode, item.price, item.costPrice])
})
// 将数据转换为工作表
const worksheet = XLSX.utils.aoa_to_sheet(data);
// 创建工作簿并添加工作表
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
// 生成Excel文件
const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
// 使用blob和FileReader创建一个Blob URL
const dataBlob = new Blob([excelBuffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8' });
const blobUrl = window.URL.createObjectURL(dataBlob);
// 使用saveAs下载文件
saveAs(dataBlob, 'SKU列表.xlsx');
// 清理
window.URL.revokeObjectURL(blobUrl);
}
}
}
</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;
}
}
}
</style>

View File

@@ -2,7 +2,7 @@
<ai-list class="list" v-loading="isLoading" element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading">
<ai-title
slot="title"
title="销售数据(希音)"
title="销售数据"
isShowBottomBorder>
</ai-title>
<template slot="content">
@@ -46,22 +46,17 @@
</div>
<ai-card title="数据明细" style="padding-bottom: 40px;">
<template #right>
<json-excel
:data="list"
:fields="jsonFields"
:before-generate = "startDownload"
name="销售数据.xls"
worksheet="销售统计">
<el-button type="primary">导出数据</el-button>
</json-excel>
<div class="search-item" style="width: 160px;">
<el-checkbox v-model="showTodaySale">只显示今日有销量</el-checkbox>
</div>
</template>
<ai-table
ref="table0"
:isShowPagination="false"
:tableData="list"
:tableData="filteredData"
height="500"
:col-configs="colConfigs"
:total="list.length"
:total="filteredData.length"
style="margin-top: 8px;"
@getList="() => {}">
<el-table-column slot="goodsThumb" width="120px" label="商品图片" fixed="left">
@@ -90,6 +85,7 @@ import { Message } from 'element-ui'
data () {
return {
list: [],
mallId: '',
isLoading: false,
pageSize: 100,
currentPage: 1,
@@ -104,61 +100,7 @@ import { Message } from 'element-ui'
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.waitOnShelfNum",
"库存数据 - 已发货库存": "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",
"在途库存货值(CNY)": "inroadTotalPrice",
"店铺名称": "mallName",
"评分": "mark",
"是否热销": 'hotTag',
"生产建议信息 - 建议生产数": 'adviceProduceNum',
"生产建议信息 - 剩余件数": 'availableProduceNum',
"类目": 'category'
},
showTodaySale: false,
skuSaleNumberFields: {},
skuSaleNumberList: [],
@@ -166,134 +108,52 @@ import { Message } from 'element-ui'
skuDownloadForm: {
date: ''
},
costList: []
}
},
computed: {
filteredData() {
const filteredData = this.list.filter(item => {
if (this.showTodaySale) {
return item.todaySale > 0
}
return true
})
return filteredData
},
colConfigs () {
return [
{
slot: 'goodsThumb',
label: '图片'
},
{
prop: 'supplierCode',
label: '货号',
width: '120px',
align: 'center',
fixed: 'left'
},
{
prop: 'suffixZh',
label: '规格',
width: '160px',
align: 'center',
fixed: 'left'
},
{
prop: 'categoryName',
label: '分类',
"show-overflow-tooltip": true,
width: '100px'
},
{
prop: 'mallStatusName',
label: '状态',
width: '120px',
align: 'center'
},
{
prop: 'skc',
label: 'SKC',
width: '160px',
align: 'center'
},
{
prop: 'ableSaleDays',
label: '可售天数',
align: 'center'
},
{
prop: 'cost',
label: '价格',
width: '120px',
align: 'center'
},
{
prop: 'todaySale',
label: '今日销量',
width: '120px',
align: 'center'
},
{
prop: 'saleInfoWeek',
label: '近7天销量',
width: '120px',
align: 'center'
},
{
prop: 'saleInfoMonth',
label: '本月销量',
width: '120px',
align: 'center'
},
{
prop: 'saleInfoPreMonth',
label: '上月销量',
width: '120px',
align: 'center'
},
{
prop: 'purchaseConfig',
label: '备货逻辑',
width: '160px',
align: 'center'
},
{
prop: 'onWayNum',
label: '在途数量',
width: '160px',
align: 'center'
},
{
prop: 'ivtNum',
label: '库存数量',
width: '160px',
align: 'center'
},
{
prop: 'productTotalPrice',
label: '库存货值(CNY)',
width: '180px',
align: 'center',
fixed: "right",
sortable: true,
'sort-method': (a, b) => {
{slot: 'goodsThumb',label: '图片'},
{prop: 'supplierCode',label: '货号',width: '120px',align: 'center',fixed: 'left'},
{prop: 'suffixZh',label: '规格',width: '160px',align: 'center',fixed: 'left'},
{prop: 'categoryName',label: '分类',"show-overflow-tooltip": true,width: '100px'},
{prop: 'mallStatusName',label: '状态',width: '120px',align: 'center'},
{prop: 'skc',label: 'SKC',width: '160px',align: 'center'},
{prop: 'ableSaleDays',label: '可售天数',align: 'center'},
{prop: 'cost',label: '价格',width: '120px',align: 'center'},
{prop: 'costPrice',label: '成本',width: '120px',align: 'center'},
{prop: 'todaySale',label: '今日销量',width: '120px',align: 'center'},
{prop: 'todayProfit',label: '利润',width: '120px',align: 'center',sortable: true,'sort-method': (a, b) => {
return a.todayProfit - b.todayProfit
}},
{prop: 'saleInfoWeek',label: '近7天销量',width: '120px',align: 'center'},
{prop: 'saleInfoMonth',label: '本月销量',width: '120px',align: 'center'},
{prop: 'saleInfoPreMonth',label: '上月销量',width: '120px',align: 'center'},
{prop: 'purchaseConfig',label: '备货逻辑',width: '160px',align: 'center'},
{prop: 'onWayNum',label: '在途数量',width: '160px',align: 'center'},
{prop: 'ivtNum',label: '库存数量',width: '160px',align: 'center'},
{prop: 'productTotalPrice',label: '库存货值(CNY)',width: '180px',align: 'center',fixed: "right",sortable: true,'sort-method': (a, b) => {
return a.productTotalPrice - b.productTotalPrice
}
},
{
prop: 'inroadTotalPrice',
label: '在途货值(CNY)',
width: '180px',
align: 'center',
fixed: "right",
sortable: true,
'sort-method': (a, b) => {
{prop: 'inroadTotalPrice',label: '在途货值(CNY)',width: '180px',align: 'center',fixed: "right",sortable: true,'sort-method': (a, b) => {
return a.inroadTotalPrice - b.inroadTotalPrice
}
},
{
prop: 'purchaseConfig',
label: '备货逻辑',
align: 'center',
width: '120px',
fixed: "right",
sortable: true,
{prop: 'purchaseConfig',
label: '备货逻辑',align: 'center',width: '120px',fixed: "right",sortable: true,
'sort-method': (a, b) => {
if (a.purchaseConfig > b.purchaseConfig) {
return 1
@@ -304,8 +164,6 @@ import { Message } from 'element-ui'
}
}
}
]
}
},
@@ -329,7 +187,20 @@ import { Message } from 'element-ui'
},
methods: {
beforeGetList() {
async getUserInfo() {
let res = await sendGeiwohuoAPIMessage({
url: 'sso-prefix/auth/getUser?uuid=' + Date.now(),
method: 'GET'
})
if (res.code == '0' && res.msg == "OK") {
this.mallId = res.info.merchantCode
} else if (res.code == 100004 || res.code == 20302) {
this.isLoading = false
this.$store.commit("setSheinAlertShow", true)
}
},
async beforeGetList() {
await this.getUserInfo()
this.list = []
this.currentPage = 1
this.todayMoney = 0.0
@@ -339,6 +210,7 @@ import { Message } from 'element-ui'
this.inroadTotalMoney = 0.0
this.inroadTotal = 0
await this.getSkuCostList()
this.isLoading = true
this.getList()
},
@@ -368,6 +240,7 @@ import { Message } from 'element-ui'
data = {...data,
ableSaleDays: sku.ableSaleDays,
cost: sku.cost,
skuCode: sku.skuCode,
purchaseConfig: (sku.operateTime?sku.operateTime:'/') + '+' + (sku.prepareDay?sku.prepareDay:'/'),
saleInfoMonth: sku.saleInfoMonth,
saleInfoPreMonth: sku.saleInfoPreMonth,
@@ -376,18 +249,20 @@ import { Message } from 'element-ui'
todaySale: sku.todaySale,
onWayNum: sku.onWayNum,
ivtNum: sku.ivtNum,
productTotalPrice: (sku.cost * sku.ivtNum).toFixed(2),
inroadTotalPrice: (sku.cost * sku.onWayNum).toFixed(2)}
costPrice: null,
todayProfit: null,
productTotalPrice: Math.round((sku.cost * sku.ivtNum)*100)/100,
inroadTotalPrice: Math.round((sku.cost * sku.onWayNum)*100)/100}
this.todayTotal += sku.todaySale
this.todayMoney += new Number((sku.cost * sku.todaySale).toFixed(2))
this.todayMoney = new Number(this.todayMoney.toFixed(2))
this.todayMoney += Math.round((sku.cost * sku.todaySale)*100)/100
this.todayMoney = Math.round(this.todayMoney * 100) / 100
this.inventoryTotal += sku.ivtNum
this.inventoryMoney += new Number((sku.cost * sku.ivtNum).toFixed(2))
this.inventoryMoney = new Number(this.inventoryMoney.toFixed(2))
this.inventoryMoney += Math.round((sku.cost * sku.ivtNum)*100)/100
this.inventoryMoney = Math.round(this.inventoryMoney*100)/100
this.inroadTotal += sku.onWayNum
this.inroadTotalMoney += new Number((sku.cost * sku.onWayNum).toFixed(2))
this.inroadTotalMoney = new Number(this.inroadTotalMoney.toFixed(2))
this.inroadTotalMoney += Math.round((sku.cost * sku.onWayNum)*100)/100
this.inroadTotalMoney = Math.round(this.inroadTotalMoney*100)/100
this.list.push(data);
}
}
@@ -398,12 +273,24 @@ import { Message } from 'element-ui'
this.getList()
}, 1500)
} else {
for (let i = 0; i < this.list.length; i++) {
for (let j = 0; j < this.costList.length; j++) {
if (this.list[i].skuCode == this.costList[j].sku) {
this.list[i].costPrice = this.costList[j].costPrice
this.list[i].todayProfit = Math.round((this.list[i].cost - this.costList[j].costPrice) * this.list[i].todaySale * 100) / 100
break
}
}
}
this.isLoading = false
Message.success('销售数据加载完成,可进行导出')
this.$nextTick(() => { //
this.$refs['table0'].doLayout();
})
}
} else if (res.code == 100004 || res.code == 20302) {
this.isLoading = false
this.$store.commit("setSheinAlertShow", true)
} else {
setTimeout(() => {
this.getList()
@@ -413,18 +300,16 @@ import { Message } from 'element-ui'
this.isLoading = false
})
},
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;
}
async getSkuCostList() {
let res = await this.$http.post(`/api/skuCost/listAll`, null, {
params: {
mallId: this.mallId
}
})
}
if (res.code == 0) {
this.costList = res.data
}
},
}
}
</script>

View File

@@ -0,0 +1,905 @@
<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-date-picker
v-model="dateRange"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss"
:default-time="['00:00:00', '23:59:59']">
</el-date-picker>
</div>
<el-button type="button" :class="'el-button el-button--primary'" @click="beginStat">开始统计</el-button>
<el-button type="primary" icon="el-icon-download" @click="downloadPicture">下载图片</el-button>
<json-excel
:data="skcList"
:fields="skcJsonFields"
name="SKC账单数据.xls"
worksheet="SKC账单数据">
<el-button type="primary" icon="el-icon-download">下载SKC统计</el-button>
</json-excel>
<json-excel
:data="skuList"
:fields="skuJsonFields"
name="SKC账单数据.xls"
worksheet="SKC账单数据">
<el-button type="primary" icon="el-icon-download">下载SKU统计</el-button>
</json-excel>
</div>
</template>
</ai-title>
<template slot="content">
<div id="app-content">
<ai-card title="数据概览" style="padding-bottom: 40px;">
<div>
<el-row :gutter="20">
<el-col :span="5">
<div>
<el-statistic
title="销售额/单数"
>
<template slot="formatter">
{{ saleAmount }}/{{ saleCount }}
</template>
</el-statistic>
</div>
</el-col>
<el-col :span="5">
<div>
<el-statistic
group-separator=","
:precision="2"
:value="deductionAmount"
title="扣款"
>
</el-statistic>
</div>
</el-col>
<el-col :span="5">
<div>
<el-statistic
group-separator=","
:precision="2"
:value="costAmount"
title="成本"
></el-statistic>
</div>
</el-col>
<el-col :span="5">
<div>
<el-statistic
group-separator=","
:precision="2"
:value="profitAmount"
title="利润"
></el-statistic>
</div>
</el-col>
<el-col :span="4">
<div>
<el-statistic
group-separator=","
:precision="2"
:value="profitPercent"
title="利润率"
>
<template slot="suffix">%</template>
</el-statistic>
</div>
</el-col>
</el-row>
</div>
</ai-card>
<ai-card title="趋势分析" style="padding-bottom: 40px;">
<div>
<div id="chart1"></div>
</div>
</ai-card>
<div>
<el-row :gutter="20">
<el-col :span="12">
<ai-card title="SKC销售额TOP10排行">
<ai-table
ref="table0"
:isShowPagination="false"
:tableData="topSaleAmountSkcList"
:col-configs="skcColConfigs"
style="margin-top: 8px;">
<el-table-column slot="amount" label="销售额">
<template slot-scope="scope">
<div style="color: red">{{ scope.row.amount }}</div>
</template>
</el-table-column>
<el-table-column slot="profitAmount" label="利润">
<template slot-scope="scope">
<div>{{ scope.row.profitAmount }}</div>
</template>
</el-table-column>
<el-table-column slot="profitPercent" label="利润率">
<template slot-scope="scope">
<div>{{ scope.row.profitPercent + '%' }}</div>
</template>
</el-table-column>
</ai-table>
</ai-card>
</el-col>
<el-col :span="12">
<ai-card title="SKC利润TOP10排行">
<ai-table
ref="table1"
:isShowPagination="false"
:tableData="topSaleProfitSkcList"
:col-configs="skcColConfigs"
style="margin-top: 8px;">
<el-table-column slot="amount" label="销售额">
<template slot-scope="scope">
<div>{{ scope.row.amount }}</div>
</template>
</el-table-column>
<el-table-column slot="profitAmount" label="利润">
<template slot-scope="scope">
<div style="color: red">{{ scope.row.profitAmount }}</div>
</template>
</el-table-column>
<el-table-column slot="profitPercent" label="利润率">
<template slot-scope="scope">
<div>{{ scope.row.profitPercent + '%' }}</div>
</template>
</el-table-column>
</ai-table>
</ai-card>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<ai-card title="SKC利润率TOP10排行">
<ai-table
ref="table2"
:isShowPagination="false"
:tableData="topProfitPercentSkcList"
:col-configs="skcColConfigs"
style="margin-top: 8px;">
<el-table-column slot="amount" label="销售额">
<template slot-scope="scope">
<div>{{ scope.row.amount }}</div>
</template>
</el-table-column>
<el-table-column slot="profitAmount" label="利润">
<template slot-scope="scope">
<div>{{ scope.row.profitAmount }}</div>
</template>
</el-table-column>
<el-table-column slot="profitPercent" label="利润率">
<template slot-scope="scope">
<div style="color: red">{{ scope.row.profitPercent + '%' }}</div>
</template>
</el-table-column>
</ai-table>
</ai-card>
</el-col>
<el-col :span="12">
<ai-card title="SKU销售额TOP10排行">
<ai-table
ref="table3"
:isShowPagination="false"
:tableData="topSaleAmountSkuList"
:col-configs="skuColConfigs"
style="margin-top: 8px;">
<el-table-column slot="amount" label="销售额">
<template slot-scope="scope">
<div style="color: red">{{ scope.row.amount }}</div>
</template>
</el-table-column>
<el-table-column slot="profitAmount" label="利润">
<template slot-scope="scope">
<div>{{ scope.row.profitAmount }}</div>
</template>
</el-table-column>
<el-table-column slot="profitPercent" label="利润率">
<template slot-scope="scope">
<div>{{ scope.row.profitPercent + '%' }}</div>
</template>
</el-table-column>
</ai-table>
</ai-card>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<ai-card title="SKU利润TOP10排行">
<ai-table
ref="table4"
:isShowPagination="false"
:tableData="topSaleProfitSkuList"
:col-configs="skuColConfigs"
style="margin-top: 8px;">
<el-table-column slot="amount" label="销售额">
<template slot-scope="scope">
<div>{{ scope.row.amount }}</div>
</template>
</el-table-column>
<el-table-column slot="profitAmount" label="利润">
<template slot-scope="scope">
<div style="color: red">{{ scope.row.profitAmount }}</div>
</template>
</el-table-column>
<el-table-column slot="profitPercent" label="利润率">
<template slot-scope="scope">
<div>{{ scope.row.profitPercent + '%' }}</div>
</template>
</el-table-column>
</ai-table>
</ai-card>
</el-col>
<el-col :span="12">
<ai-card title="SKU利润率TOP10排行">
<ai-table
ref="table5"
:isShowPagination="false"
:tableData="topProfitPercentSkuList"
:col-configs="skuColConfigs"
style="margin-top: 8px;">
<el-table-column slot="amount" label="销售额">
<template slot-scope="scope">
<div>{{ scope.row.amount }}</div>
</template>
</el-table-column>
<el-table-column slot="profitAmount" label="利润">
<template slot-scope="scope">
<div>{{ scope.row.profitAmount }}</div>
</template>
</el-table-column>
<el-table-column slot="profitPercent" label="利润率">
<template slot-scope="scope">
<div style="color: red">{{ scope.row.profitPercent + '%' }}</div>
</template>
</el-table-column>
</ai-table>
</ai-card>
</el-col>
</el-row>
</div>
</div>
</template>
</ai-list>
</template>
<script>
import {sendGeiwohuoAPIMessage} from '@/api/chromeApi'
import JsonExcel from 'vue-json-excel'
import { Message } from 'element-ui'
import html2canvas from 'html2canvas'
import { DualAxes } from '@antv/g2plot'
export default {
name: 'ExportSaleStatShein',
data () {
return {
storeCode: '',
productList: [],
dateRange: [],
page: 1,
perPage: 100,
orderList: [],
saleAmount: 0.0,
saleCount: 0,
costAmount: 0.0,
profitAmount: 0.0,
profitPercent: 0.0,
deductionAmount: 0.0,
skcList: [],
skuList: [],
leftChartData: [],
rightChartData: [],
isLoading: false,
skcColConfigs: [
{ prop: 'skcCode', label: '货号', width: '180px' },
{ slot: 'amount', label: '销售额', align: 'center' },
{ prop: 'quantity', label: '单数', align: 'center' },
{ prop: 'cost', label: '成本', align: 'center'},
{ slot: 'profitAmount', label: '利润', align: 'center' },
{ slot: 'profitPercent', label: '利润率', align: 'center'},
],
skcJsonFields: {
"SKC": "skc",
"货号": "skcCode",
"销售额": "amount",
"单数": "quantity",
"成本": "cost",
"利润": "profitAmount",
"利润率": "profitPercent",
},
skuColConfigs: [
{ prop: 'skuCode', label: '属性集', width: '220px' },
{ slot: 'amount', label: '销售额', align: 'center' },
{ prop: 'quantity', label: '单数', align: 'center' },
{ prop: 'cost', label: '成本', align: 'center'},
{ slot: 'profitAmount', label: '利润', align: 'center' },
{ slot: 'profitPercent', label: '利润率', align: 'center'},
],
skuJsonFields: {
"SKC": "skc",
"SKU": "sku",
"属性集": "skuCode",
"销售额": "amount",
"单数": "quantity",
"成本": "cost",
"利润": "profitAmount",
"利润率": "profitPercent",
},
chartObj: null
}
},
computed: {
topSaleAmountSkcList() {
const list = Object.assign([], this.skcList)
list.sort((a, b) => b.amount - a.amount)
return list.slice(0, 10)
},
topSaleProfitSkcList() {
const list = Object.assign([], this.skcList)
list.sort((a, b) => b.profitAmount - a.profitAmount)
return list.slice(0, 10)
},
topProfitPercentSkcList() {
const list = Object.assign([], this.skcList)
list.sort((a, b) => b.profitPercent - a.profitPercent)
return list.slice(0, 10)
},
topSaleAmountSkuList() {
const list = Object.assign([], this.skuList)
list.sort((a, b) => b.amount - a.amount)
return list.slice(0, 10)
},
topSaleProfitSkuList() {
const list = Object.assign([], this.skuList)
list.sort((a, b) => b.profitAmount - a.profitAmount)
return list.slice(0, 10)
},
topProfitPercentSkuList() {
const list = Object.assign([], this.skuList)
list.sort((a, b) => b.profitPercent - a.profitPercent)
return list.slice(0, 10)
}
},
components: {
JsonExcel
},
mounted () {
this.$nextTick(() => {
this.initChart1()
})
},
created () {
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;
}
this.getStoreCode()
}
})
},
methods: {
initChart1 () {
/*const data = [
{ year: '1991', value: 3, count: 11, type: '销售额' },
{ year: '1992', value: 4, count: 143, type: '销售额' },
{ year: '1993', value: 3.5, count: 143, type: '销售额' },
{ year: '1991', value: 5, count: 11, type: '利润' },
{ year: '1992', value: 4.9, count: 17, type: '利润' },
{ year: '1993', value: 6, count: 143, type: '利润' }
]
const data1 = [
{ year: '1991', value1: 30.1, count: 11, type: '利润率' },
{ year: '1992', value1: 32.1, count: 143, type: '利润率' },
{ year: '1993', value1: 44.1, count: 143, type: '利润率' }
]*/
this.chartObj = new DualAxes('chart1', {
data: [this.leftChartData, this.rightChartData],
xField: 'day',
yField: ['value', 'value1'],
yAxis: {
value: {
title: {
text: '金额'
},
label: {
formatter: (value) => {
return `${value}`;
}
}
},
value1: {
max: 100,
title: {
text: '利润率'
},
label: {
formatter: (value) => {
return `${value}%`;
}
}
}
},
geometryOptions: [
{
geometry: 'line',
color: ['#FF9A49', '#7A8AA1', '#1EEB73', '#C947AE' ],
isGroup: true,
smooth: true, // 是否平滑
seriesField: 'type'
},
{
geometry: 'line',
color: ['red' ],
lineStyle: {
fill: 'red',
fillOpacity: 0.5,
stroke: 'red',
lineWidth: 4,
strokeOpacity: 0.7,
shadowColor: 'black',
shadowBlur: 10,
shadowOffsetX: 5,
shadowOffsetY: 5,
cursor: 'pointer'
},
isStack: true,
smooth: true, // 是否平滑
seriesField: 'type', // 指定使用第二个Y轴字段
}],
});
this.chartObj.render();
},
async beginStat() {
if (!this.dateRange) {
Message.error("请选择统计时间范围")
return
}
this.page = 1
this.isLoading = true
this.saleAmount = 0.0
this.saleCount = 0
this.costAmount = 0.0
this.profitAmount = 0.0
this.profitPercent = 0.0
this.deductionAmount = 0.0
this.orderList = []
this.skcList = []
this.skuList = []
this.leftChartData = []
this.rightChartData = []
this.chartObj.changeData([this.leftChartData, this.rightChartData])
this.getOrderList()
},
async getStoreCode() {
let res = await sendGeiwohuoAPIMessage({
url: 'sso-prefix/auth/getUser?uuid=' + Date.now(),
method: 'GET'
})
if (res.code == '0' && res.msg == "OK") {
this.storeCode = res.info.merchantCode
this.getPorductList()
} else if (res.code == 100004 || res.code == 20302) {
this.isLoading = false
this.$store.commit("setSheinAlertShow", true)
}
},
getPorductList() {
this.$http.post(`/api/skuCost/listAll`, null, {
params: {
mallId: this.storeCode
}
}).then(res => {
if (res.code == 0) {
this.productList = res.data
}
})
},
async getOrderList() {
let res = await sendGeiwohuoAPIMessage({
url: 'gsfs/finance/reportOrder/dualMode/reportOrderList',
method: 'POST',
data: {
"page": this.page,
"perPage": this.perPage,
"addTimeStart": this.dateRange[0],
"addTimeEnd": this.dateRange[1]
}
})
if (res.code == '0' && res.msg == "OK") {
for (let i = 0; i < res.info.data.length; i++) {
this.orderList.push(res.info.data[i])
}
if (res.info.data.length == this.perPage && (res.info.meta.count > this.perPage*this.page)) {
this.page++
await this.sleepSync(200)
await this.getOrderList()
} else {
this.page = 1
await this.getDetail()
// 计算SKC维度利润
for (let i = 0; i < this.skcList.length; i++) {
this.skcList[i].amount = Math.round(this.skcList[i].amount * 100)/100
this.skcList[i].cost = Math.round(this.skcList[i].cost * 100)/100
this.skcList[i].profitAmount = Math.round((this.skcList[i].amount - this.skcList[i].cost)*100)/100
this.skcList[i].profitPercent = Math.round((this.skcList[i].profitAmount / this.skcList[i].amount)*10000)/100
this.saleAmount += this.skcList[i].amount
this.costAmount += this.skcList[i].cost
this.saleCount += this.skcList[i].quantity
}
this.saleAmount = Math.round(this.saleAmount*100)/100
this.profitAmount = this.saleAmount - this.costAmount
for (let i = 0; i < this.orderList.length; i++) {
let item = this.orderList[i]
let saleAmount = 0
for (let j = 0; j < item.details.length; j++) {
// this.saleAmount -= (item.details[j].deduction.funds ? item.details[j].deduction.funds: 0)
this.deductionAmount += (item.details[j].deduction.funds ? item.details[j].deduction.funds: 0)
saleAmount += item.details[j].income
}
this.leftChartData.push({day: item.addTime, value: Math.round(saleAmount*100)/100, type: '销售额'})
}
this.profitPercent = Math.round((this.profitAmount / this.saleAmount)*10000)/100
// 计算SKU维度利润
for (let i = 0; i < this.skuList.length; i++) {
this.skuList[i].amount = Math.round(this.skuList[i].amount * 100)/100
this.skuList[i].cost = Math.round(this.skuList[i].cost * 100)/100
this.skuList[i].profitAmount = Math.round((this.skuList[i].amount - this.skuList[i].cost)*100)/100
this.skuList[i].profitPercent = Math.round(((this.skuList[i].profitAmount / this.skuList[i].amount)*10000))/100
}
// 计算每天利润和利润率
const tempData = Object.assign([], this.leftChartData)
for (let i = 0; i < this.orderList.length; i++) {
for (let j = 0; j < tempData.length; j++) {
if ((tempData[j].type == '销售额') && (tempData[j].day == this.orderList[i].addTime)) {
for (let m = 0; m < tempData.length; m++) {
if ((tempData[m].type == '成本') && (tempData[m].day == this.orderList[i].addTime)) {
this.leftChartData.push({
day: this.orderList[i].addTime,
value: Math.round((tempData[j].value - tempData[m].value)*100)/100,
type: '利润'
})
this.rightChartData.push({
day: this.orderList[i].addTime,
value1: Math.round(((tempData[j].value - tempData[m].value)/tempData[j].value)*10000)/100,
type: '利润率'
})
}
}
}
}
}
console.log(this.leftChartData)
this.chartObj.changeData([this.leftChartData, this.rightChartData])
this.isLoading = false
}
} else if (res.code == 100004 || res.code == 20302) {
this.isLoading = false
this.$store.commit("setSheinAlertShow", true)
}
},
async getDetail() {
for (let i = 0; i < this.orderList.length; i++) {
let item = this.orderList[i]
for (let j = 0; j < item.details.length; j++) {
if (item.details[j].sourceSystem == "GSFS") {
let currentPage = 1
while(true) {
await this.sleepSync(200)
let res = await sendGeiwohuoAPIMessage({
url: 'gsfs/finance/reportOrder/dualMode/checkOrderList/item',
method: 'POST',
data: {
"page": currentPage,
"perPage": this.perPage,
"reportOrderNo": item.details[j].reportOrderNo,
"secondOrderTypes": [1,2,41,42]
}
})
if (res.code == '0' && res.msg == "OK") {
// calc
this.calculate(res.info.data, item.addTime)
if (res.info.data.length == this.perPage && (res.info.meta.count > this.perPage*currentPage)) {
currentPage ++
} else {
break
}
} else if (res.code == 100004 || res.code == 20302) {
this.isLoading = false
this.$store.commit("setSheinAlertShow", true)
}
}
} else if (item.details[j].sourceSystem == "PMS") {
let currentPage = 1
while(true) {
await this.sleepSync(200)
let res2 = await sendGeiwohuoAPIMessage({
url: 'gmpj/finance/incomeAndExpensesDetail',
isWWW: true,
method: 'POST',
data: {
"page": currentPage,
"perPage": this.perPage,
"reportOrderNo": item.details[j].reportOrderNo
}
})
if (res2.code == '0' && res2.msg == "OK") {
// calc
this.calculate(res2.info.checkDetailList, item.addTime)
if (res2.info.checkDetailList.length == this.perPage && (res2.info.count > this.perPage*currentPage)) {
currentPage ++
} else {
break
}
}
}
}
}
}
},
calculate(data, addTime) {
for (let i = 0; i < data.length; i++) {
let exist = false
let cost = this.getCost(data[i].skuCode) * (data[i].goodsCount || data[i].quantity)
// 按SKC维度统计
for (let j = 0; j < this.skcList.length; j++) {
if (this.skcList[j].skc == (data[i].skc || data[i].skcName)) {
exist = true
if (data[i].incomeAndExpensesTypeName == '支出' || data[i].inAndOutName == '支出') {
this.skcList[j].amount -= data[i].amount || data[i].income
} else if (data[i].incomeAndExpensesTypeName == '收入' || data[i].inAndOutName == '收入') {
this.skcList[j].quantity += data[i].goodsCount || data[i].quantity
this.skcList[j].amount += data[i].amount || data[i].income
this.skcList[j].cost += cost
}
break
}
}
if (!exist) {
if (data[i].incomeAndExpensesTypeName == '支出' || data[i].inAndOutName == '支出') {
this.skcList.push({
skc: (data[i].skc || data[i].skcName),
skcCode: (data[i].goodsSn || data[i].supplierCode),
amount: -(data[i].amount || data[i].income),
quantity: 0,
cost: 0,
profitPercent: 0.0,
profitAmount: 0.0
})
} else if (data[i].incomeAndExpensesTypeName == '收入' || data[i].inAndOutName == '收入') {
this.skcList.push({
skc: (data[i].skc || data[i].skcName),
skcCode: (data[i].goodsSn || data[i].supplierCode),
amount: data[i].amount || data[i].income,
quantity: data[i].goodsCount || data[i].quantity,
cost: cost,
profitPercent: 0.0,
profitAmount: 0.0
})
}
}
// 按SKU维度统计
exist = false
for (let j = 0; j < this.skuList.length; j++) {
if (this.skuList[j].sku == data[i].skuCode) {
exist = true
if (data[i].incomeAndExpensesTypeName == '支出' || data[i].inAndOutName == '支出') {
this.skuList[j].amount -= data[i].amount || data[i].income
} else if (data[i].incomeAndExpensesTypeName == '收入' || data[i].inAndOutName == '收入') {
this.skuList[j].quantity += data[i].goodsCount || data[i].quantity
this.skuList[j].amount += data[i].amount || data[i].income
this.skuList[j].cost += cost
}
break
}
}
if (!exist) {
if (data[i].incomeAndExpensesTypeName == '支出' || data[i].inAndOutName == '支出') {
this.skuList.push({
skc: (data[i].skc || data[i].skcName),
sku: data[i].skuCode,
skuCode: (data[i].goodsSn || data[i].supplierCode) + '/' + (data[i].suffix || data[i].suffixZh),
amount: -(data[i].amount || data[i].income),
quantity: 0,
cost: 0,
profitPercent: 0.0,
profitAmount: 0.0
})
} else if (data[i].incomeAndExpensesTypeName == '收入' || data[i].inAndOutName == '收入') {
this.skuList.push({
skc: (data[i].skc || data[i].skcName),
sku: data[i].skuCode,
skuCode: (data[i].goodsSn || data[i].supplierCode) + '/' + (data[i].suffix || data[i].suffixZh),
amount: data[i].amount || data[i].income,
quantity: data[i].goodsCount || data[i].quantity,
cost: cost,
profitPercent: 0.0,
profitAmount: 0.0
})
}
}
// 计算每天的成本
exist = false
for (let x = 0; x < this.leftChartData.length; x++) {
if ((this.leftChartData[x].day == addTime) && (this.leftChartData[x].type == '成本')) {
exist = true
if (data[i].incomeAndExpensesTypeName == '收入' || data[i].inAndOutName == '收入') {
this.leftChartData[x].value = Math.round((this.leftChartData[x].value + cost)*100)/100
}
break
}
}
if (!exist) {
if (data[i].incomeAndExpensesTypeName == '收入' || data[i].inAndOutName == '收入') {
this.leftChartData.push({
day: addTime,
type: '成本',
value: cost
})
}
}
// 计算每天的单数
exist = false
for (let x = 0; x < this.leftChartData.length; x++) {
if ((this.leftChartData[x].day == addTime) && (this.leftChartData[x].type == '单数')) {
exist = true
if (data[i].incomeAndExpensesTypeName == '收入' || data[i].inAndOutName == '收入') {
this.leftChartData[x].value += (data[i].goodsCount || data[i].quantity)
}
break
}
}
if (!exist) {
if (data[i].incomeAndExpensesTypeName == '收入' || data[i].inAndOutName == '收入') {
this.leftChartData.push({
day: addTime,
type: '单数',
value: (data[i].goodsCount || data[i].quantity)
})
}
}
}
},
getCost(sku) {
for (let i = 0; i < this.productList.length; i++) {
if (sku == this.productList[i].sku) {
return this.productList[i].costPrice
}
}
return 0
},
sleepSync(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds));
},
async downloadPicture() {
try {
const element = document.getElementById('app-content');
const canvas = await html2canvas(element);
// 创建一个图片元素
const img = new Image();
img.src = canvas.toDataURL('image/png');
// 添加到body中以便下载
document.body.appendChild(img);
// 触发下载
const a = document.createElement('a');
a.style.display = 'none'
a.href = img.src;
a.download = '账单统计(希音).png';
a.click();
document.body.removeChild(img);
} catch (error) {
console.error('Error saving image:', error);
}
}
}
}
</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>

View File

@@ -19,8 +19,7 @@
<!--<el-button type="primary" :disabled="isBegin" @click="isShow = true">添加备货单</el-button>
<el-button type="primary" :disabled="isBegin" @click="loadAll">一键加载全部</el-button>-->
</el-dropdown>
<el-button v-if="!isBegin" type="button" :class="'el-button el-button--primary'" @click="beginCreate">开始创建</el-button>
<el-button v-else type="button" :icon="isBegin? 'el-icon-loading': ''" :class="isBegin ? 'el-button el-button--danger': 'el-button el-button--primary'" @click="beginCreate()">{{ `结束创建(${choosedList.length}/${createTotal})` }}</el-button>
<el-button type="button" :class="'el-button el-button--primary'" @click="beginCreate">开始创建</el-button>
</template>
</ai-search-bar>
@@ -225,12 +224,10 @@
Message.error("请选择待创建备货单")
return
}
console.log(this.arr)
this.arr.map(item => {
let temp = this.choosedList.filter(i => {
return i.subPurchaseOrderSn == item.subPurchaseOrderSn
})
console.log(temp)
if (temp.length == 0) {
this.choosedList.push(item)
console.log(this.choosedList)
@@ -261,20 +258,12 @@
this.loadAll()
}
},
beginCreateConfirm() {
beforeBegin() {
if (this.choosedList.length <= 0) {
Message.error('请先添加备货单');
return;
}
this.createDlgShow = true
},
beforeBegin() {
this.$refs.robForm.validate((valid) => {
if (valid) {
this.createDlgShow = false
this.beginCreate()
}
})
this.beginCreate()
},
getList (data, mallId, mallName, currentPage) {
sendChromeAPIMessage({
@@ -334,74 +323,43 @@
getStyle(url) {
return "background-image: url(" + url + "); width: 72px; height: 72px; cursor: pointer; border-radius: 6px; border: 1px solid rgba(0, 0, 0, 0.14);";
},
beginCreate() {
if (this.isBegin) {
this.isBegin = false;
return;
async beginCreate() {
let waitCreateList = []
for (let i = 0; i < this.mallList.length; i++) {
let subPurchaseOrderSnList = []
this.choosedList.map(item => {
if (item.mallId == this.mallList[i].mallId) {
subPurchaseOrderSnList.push(item.subPurchaseOrderSn)
}
})
if (subPurchaseOrderSnList.length > 0) {
waitCreateList.push({
mallId: this.mallList[i].mallId,
subPurchaseOrderSnList: subPurchaseOrderSnList
})
}
}
this.createTotal = this.choosedList.length
this.isBegin = true;
for (let j = 0; j < waitCreateList.length; j++) {
let res = await sendChromeAPIMessage({
url: 'oms/bg/venom/api/supplier/purchase/manager/batchAcceptSubPurchaseOrder',
needMallId: true,
anti: true,
mallId: waitCreateList[j].mallId,
data: {
subPurchaseOrderSnList: waitCreateList[j].subPurchaseOrderSnList,
confirmFulfilment: false
}})
this.choosedList.map((data, index) => {
let deliverOrderDetailInfos = data.skuQuantityDetailList.map(item => {
if (this.robForm.isModifyMaxNum) {
return {
productSkuId: item.productSkuId,
deliverSkuNum: item.skuDeliveryQuantityMaxLimit
}
} else {
return {
productSkuId: item.productSkuId,
deliverSkuNum: item.productSkuPurchaseQuantity
if (res.success && res.errorCode == 1000000) {
if (res.result.isSuccess) {
for (let n = 0; n < waitCreateList[j].subPurchaseOrderSnList.length; n++) {
this.remove(waitCreateList[j].subPurchaseOrderSnList[n])
}
}
}
})
let packageInfos = []
data.orderDetailVOList.map(item => {
let packageDetailSaveInfos = []
if (this.robForm.isModifyMaxNum) {
packageDetailSaveInfos.push({
productSkuId: item.productSkuId,
skuNum: item.skuDeliveryQuantityMaxLimit
})
} else {
packageDetailSaveInfos.push({
productSkuId: item.productSkuId,
skuNum: item.productSkuPurchaseQuantity
})
}
packageInfos.push({packageDetailSaveInfos: packageDetailSaveInfos})
})
let addressInfo = this.addressList.filter(k => {
return k.mallId == data.mallId
})
let deliveryOrderCreateInfos = []
deliveryOrderCreateInfos.push({
deliverOrderDetailInfos: deliverOrderDetailInfos,
subPurchaseOrderSn: data.subPurchaseOrderSn,
deliveryAddressId: addressInfo[0].addressId,
packageInfos: packageInfos
})
let deliveryOrderCreateGroupList = []
deliveryOrderCreateGroupList.push({
deliveryOrderCreateInfos: deliveryOrderCreateInfos,
receiveAddressInfo: data.receiveAddressInfo,
subWarehouseId: data.subWarehouseId
})
})
let _this = this;
let tt = setInterval(function() {
if (_this.choosedList.length == 0) {
_this.isBegin = false;
clearInterval(tt);
}
}, 1000)
}
},
formatTime(time) {
return timestampToTime(time)

View File

@@ -2054,6 +2054,11 @@ address@^1.1.2:
resolved "https://registry.npmmirror.com/address/-/address-1.2.2.tgz"
integrity sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==
adler-32@~1.3.0:
version "1.3.1"
resolved "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz#1dbf0b36dda0012189a32b3679061932df1821e2"
integrity sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==
aggregate-error@^3.0.0:
version "3.1.0"
resolved "https://registry.npmmirror.com/aggregate-error/-/aggregate-error-3.1.0.tgz"
@@ -2358,6 +2363,11 @@ balanced-match@^1.0.0:
resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base64-arraybuffer@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz"
integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==
base64-js@^1.3.1:
version "1.5.1"
resolved "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz"
@@ -2568,6 +2578,14 @@ center-align@^0.1.1:
align-text "^0.1.3"
lazy-cache "^1.0.3"
cfb@~1.2.1:
version "1.2.2"
resolved "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz#94e687628c700e5155436dac05f74e08df23bc44"
integrity sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==
dependencies:
adler-32 "~1.3.0"
crc-32 "~1.2.0"
chalk@4.1.0:
version "4.1.0"
resolved "https://registry.npmmirror.com/chalk/-/chalk-4.1.0.tgz"
@@ -2751,6 +2769,11 @@ clone@^1.0.2:
resolved "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz"
integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==
codepage@~1.15.0:
version "1.15.0"
resolved "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz#2e00519024b39424ec66eeb3ec07227e692618ab"
integrity sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==
color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz"
@@ -2953,6 +2976,11 @@ cosmiconfig@^7.0.0:
path-type "^4.0.0"
yaml "^1.10.0"
crc-32@~1.2.0, crc-32@~1.2.1:
version "1.2.2"
resolved "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff"
integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==
cross-spawn@^5.0.1:
version "5.1.0"
resolved "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-5.1.0.tgz"
@@ -2997,6 +3025,13 @@ css-declaration-sorter@^6.3.1:
resolved "https://registry.npmmirror.com/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz"
integrity sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==
css-line-break@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz"
integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==
dependencies:
utrie "^1.0.2"
css-loader@^6.5.0:
version "6.8.1"
resolved "https://registry.npmmirror.com/css-loader/-/css-loader-6.8.1.tgz"
@@ -3897,6 +3932,11 @@ file-entry-cache@^6.0.1:
dependencies:
flat-cache "^3.0.4"
file-saver@^2.0.5:
version "2.0.5"
resolved "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38"
integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz"
@@ -3989,6 +4029,11 @@ forwarded@0.2.0:
resolved "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz"
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
frac@~1.1.2:
version "1.1.2"
resolved "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz#3d74f7f6478c88a1b5020306d747dc6313c74d0b"
integrity sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==
fraction.js@^4.2.0:
version "4.2.0"
resolved "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.2.0.tgz"
@@ -4335,6 +4380,14 @@ html-webpack-plugin@^5.1.0:
pretty-error "^4.0.0"
tapable "^2.0.0"
html2canvas@^1.4.1:
version "1.4.1"
resolved "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz"
integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==
dependencies:
css-line-break "^2.1.0"
text-segmentation "^1.0.3"
htmlparser2@^6.1.0:
version "6.1.0"
resolved "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-6.1.0.tgz"
@@ -6788,6 +6841,13 @@ sprintf-js@~1.0.2:
resolved "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz"
integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
ssf@~0.11.2:
version "0.11.2"
resolved "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz#0b99698b237548d088fc43cdf2b70c1a7512c06c"
integrity sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==
dependencies:
frac "~1.1.2"
ssri@^8.0.1:
version "8.0.1"
resolved "https://registry.npmmirror.com/ssri/-/ssri-8.0.1.tgz"
@@ -7060,6 +7120,13 @@ terser@^5.10.0, terser@^5.16.8:
commander "^2.20.0"
source-map-support "~0.5.20"
text-segmentation@^1.0.3:
version "1.0.3"
resolved "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz"
integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==
dependencies:
utrie "^1.0.2"
text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz"
@@ -7341,6 +7408,13 @@ utils-merge@1.0.1:
resolved "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
utrie@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz"
integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==
dependencies:
base64-arraybuffer "^1.0.2"
uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz"
@@ -7722,11 +7796,21 @@ window-size@0.1.0:
resolved "https://registry.npmmirror.com/window-size/-/window-size-0.1.0.tgz"
integrity sha512-1pTPQDKTdd61ozlKGNCjhNRd+KPmgLSGa3mZTHoOliaGcESD8G1PXhh7c1fgiPjVbNVfgy2Faw4BI8/m0cC8Mg==
wmf@~1.0.1:
version "1.0.2"
resolved "https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz#7d19d621071a08c2bdc6b7e688a9c435298cc2da"
integrity sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==
word-wrap@~1.2.3:
version "1.2.5"
resolved "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz"
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
word@~0.3.0:
version "0.3.0"
resolved "https://registry.npmmirror.com/word/-/word-0.3.0.tgz#8542157e4f8e849f4a363a288992d47612db9961"
integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==
wordwrap@0.0.2:
version "0.0.2"
resolved "https://registry.npmmirror.com/wordwrap/-/wordwrap-0.0.2.tgz"
@@ -7764,6 +7848,19 @@ ws@^8.13.0:
resolved "https://registry.npmmirror.com/ws/-/ws-8.13.0.tgz"
integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==
xlsx@^0.18.5:
version "0.18.5"
resolved "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz#16711b9113c848076b8a177022799ad356eba7d0"
integrity sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==
dependencies:
adler-32 "~1.3.0"
cfb "~1.2.1"
codepage "~1.15.0"
crc-32 "~1.2.1"
ssf "~0.11.2"
wmf "~1.0.1"
word "~0.3.0"
y18n@^5.0.5:
version "5.0.8"
resolved "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz"