Merge pull request !6 from juanmao2009/master
This commit is contained in:
juanmao2009
2024-10-19 09:48:10 +00:00
committed by Gitee
21 changed files with 1680 additions and 33 deletions

View File

@@ -55,7 +55,6 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
headers['Content-Type'] = 'application/json';
data = JSON.stringify(request.data)
} else {
console.log(request.data)
const formData = new FormData();
Object.keys(request.data).forEach(key => {
const value = request.data[key]

View File

@@ -33,7 +33,8 @@
"declarativeNetRequest",
"declarativeNetRequestWithHostAccess",
"declarativeNetRequestFeedback",
"activeTab"
"activeTab",
"fileSystemProvider"
],
"declarative_net_request": {
"rule_resources": [

View File

@@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "TEMU助手",
"description": "TEMU助手 - 自动化提高生产效率",
"version": "3.3.0",
"version": "3.3.1",
"background": {
"service_worker": "/background.js"
},
@@ -32,7 +32,8 @@
"declarativeNetRequest",
"declarativeNetRequestWithHostAccess",
"declarativeNetRequestFeedback",
"activeTab"
"activeTab",
"fileSystemProvider"
],
"declarative_net_request": {
"rule_resources": [

View File

@@ -110,6 +110,11 @@ const router = new VueRouter({
name: 'copyProductAliExpress',
component: () => import('../view/product/CopyProductAliExpress.vue')
},
{
path: 'batchUpload',
name: 'batchUpload',
component: () => import('../view/product/BatchUpload.vue')
},
{
path: 'niubiCopy',
name: 'niubiCopy',
@@ -222,6 +227,11 @@ const router = new VueRouter({
name: 'logisticFee',
component: () => import('../view/sale/LogisticFee.vue')
},
{
path: 'billStat',
name: 'billStat',
component: () => import('../view/sale/ExportBillStatTemu.vue')
},
{
path: 'costManageShein',
name: 'costManageShein',
@@ -272,6 +282,12 @@ const router = new VueRouter({
name: 'purchaseOrderListShein',
component: () => import('../view/shein/PurchaseOrderListShein.vue')
},
{
path: 'syncDataTemu',
name: 'syncDataTemu',
component: () => import('../view/data/SyncDataTemu.vue')
},
// {
// path: 'statistics',
// name: 'statistics',
@@ -338,6 +354,14 @@ const router = new VueRouter({
title: '注册'
},
component: () => import('../view/login/Register.vue')
},
{
path: '/forget',
name: 'forget',
meta: {
title: '注册'
},
component: () => import('../view/login/Forget.vue')
}
],
scrollBehavior (to, from, savedPosition) {

View File

@@ -19,8 +19,29 @@ export function getImageMd5(imageUrl) {
});
}
export async function uploadImage(folderId, imageUrl, mallId) {
let res1 = await getImageMd5(imageUrl)
export function getImageMd5Local(file) {
return new Promise((resolve) => {
var reader = new FileReader()
// 读取Blob对象的内容
reader.onloadend = function () {
/*const spark = new SparkMd5.ArrayBuffer()
console.log(reader.result)
spark.append(reader.result);*/
const md5 = SparkMd5.hash(reader.result)
resolve({md5, fileName: file.name});
};
reader.readAsDataURL(file);
})
}
export async function uploadImage(folderId, imageUrl, mallId, local = false) {
let res1
if (local) {
res1 = await getImageMd5Local(imageUrl)
imageUrl = URL.createObjectURL(imageUrl)
} else {
res1 = await getImageMd5(imageUrl)
}
let detailList = []
detailList.push({
materialMd5: res1.md5,
@@ -35,6 +56,7 @@ export async function uploadImage(folderId, imageUrl, mallId) {
createDetailList: detailList,
folderId: folderId
}})
await sleepSync(200)
if (res2.success) {
if (res2.result.responseDetailList[0].alreadyExists) {
@@ -48,6 +70,7 @@ export async function uploadImage(folderId, imageUrl, mallId) {
bucket_tag: "product-material-tag"
}
})
await sleepSync(200)
let res4 = await sendChromeAPIMessage({
url: 'https://file.kuajingmaihuo.com/api/galerie/v3/store_image?sdk_version=js-0.0.16-alpha.0&tag_name=product-material-tag',
@@ -58,6 +81,7 @@ export async function uploadImage(folderId, imageUrl, mallId) {
upload_sign: res3.result.signature
}
})
await sleepSync(200)
let res5 = await sendChromeAPIMessage({
url: 'marvel-mms/cn/api/kiana/gmp/bg/phoenix/api/material/edit',
@@ -76,3 +100,7 @@ export async function uploadImage(folderId, imageUrl, mallId) {
}
}
function sleepSync(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds));
}

View File

@@ -26,7 +26,8 @@ export function transform(leftData) {
propName: leftData.productPropertyList[i].propName,
refPid: leftData.productPropertyList[i].refPid,
vid: leftData.productPropertyList[i].vid,
controlType: 1,
controlType: leftData.productPropertyList[i].controlType || 1,
numberInputValue: leftData.productPropertyList[i].numberInputValue || "",
pid: leftData.productPropertyList[i].pid,
templatePid: leftData.productPropertyList[i].templatePid,
valueExtendInfo: leftData.productPropertyList[i].valueExtendInfo
@@ -65,7 +66,7 @@ export function transform(leftData) {
rightSkcItem.previewImgUrls = leftSkc[i].previewImgUrls;
rightSkcItem.productSkcCarouselImageI18nReqs = leftSkc[i].productSkcCarouselImageI18nVOList;
rightSkcItem.extCode = leftSkc[i].extCode;
rightSkcItem.mainProductSkuSpecReqs = [
rightSkcItem.mainProductSkuSpecReqs = leftSkc[i].mainProductSkuSpec || [
{
"parentSpecId": 0,
"parentSpecName": "",
@@ -83,6 +84,12 @@ export function transform(leftData) {
rightSkuItem.extCode = leftSkuItem.extCode;
rightSkuItem.supplierPrice = leftSkuItem.supplierPrice;
rightSkuItem.currencyType = leftSkuItem.currencyType;
rightSkuItem.productSkuSuggestedPriceReq = leftSkuItem.productSkuSuggestedPrice;
rightSkuItem.productSkuMultiPackReq = leftSkuItem.productSkuMultiPack;
if (rightSkuItem.productSkuMultiPackReq) {
delete rightSkuItem.productSkuMultiPackReq.productSkuNetContent
}
rightSkuItem.productSkuMultiPackReq.productSkuNetContentReq = {}
rightSkuItem.productSkuSpecReqs = leftSkuItem.productSkuSpecList;
productSpecPropertyReqs.push({
"parentSpecId": leftSkuItem.productSkuSpecList[0].parentSpecId,
@@ -127,6 +134,7 @@ export function transform(leftData) {
rightData.materialImgUrl = leftData.materialImgUrl;
rightData.goodsLayerDecorationReqs = leftData.goodsLayerDecorationVOList;
rightData.sizeTemplateIds = !leftData.sizeTemplateIds ? []: leftData.sizeTemplateIds;
rightData.sizeTemplateId = leftData.sizeTemplateId || 0;
rightData.showSizeTemplateIds = !leftData.showSizeTemplateIds ? []: leftData.showSizeTemplateIds;
rightData.goodsModelReqs = !leftData.goodsModelList ? []: leftData.goodsModelList;
rightData.productWhExtAttrReq = {
@@ -139,6 +147,9 @@ export function transform(leftData) {
rightData.productCarouseVideoReqList = leftData.carouseVideoVOList;
rightData.goodsAdvantageLabelTypes = leftData.goodsAdvantageLabelVOList;
rightData.productDetailVideoReqList = leftData.detailVideoVOList;
if (leftData.productSpecPropertyVOS) {
rightData.productSpecPropertyReqs = leftData.productSpecPropertyVOS
}
rightData.productOuterPackageImageReqs = [];
for (let i = 0;i < leftData.outerPackageImages.length; i++) {
rightData.productOuterPackageImageReqs.push({

View File

@@ -13,6 +13,7 @@
</div>
</el-tooltip>
<el-button type="button" :class="'el-button el-button--primary'" @click="sign">签到</el-button>
<!--<el-button type="button" :class="'el-button el-button--primary'" @click="openFolder">打开文件夹</el-button>-->
<el-tooltip class="item" effect="dark" content="用户激活" placement="top">
<div class="left" @click="toActive">
<span>会员信息:</span>
@@ -72,6 +73,7 @@
<el-menu-item index="/productList">商品列表</el-menu-item>
<el-menu-item index="/copyProduct">商品复制</el-menu-item>
<el-menu-item index="/findSeller">查找买手</el-menu-item>
<!--<el-menu-item index="/batchUpload">批量上品</el-menu-item>-->
<el-menu-item v-if="$store.state.userInfo.phone == '18610967550' || $store.state.userInfo.phone == '18571466720'" index="/draft">
草稿箱管理
</el-menu-item>
@@ -138,6 +140,7 @@
<el-menu-item index="/afterSaleDeductStat">售后赔付统计</el-menu-item>
<el-menu-item index="/priceAdjustment">调价管理</el-menu-item>
<el-menu-item index="/logisticFee">物流统计</el-menu-item>
<el-menu-item index="/billStat">账务明细统计</el-menu-item>
</el-submenu>
<el-submenu index="/shein">
<template slot="title">
@@ -159,6 +162,13 @@
</el-menu-item>
<el-menu-item index="/saleStatShein">商家账单统计</el-menu-item>
</el-submenu>
<el-submenu index="/dataManager" v-if="$store.state.userInfo.phone == '18571466720'">
<template slot="title">
<i class="el-icon-s-data"></i>
<span slot="title">数据管理</span>
</template>
<el-menu-item index="/syncDataTemu">数据同步(TEMU)</el-menu-item>
</el-submenu>
<el-menu-item index="/info">
<i class="el-icon-info"></i>
<span slot="title">弹窗消息</span>
@@ -359,6 +369,30 @@ export default {
}
})
},
async openFolder() {
console.log(22)
let fileList = []
const res = await window.showDirectoryPicker({})
const detalAction = async (obj) => {
if (obj.entries) {
const dirs = obj.entries()
for await (const entry of dirs) {
if (entry[1].entries) {
detalAction(entry[1])
} else {
fileList.push({
name: entry[0],
path: obj.name,
fileHandle: entry[1],
file: await entry[1].getFile()
})
}
}
}
}
await detalAction(res);
console.log("--fileList--", fileList)
},
gotoKefu() {
window.open('https://work.weixin.qq.com/kfid/kfcaa4208f661131eba', '_blank')
},

View File

@@ -232,6 +232,11 @@ import BiVueMindmap from "bi-vue-mindmap";
parentId: "01010104",
title: "物流统计:对应“履约服务账单->明细->缴费记录”,以及“发货单列表->物流计费重核实”,统计一段时期内物流费用,以及货物重量,物流费用分布"
},
{
id: "0101010408",
parentId: "01010104",
title: "账务明细统计:对应“账户资金->对账中心->账务明细”,真实统计一段时间内的实际收入,以及各种类型的支出,汇总统计"
},
{
id: "01010105",
parentId: "010101",

View File

@@ -0,0 +1,359 @@
<template>
<ai-list class="list" v-loading="isLoading" element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading">
<ai-title
slot="title"
title="数据同步(TEMU)"
isShowBottomBorder>
</ai-title>
<template slot="content">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>店铺列表</span>
</div>
<div>
<el-checkbox :indeterminate="isMallIndeterminate" v-model="checkAllMall" @change="handleCheckAllMallChange">全选</el-checkbox>
<div style="margin: 15px 0;"></div>
<el-checkbox-group v-model="checkMallList" @change="handleCheckedMallChange">
<el-checkbox v-for="mall in $store.state.mallList" style="width: 300px; margin-right: 0px" :label="mall.mallId" :key="mall.mallId">{{mall.isSemiManagedMall ? mall.mallName + '(半托管)': mall.mallName}}</el-checkbox>
</el-checkbox-group>
</div>
</el-card>
<el-card class="box-card" style="margin-top: 5px;">
<div slot="header" class="clearfix">
<span>同步操作</span>
</div>
<div>
<div>
<label style="width:90px">时间范围</label>
<el-date-picker
v-model="dateRange"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
:picker-options="pickerOptions">
</el-date-picker>
</div>
<br>
<el-checkbox :indeterminate="isOptionIndeterminate" v-model="checkAllOptions" @change="handleCheckAllOptionChange">全选</el-checkbox>
<div style="margin: 15px 0;"></div>
<el-checkbox-group v-model="checkOptionList" @change="handleCheckedOptionChange">
<el-checkbox v-for="option in options" style="width: 300px; margin-right: 0px" :label="option.value" :key="option.value">{{option.label}}</el-checkbox>
</el-checkbox-group>
</div>
</el-card>
<div style="display: grid; grid-template-columns: 1fr; gap: 16px; margin-top: 10px;">
<el-button type="button" :class="'el-button el-button--primary'" @click="sync()">同步</el-button>
<!--<el-button type="button" :class="'el-button el-button--primary'" @click="toSyncWaitDeliveryOrder()">同步待仓库收货发货单</el-button>
<el-button type="button" :class="'el-button el-button--primary'" @click="toSyncDeliveryOrder()">同步历史发货单</el-button>
<el-button type="button" :class="'el-button el-button--primary'" @click="toBeginCollectResource()">采集资源位</el-button>-->
</div>
<el-card class="box-card" style="margin-top: 5px;">
<div slot="header" class="clearfix">
<span>日志输出</span>
</div>
<div id="log-container" v-html="logsContent" style="height: 500px; background-color: black; overflow-y: scroll; border: 1px solid #000; padding: 5px; color: white"></div>
</el-card>
</template>
</ai-list>
</template>
<script>
import { Message } from 'element-ui'
import {sendChromeAPIMessage} from '@/api/chromeApi'
import {timestampToTime} from '@/utils/date'
export default {
name: 'SyncDataTemu',
data () {
return {
isLoading: false,
categoryList: [],
checkAllMall: false,
checkAllOptions: false,
checkMallList: [],
checkOptionList: [],
isMallIndeterminate: false,
isOptionIndeterminate: false,
mallList: [],
currentIndex: 0,
pageSize: 100,
pageNo: 1,
syncProductData: [],
deliveryOrderList: [],
currentWaitDeliveryIndex: 0,
waitDeliveryData: [],
options: [
{label: '同步已上架商品列表', value: 0},
{label: '同步今日待收货发货单', value: 1},
{label: '同步今日销售数据', value: 2},
{label: '同步SKU历史销售数据', value: 3}
],
dateRange: [new Date(), new Date()],
pickerOptions: {
disabledDate(time) {
return time.getTime() > Date.now()
}
},
logsList: [],
reqListData: []
}
},
created() {
},
computed: {
logsContent() {
return this.logsList.join('<br>')
}
},
methods: {
handleCheckAllMallChange(val) {
if (val) {
this.checkMallList = this.mallList.map(item => {
return item.mallId
})
} else {
this.checkMallList = []
}
this.isMallIndeterminate = false;
},
handleCheckedMallChange(value) {
let checkedCount = value.length;
this.checkAllMall = checkedCount === this.mallList.length;
this.isMallIndeterminate = checkedCount > 0 && checkedCount < this.mallList.length;
},
handleCheckAllOptionChange(val) {
if (val) {
this.checkOptionList = this.options.map(item => {
return item.value
})
} else {
this.checkOptionList = []
}
this.isOptionIndeterminate = false;
},
handleCheckedOptionChange(value) {
let checkedCount = value.length;
this.checkAllOptions = checkedCount === this.options.length;
this.isOptionIndeterminate = checkedCount > 0 && checkedCount < this.options.length;
},
async sync() {
if (this.checkMallList.length == 0) {
Message.error("请选择要同步的店铺")
return
}
if (this.checkOptionList.length == 0) {
Message.error("请选择要同步的操作")
return
}
this.isLoading = true
for (let i = 0; i < this.checkOptionList.length; i++) {
this.pageNo = 1
if (this.checkOptionList[i] == 0) { // 同步商品
for (let j = 0; j < this.checkMallList.length; j++) {
let mallInfo = this.$store.state.mallList.filter(item => {
return item.mallId == this.checkMallList[j]
})
let mallName = mallInfo[0].mallName
this.syncProductData = []
await this.syncProduct(this.checkMallList[j], mallName)
}
}
if (this.checkOptionList[i] == 1) { // 同步待发货发货单
for (let j = 0; j < this.checkMallList.length; j++) {
let mallInfo = this.$store.state.mallList.filter(item => {
return item.mallId == this.checkMallList[j]
})
let mallName = mallInfo[0].mallName
this.deliveryOrderList = []
await this.syncDeliveryOrder(this.checkMallList[i], mallName)
}
}
if (this.checkOptionList[i] == 2) { // 同步今日销售数据
for (let j = 0; j < this.checkMallList.length; j++) {
this.reqListData = []
await this.syncTodaySaleData(this.checkMallList[i])
}
}
}
this.isLoading = false
},
async syncProduct(mallId, mallName) {
let res = await sendChromeAPIMessage({url: 'bg-visage-mms/product/skc/pageQuery',
anti:true,
needMallId: true,
mallId: mallId,
data: {
pageSize: this.pageSize,
page: this.pageNo,
skcSiteStatus: 1
}
})
if (res.errorCode == 1000000) {
res.result.pageItems.map(item => {
let temp = {
mallId: mallId,
mallName: mallName,
productName: item.productName,
spu: item.productId,
skc: item.productSkcId,
skcCode: item.extCode,
createTime: timestampToTime(item.createdAt)
}
item.productSkuSummaries.map(item1 => {
let temp1 = {
...temp,
skuCode: item1.extCode,
sku: item1.productSkuId,
mainPic: item1.thumbUrl,
price: item1.supplierPrice /100
}
let specArr = item1.productSkuSpecList.map(item2 => {
return item2.specName
})
temp1.skuSpec = specArr.join(',')
this.syncProductData.push(temp1)
})
})
if (res.result.pageItems.length == this.pageSize) {
this.pageNo ++
await this.syncProduct(mallId, mallName)
} else {
this.$http.post('/api/stock/product/addBatch', this.syncProductData
).then(res => {
if (res.code == 0) {
this.logs(mallName, '已上架商品', true)
} else {
this.logs(mallName, '已上架商品', false)
}
})
}
} else {
this.logs(mallName, '已上架商品', false)
}
},
async syncDeliveryOrder(mallId, mallName) {
let now = new Date()
now.setHours(0, 0, 0, 0)
let start = now.getTime()
let end = start + 86400*1000 - 1000
let res = await sendChromeAPIMessage({url: 'bgSongbird-api/supplier/deliverGoods/management/pageQueryDeliveryBatch',
anti:true,
needMallId: true,
mallId: mallId,
data: {
pageNo: this.pageNo,
pageSize: this.pageSize,
productLabelCodeStyle: 0,
onlyTaxWarehouseWaitApply: false,
deliverTimeFrom: start,
deliverTimeTo: end,
status: 1
}
})
if (res.errorCode == 1000000) {
res.result.list.map(item => {
let temp = {
mallId: mallId,
mallName: mallName,
subWarehouseName: item.subWarehouseName,
expressCompany: item.expressCompany,
deliveryOrderSn: item.deliveryOrderSn
}
item.deliveryOrderList.map(item1 => {
temp = {
...temp,
productName: item1.subPurchaseOrderBasicVO.productName,
skc: item1.productSkcId,
skcCode: item1.skcExtCode,
isFirst: item1.subPurchaseOrderBasicVO.isFirst ? 1: 0
}
item1.packageDetailList.map(item2 => {
temp = {
...temp,
sku: item2.productSkuId,
num: item2.skuNum
}
})
this.deliveryOrderList.push(temp)
})
})
if (res.result.list.length == this.pageSize) {
this.pageNo ++
await this.syncDeliveryOrder(mallId, mallName)
} else {
this.$http.post('/api/stock/orderInfo/addBatch', this.deliveryOrderList
).then(res => {
if (res.code == 0) {
this.logs(mallName, '今日待收货发货单', true)
} else {
this.logs(mallName, '今日待收货发货单', false)
}
})
}
} else {
this.logs(mallName, '今日待收货发货单', false)
}
},
async syncTodaySaleData(mallId) {
let res = await sendChromeAPIMessage({
url: 'marvel-mms/cn/api/kiana/venom/sales/management/list',
needMallId: true,
mallId: mallId,
data: {
pageNo: this.pageNo,
pageSize: this.pageSize,
isLack: 0,
priceAdjustRecentDays: 7
}})
if (res.errorCode == 1000000) {
for(let i = 0;i < res.result.subOrderList.length; i++) {
let item = res.result.subOrderList[i]
for(let j = 0;j < item.skuQuantityDetailList.length; j++) {
this.reqListData.push({
mallId: mallId,
sku: item.skuQuantityDetailList[j].productSkuId,
saleNum: item.skuQuantityDetailList[j].todaySaleVolume,
salePrice: item.skuQuantityDetailList[j].supplierPrice / 100
})
}
}
if (this.pageSize == res.result.subOrderList.length) {
this.pageNo ++
await this.syncTodaySaleData(mallId)
} else {
await this.$http.post('http://192.168.1.199:8080/jeecg-boot/eshop/saleManage/addBatch', this.reqListData)
}
}
},
logs(mallName, subject, flag) {
if (flag) {
this.logsList.unshift(`${mallName}${subject}<b style='color: green'>同步成功</b>`)
} else {
this.logsList.unshift(`${mallName}${subject}<b style='color: red'>同步失败</b>`)
}
}
}
}
</script>
<style scoped lang="scss">
</style>

380
src/view/login/Forget.vue Normal file
View File

@@ -0,0 +1,380 @@
<template>
<div class="login">
<div class="body">
<div class="middle">
<div class="right">
<div class="tab">
<h2 class="active" @click="currIndex = 0">找回密码</h2>
</div>
<el-form :model="form" label-position="top" ref="form" label-width="100px" class="form">
<el-form-item
prop="phone"
:rules="[{ required: true, message: '请输入手机号', trigger: 'blur' }, { validator: phoneReg, trigger: 'blur' }]">
<el-input maxlength="11" placeholder="请输入手机号" v-model="form.phone"></el-input>
</el-form-item>
<el-form-item
prop="password"
:rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]">
<el-input placeholder="请输入密码" type="password" v-model="form.password"></el-input>
</el-form-item>
<el-form-item
prop="repassword"
:rules="[{ required: true, message: '请再次输入密码', trigger: 'blur' }]">
<el-input placeholder="请再次输入密码" type="password" v-model="form.repassword"></el-input>
</el-form-item>
<el-form-item label="" prop="code" :rules="[{ required: true, message: '请输入验证码', trigger: 'blur' }]">
<div class="code-item">
<el-input style="width: 300px;" maxlength="4" placeholder="请输入验证码" v-model="form.code"></el-input>
<span @click="getCode" :loading="btnLoading">{{ isStart ? time + ' S' : '发送验证码' }}</span>
</div>
</el-form-item>
<el-button type="primary" style="width: 100%" @click="login" :loading="btnLoading">重置设置</el-button>
</el-form>
<div class="login-footer">
<div class="left">
<i class="hover" @click="$router.back()">返回登录</i>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import CryptoJS from 'crypto-js'
export default {
data () {
return {
form: {
password: '',
repassword: '',
code: '',
phone: ''
},
phoneReg: (rule, value, callback) => {
if (/^1[0-9]{10,10}$/.test(value)) {
return callback()
}
callback(new Error('手机号格式错误'))
},
timer: null,
time: 60,
isSend: false,
isStart: false,
isLoading: false,
currIndex: 0,
btnLoading: false,
loginType: '0'
}
},
mounted () {
},
methods: {
login () {
this.$refs.form.validate((valid) => {
if (valid) {
if (this.form.password != this.form.repassword) {
this.$message.success('两次密码输入不一致');
return;
}
this.btnLoading = true
this.$http.post(`/api/malluser/forget`, null, {
params: {
...this.form
}
}, {
headers: {
Authorization: 'Basic cGM6cGM='
}
}).then(res => {
if (res.code === 0) {
this.$message.success('注册成功')
setTimeout(() => {
this.$router.replace('/login')
}, 500)
}
this.btnLoading = false
}).catch(() => {
this.btnLoading = false
})
} else {
return false;
}
})
},
encryptPhone (phone) {
const u = navigator.userAgent
const isIos = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)
var key = 'thanks,temulll11'
var iv = CryptoJS.enc.Latin1.parse(key)
var encrypted = CryptoJS.AES.encrypt(phone, iv, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.ZeroPadding
})
if (isIos) {
return encodeURIComponent(encrypted.toString())
} else {
return encrypted.toString()
}
},
getCode () {
if (this.isSend) {
return this.$message.error('验证码已发送')
}
this.$refs.form.validateField('phone', e => {
if (!e) {
let phone = this.encryptPhone(this.form.phone)
this.isSend = true
this.btnLoading = true
this.$http.post(`/api/sms/getRegSmsCodeNew?phone=${phone}`, {}, {
withoutToken: true
}).then(res => {
if (res.code === 0) {
this.$message.success('验证码发送成功')
this.isStart = true
this.timer = setInterval(() => {
if (this.time === 0) {
this.isSend = false
this.isStart = false
this.time = 60
clearInterval(this.timer)
return false
}
this.time = this.time - 1
}, 1000)
}
this.btnLoading = false
this.isSend = false
}).catch(() => {
this.isSend = false
this.btnLoading = false
})
}
})
},
onChange (e) {
this.$emit('change', e)
}
}
}
</script>
<style lang="scss" scoped>
.login {
display: flex;
flex-direction: column;
height: 100vh;
.body {
position: relative;
flex: 1;
width: 100%;
background-color: #cdc8c8;
// background: url(../../assets/images/login/login.png) no-repeat;
background-size: 100% 100%;
.middle {
position: absolute;
display: flex;
justify-content: flex-end;
top: 50%;
left: 50%;
z-index: 11;
width: 1280px;
transform: translate(-50%, -50%);
& > .left {
padding-top: 42px;
font-family: MicrosoftYaHei;
p {
line-height: 24px;
margin-top: 12px;
margin-bottom: 13px;
font-size: 18px;
color: #fff;
}
span {
color: #fff;
}
}
& > .right {
width: 500px;
padding: 63px 40px 42px;
box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.06);
background: #fff;
border-radius: 16px;
backdrop-filter: blur(6px);
overflow: hidden;
.tab {
display: flex;
position: relative;
align-items: center;
margin-bottom: 36px;
padding-bottom: 17px;
border-bottom: 1px solid #DCDFE6;
&::after {
position: absolute;
bottom: -1px;
left: 0;
z-index: 1;
width: 81px;
height: 2px;
background: #1FBAD6;
content: ' ';
transition: all ease-in-out 0.3s;
}
&.tab-active:after {
transform: translateX(138px);
}
h2 {
width: 81px;
line-height: 24px;
text-align: center;
font-size: 20px;
font-family: SJsuqian;
color: #000000;
cursor: pointer;
user-select: none;
transition: all ease 0.4s;
&.active, &:hover {
color: #1FBAD6;
}
&:first-child {
margin-right: 58px;
}
}
}
.login-type {
font-size: 14px;
font-family: SJsuqian;
text-align: center;
color: #54657D;
cursor: pointer;
transition: all ease-in-out 0.4s;
&:hover {
color: #1FBAD6;
}
}
.login-footer {
display: flex;
align-items: center;
justify-content: center;
margin-top: 21px;
margin-bottom: 38px;
div {
display: flex;
align-items: center;
span {
font-family: SJsuqian;
color: #54657D;
}
i {
font-family: SJsuqian;
color: #1FBAD6;
}
}
}
}
}
::v-deep .el-form {
.el-input__inner {
height: 48px;
border-color: #DCDFE6;
background-color: transparent;
&:focus, &:hover {
border-color: #1FBAD6;
}
&::placeholder {
color: #666666;
}
}
.el-form-item.is-error .el-input__inner, .el-form-item.is-error .el-input__inner:focus, .el-form-item.is-error .el-textarea__inner, .el-form-item.is-error .el-textarea__inner:focus {
border-color: #F56C6C;
}
.el-form-item {
margin-bottom: 20px;
}
.el-button {
height: 48px;
margin-top: 28px;
font-size: 16px;
}
.code-item {
display: flex;
align-items: center;
justify-content: space-between;
span {
width: 110px;
height: 48px;
line-height: 48px;
text-align: center;
font-size: 14px;
color: #333;
cursor: pointer;
user-select: none;
border-radius: 4px;
transition: all cubic-bezier(0.215, 0.61, 0.355, 1) 0.3s;
border: 1px solid #DCDFE6;
&:hover {
border-color: #1FBAD6;
}
}
.el-form-item {
width: 300px;
}
}
}
.copyright {
position: fixed;
bottom: 24px;
left: 50%;
font-size: 14px;
font-weight: 400;
color: #fff;
letter-spacing: 1px;
transform: translateX(-50%);
}
}
}
</style>

View File

@@ -24,9 +24,16 @@
<span>没有账号</span>
<i class="hover" @click="$router.push('/register')">立即注册</i>
</div>
<div class="left" style="margin-left: 10px;">
<span>忘记密码</span>
<i class="hover" @click="$router.push('/forget')">找回密码</i>
</div>
</div>
</div>
</div>
<div id="kefu" @click="gotoKefu">
<label slot="reference" class="topBtn" title="联系客服"></label>
</div>
</div>
</div>
</template>
@@ -139,6 +146,9 @@
onChange (e) {
this.$emit('change', e)
},
gotoKefu() {
window.open('https://work.weixin.qq.com/kfid/kfcaa4208f661131eba', '_blank')
}
}
}
@@ -348,4 +358,30 @@
}
}
}
#kefu {
position: fixed;
right: 20px;
bottom: 40px;
z-index: 999;
width: 60px;
height: 60px;
}
#kefu .topBtn {
width: 60px;
height: 60px;
background-color: #fff;
position: absolute;
left: 0;
top: 0;
border-radius: 50%;
cursor: pointer;
background-position: center center;
background-repeat: no-repeat;
background-size: 40px 40px;
-webkit-animation: wobble 250ms infinite;
animation: wobble 250ms infinite;
background-image: url('data:image/svg+xml;%20charset=utf8,%3Csvg%20t%3D%221575450105478%22%20class%3D%22icon%22%20viewBox%3D%220%200%201220%201024%22%20version%3D%221.1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20p-id%3D%222883%22%20width%3D%2248%22%20height%3D%2248%22%3E%3Cpath%20d%3D%22M609.524%20103.522c-222.89%200-403.712%20178.472-403.712%20398.78%200%20220.31%20180.823%20398.782%20403.712%20398.782%20222.889%200%20403.712-178.473%20403.712-398.781%200-220.309-180.823-398.781-403.712-398.781v48.762c196.1%200%20354.95%20156.785%20354.95%20350.019s-158.85%20350.019-354.95%20350.019-354.95-156.785-354.95-350.02c0-193.233%20158.85-350.018%20354.95-350.018v-48.762z%22%20fill%3D%22%231296db%22%20p-id%3D%222884%22%3E%3C%2Fpath%3E%3Cpath%20d%3D%22M786.578%20916.34c166.45-69.217%20278.408-231.055%20278.408-414.035%200-248.026-203.847-449.219-455.457-449.219-251.619%200-455.457%20201.188-455.457%20449.22%200%2055.397%2010.152%20109.367%2029.718%20159.975%204.855%2012.56-1.39%2026.677-13.949%2031.533-12.56%204.855-26.677-1.39-31.532-13.949a490.396%20490.396%200%200%201-3.042-8.078c-1.85%200.077-3.711%200.116-5.581%200.116C58.06%20671.903%200%20614.597%200%20543.903c0-65.005%2049.09-118.69%20112.68-126.91C153.65%20182.56%20360.56%204.324%20609.528%204.324c248.962%200%20455.877%20178.24%20496.85%20412.67%2063.583%208.225%20112.669%2061.907%20112.669%20126.909%200%2070.694-58.06%20128-129.686%20128-1.89%200-3.771-0.04-5.642-0.119-47.536%20129.702-148.34%20235.841-279.493%20290.027-1.161%2033.464-29.012%2060.24-63.2%2060.24-34.925%200-63.237-27.944-63.237-62.416%200-34.471%2028.312-62.415%2063.237-62.415%2017.892%200%2034.048%207.333%2045.551%2019.12z%22%20fill%3D%22%231296db%22%20p-id%3D%222885%22%3E%3C%2Fpath%3E%3Cpath%20d%3D%22M609.528%20611.405c-58.933%200-112.056-10.644-158.472-28.342-16.123-6.147-30.211-12.702-42.138-19.208-6.926-3.777-11.447-6.59-13.437-7.972-19.24-13.373-44.428%205.446-37.059%2027.688%2035.296%20106.527%20136.054%20179.913%20251.106%20179.913%20115.05%200%20215.796-73.384%20251.092-179.913%207.37-22.243-17.82-41.062-37.06-27.687-1.99%201.383-6.51%204.195-13.434%207.972-11.926%206.505-26.012%2013.06-42.133%2019.207-46.413%2017.698-99.533%2028.342-158.465%2028.342z%22%20fill%3D%22%231296db%22%20p-id%3D%222886%22%3E%3C%2Fpath%3E%3C%2Fsvg%3E');
}
</style>

View File

@@ -0,0 +1,305 @@
<template>
<ai-list class="list" v-loading="isLoading" element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading">
<ai-title
slot="title"
title="批量上品"
:tips="`上传一件商品将消耗${coin}金币`"
isShowBottomBorder>
</ai-title>
<template slot="content">
<el-form :model="form" ref="form" label-width="180px" class="form">
<el-divider content-position="left">模板来源</el-divider>
<el-form-item label="模板店铺来源" style="width: 100%;" prop="sourceMallId" :rules="[{ required: true, message: '请选择模板店铺来源', trigger: 'blur' }]">
<el-select style="width: 380px" v-model="form.sourceMallId" placeholder="请选择模板店铺来源">
<el-option
v-for="item in mallList"
:key="item.mallId"
:label="item.mallName"
:value="item.mallId">
</el-option>
</el-select>
</el-form-item>
<el-form-item
prop="sourceSpuId"
label="模板SPU ID"
style="width: 480px"
:rules="[{ required: true, message: '请输入模板SPU ID', trigger: 'blur' }]">
<el-input size="small" placeholder="请输入模板SPU ID" v-model="form.sourceSpuId"></el-input>
</el-form-item>
<el-divider content-position="left">目标店铺</el-divider>
<el-form-item label="店铺" style="width: 100%;" prop="targetMallId" :rules="[{ required: true, message: '请选择目标店铺', trigger: 'blur' }]">
<el-select style="width: 380px" v-model="form.targetMallId" placeholder="请选择目标店铺">
<el-option
v-for="item in mallList"
:key="item.mallId"
:label="item.mallName"
:value="item.mallId">
</el-option>
</el-select>
</el-form-item>
<el-button type="button" :class="'el-button el-button--primary'" @click="openFolder">选择文件夹</el-button>
</el-form>
</template>
</ai-list>
</template>
<script>
import { Message } from 'element-ui'
import {sendChromeAPIMessage} from '@/api/chromeApi'
import {timestampToTime} from '@/utils/date'
import {transform} from '@/utils/product'
import { createFolderApi } from "@/utils/folder.js"
import { uploadImage } from "@/utils/image.js"
import { formatDate } from "@/utils/date.js"
export default {
name: 'BatchUpload',
data () {
return {
form: {
sourceMallId: '',
sourceSpuId: '',
targetMallId: ''
},
content: null,
mallObj: null,
folderId: null,
successCount: 0,
mainPicList: [],
mainPicUrl: null,
detailPicList: [],
coin: 150,
colConfigs: [
{ slot: 'productName', label: '商品名称', width: '180px', align: 'left', fixed: 'left' },
{ prop: 'category', 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: 'productSkuId', label: 'SKU ID', width: '120px', align: 'left' },
{ prop: 'skcSiteStatus', label: '是否在售', width: '120px', align: 'left' },
{ 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' }
],
isLoading: false,
tableData: [],
currentIndex: 0
}
},
computed: {
mallList () {
const filteredData = this.$store.state.mallList.filter(item => {
return item.isSemiManagedMall == false
})
return filteredData
}
},
created () {
},
methods: {
async openFolder() {
this.$refs.form.validate(async (valid) => {
if (valid) {
let res1 = await sendChromeAPIMessage({
url: 'bg-visage-mms/product/query',
needMallId: true,
mallId: this.form.sourceMallId,
data: {
productEditTaskUid: '',
productId: this.form.sourceSpuId
}})
if (res1.errorCode == 1000000) {
this.content = JSON.parse(transform(res1.result))
this.mallObj = this.$store.state.mallList.filter(item => {
return item.mallId == this.form.targetMallId
})
}
this.successCount = 0
this.mainPicList = []
this.detailPicList = []
this.folderId = await createFolderApi(formatDate(new Date()).split('-'), this.form.targetMallId)
const res = await window.showDirectoryPicker({})
await this.dealAction("", res)
}
})
},
async dealAction (root, obj) {
console.log(obj)
if (obj.entries) {
let dirs = obj.entries()
if (root == "") {
let length = 0
for await (const temp of dirs) {
length ++
}
if (this.$store.state.userInfo.coin < length * this.coin) {
Message.error("金币不足,请先充值金币")
return
}
}
if (root == '') {
dirs = obj.entries()
for await (const entry of dirs) {
/*if (entry[1].kind == 'file') {
let prefix = entry[0].split('.').pop()
if (prefix == 'png' || prefix == 'jpg' || prefix == 'jpeg') {
let file = await entry[1].getFile()
let img = await uploadImage(this.folderId, file, this.form.targetMallId, true)
this.mainPicUrl = img
this.$sleepSync(500)
}
} else {*/
if (entry[1].kind == 'directory') {
await this.dealAction(obj.name + "/" + entry[0], entry[1])
}
//}
}
} else {
this.mainPicList = []
this.detailPicList = []
await this.toUploadProduct(root, obj)
}
}
},
async toUploadProduct(root, obj) {
const dirs = obj.entries()
for await (const entry of dirs) {
if (entry[1].kind == 'file') {
let prefix = entry[0].split('.').pop()
if (prefix == 'png' || prefix == 'jpg' || prefix == 'jpeg') {
let file = await entry[1].getFile()
let img = await uploadImage(this.folderId, file, this.form.targetMallId, true)
this.mainPicUrl = img
this.$sleepSync(200)
}
} else if (entry[0] != '详情图' && entry[1].kind == 'directory') {
let tempList = await this.getPictureList(root + '/' + entry[0], entry[1])
this.mainPicList.push({color: entry[0], imgList: tempList})
} else if (entry[0] == '详情图' && entry[1].kind == 'directory') {
this.detailPicList = await this.getPictureList(root + '/' + entry[0], entry[1])
}
}
if (this.mainPicList.length >= 0 && this.detailPicList.length >= 0) {
await this.beginAddToDraft(obj.name)
this.$sleepSync(200)
}
},
async getPictureList(root, obj) {
const dirs = obj.entries()
let images = []
for await (const entry of dirs) {
if (entry[1].kind == 'file') {
let prefix = entry[0].split('.').pop()
if (prefix == 'png' || prefix == 'jpg' || prefix == 'jpeg') {
let file = await entry[1].getFile()
let img = await uploadImage(this.folderId, file, this.form.targetMallId, true)
if (null == img) {
} else {
images.push({
name: entry[0].split('.')[0],
url: img
})
}
}
}
}
images.sort((a, b) => {
if (!isNaN(a.name) && !isNaN(b.name)) {
return (Number(a.name) - Number(b.name))
} else {
return a.name - b.name
}
})
return images
},
async beginAddToDraft(name) {
let category = null
let i = 1
while(true) {
if (!this.content['cat'+i+'Id']) {
break
}
i++
}
category = this.content['cat'+(i-1)+'Id']
let data = {catId: category}
this.content.personalizationSwitch = this.content.personalizationSwitch || 0
let res = await sendChromeAPIMessage({
url: 'bg-visage-mms/product/draft/add',
needMallId: true,
mallId: this.form.targetMallId,
data: data})
if (res.errorCode == 1000000) {
let draftId = res.result.productDraftId
this.content.productDraftId = draftId
// 变化部分
this.content.productName = name
this.content.materialImgUrl = this.mainPicList[0].imgList[0].url
for (let i = 0; i < this.content.productSkcReqs.length; i++) {
for (let j = 0; j < this.mainPicList.length; j++) {
if (this.content.productSkcReqs[i].mainProductSkuSpecReqs[0].specName == this.mainPicList[j].color) {
this.content.productSkcReqs[i].previewImgUrls = this.mainPicList[j].imgList
break
}
}
}
let res1 = await sendChromeAPIMessage({
url: 'bg-visage-mms/product/draft/save',
needMallId: true,
mallId: this.form.targetMallId,
data: {
...this.content
}})
if (res1.errorCode == 1000000) {
Message.success("商品【" + name + "】成功添加到草稿箱")
} else {
Message.error(res1.errorMsg)
}
} else {
Message.error("【拼多多】" + res.errorMsg)
}
await this.$sleepSync(500)
}
}
}
</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

@@ -295,14 +295,18 @@ import { saveAs } from 'file-saver'
this.costDlgShow = true
},
importConfirm() {
this.isLoading = true
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.isLoading = false
this.$message.success('导入成功')
this.getList()
} else {
this.isLoading = false
}
})
})

View File

@@ -0,0 +1,428 @@
<template>
<ai-list class="list" v-loading="isLoading" element-loading-text="拼命加载中" element-loading-spinner="el-icon-loading">
<ai-title
slot="title"
title="账务明细统计"
tips="数据来源于“账户资金->对账中心->账务明细”"
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">
</el-date-picker>
</div>
<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>
<el-button type="button" :class="'el-button el-button--primary'" @click="toBeginStat">开始统计</el-button>
<el-button type="primary" icon="el-icon-download" @click="downloadPicture">下载图片</el-button>
</div>
</template>
</ai-title>
<template slot="content">
<div id="app-content">
<ai-card title="数据概览" style="padding-bottom: 40px;">
<el-divider content-position="center">收入概览</el-divider>
<div>
<el-row :gutter="20">
<el-col :span="4">
<div>
<el-card shadow="always" class="amountItem">
<el-statistic
group-separator=","
:precision="2"
:value-style="{color: 'red'}"
:value="transIncome + nonSllerDuty"
title="总收入"
>
</el-statistic>
</el-card>
</div>
</el-col>
<el-col :span="4">
<div>
<el-card shadow="always" class="amountItem">
<el-statistic
group-separator=","
:precision="2"
:value="transIncome"
title="交易收入"
>
</el-statistic>
</el-card>
</div>
</el-col>
<el-col :span="4">
<div>
<el-card shadow="always" class="amountItem">
<el-statistic
group-separator=","
:value="nonSllerDuty"
:precision="2"
title="非商责平台售后补贴金额"
>
</el-statistic>
</el-card>
</div>
</el-col>
</el-row>
</div>
<el-divider content-position="center">支出概览</el-divider>
<div>
<el-row>
<el-col :span="4">
<div>
<el-card shadow="always" class="amountItem">
<el-statistic
group-separator=","
:precision="2"
:value-style="{color: 'red'}"
:value="logisticAmount+customerOutcome+afterSaleAmount+eprAmount+withdrawAmount"
title="总支出"
></el-statistic>
</el-card>
</div>
</el-col>
<el-col :span="4">
<div>
<el-card shadow="always" class="amountItem">
<el-statistic
group-separator=","
:precision="2"
:value="logisticAmount"
title="仓储综合服务费"
></el-statistic>
</el-card>
</div>
</el-col>
<el-col :span="4">
<div>
<el-card shadow="always" class="amountItem">
<el-statistic
group-separator=","
:precision="2"
:value="customerOutcome"
title="消费者退款金额"
></el-statistic>
</el-card>
</div>
</el-col>
<el-col :span="4">
<div>
<el-card shadow="always" class="amountItem">
<el-statistic
group-separator=","
:precision="2"
:value="afterSaleAmount"
title="消费者及履约保障-售后问题"
>
</el-statistic>
</el-card>
</div>
</el-col>
<el-col :span="4">
<div>
<el-card shadow="always" class="amountItem">
<el-statistic
group-separator=","
:precision="2"
:value="eprAmount"
title="合规EPR物流包装环保费"
>
</el-statistic>
</el-card>
</div>
</el-col>
<el-col :span="4">
<div>
<el-card shadow="always" class="amountItem">
<el-statistic
group-separator=","
:precision="2"
:value="withdrawAmount"
title="提现"
>
</el-statistic>
</el-card>
</div>
</el-col>
</el-row>
</div>
</ai-card>
</div>
<ai-dialog
title="提示"
:visible.sync="isTipDlgShow"
:close-on-click-modal="false"
width="790px"
customFooter
@close="isDlgShow = false">
<div style="display: block;height: 30px;">卖家中心出现图形验证码请前往卖家中心账户资金->对账中心->账务明细随机选择一条数据点击查看详情处理验证码</div>
<div class="dialog-footer" slot="footer">
<el-button @click="isTipDlgShow = false">取消</el-button>
<el-button style="width: 180px;" @click="toGoon">已验证继续统计</el-button>
<el-button type="primary" @click="gotoValid">前往验证</el-button>
</div>
</ai-dialog>
</template>
</ai-list>
</template>
<script>
import {sendChromeAPIMessage } from '@/api/chromeApi'
import { Message } from 'element-ui'
import html2canvas from 'html2canvas'
export default {
name: 'ExportBillStatTemu',
data () {
return {
mallId: null,
dateRange: null,
transIncome: 0.0,
customerOutcome: 0.0,
nonSllerDuty: 0.0,
afterSaleAmount: 0.0,
logisticAmount: 0.0,
eprAmount: 0.0,
withdrawAmount: 0.0,
currentPage: 1,
pageSize: 100,
flowList: [],
currentIndex: 0,
isTipDlgShow: false,
isLoading: false
}
},
mounted () {
},
methods: {
toBeginStat() {
if (!this.dateRange) {
Message.error("请选择统计时间范围")
return
}
if (!this.mallId) {
Message.error("请选择店铺")
return
}
this.$userCheck(this.mallId).then(async () => {
this.currentPage = 1
this.flowList = []
this.isLoading = true
this.transIncome = 0.0
this.customerOutcome = 0.0
this.nonSllerDuty = 0.0
this.afterSaleAmount = 0.0
this.logisticAmount = 0.0
this.eprAmount = 0.0
this.withdrawAmount = 0.0
this.currentIndex = 0
await this.beginStat()
this.nonSllerDuty = Math.round(this.nonSllerDuty * 100) / 100
this.isLoading = false
}).catch((err) => {
this.mallId = ''
this.isLoading = false
})
},
async beginStat() {
let res = await sendChromeAPIMessage({
url: 'api/merchant/fund/detail/pageSearch',
needMallId: true,
mallId: this.mallId,
data: {
moneyChangeTypeList: [],
fundType: [],
beginTime: this.dateRange[0].getTime(),
endTime: this.dateRange[1].getTime() + 86400*1000 - 1,
pageSize: this.pageSize,
pageNum: this.currentPage
}})
if (res.errorCode == 1000000) {
this.flowList = this.flowList.concat(res.result.resultList)
if (res.result.resultList.length == this.pageSize) {
this.currentPage ++
await this.beginStat()
} else {
this.currentIndex = 0
await this.stat()
}
}
},
async stat() {
for (; this.currentIndex < this.flowList.length; this.currentIndex++) {
if (this.isTipDlgShow) {
this.currentIndex --
break
}
if (this.flowList[this.currentIndex].fundType == 100) { // 结算
await this.$sleepSync(300)
await this.querySattement(this.flowList[this.currentIndex])
} else if (this.flowList[this.currentIndex].fundType == 200) { // 提现
this.withdrawAmount += this.flowList[this.currentIndex].originAmount / 100
} else if (this.flowList[this.currentIndex].fundType == 400 && this.flowList[this.currentIndex].bizType == "deduct_risk_punish") { // 消费者及履约保障-售后问题
this.afterSaleAmount += Number(this.flowList[this.currentIndex].amount.replace('¥', ''))
} else if (this.flowList[this.currentIndex].fundType == 400 && this.flowList[this.currentIndex].bizType == "deduct_storage_service_fee") { // 仓储综合服务费
this.logisticAmount += Number(this.flowList[this.currentIndex].amount.replace('¥', ''))
} else if (this.flowList[this.currentIndex].fundType == 400 && this.flowList[this.currentIndex].bizType == "deduct_epr") { // 合规EPR物流包装环保费
this.eprAmount += Number(this.flowList[this.currentIndex].amount.replace('¥', ''))
}
}
},
async toGoon() {
this.isTipDlgShow = false
this.isLoading = true
await this.stat()
},
async querySattement(obj) {
let res = await sendChromeAPIMessage({
url: 'api/merchant/fund/detail/item',
needMallId: true,
mallId: this.mallId,
anti: true,
data: {
fundType: obj.fundType,
batchId: obj.transSn,
createdTime: obj.createTime
}})
if (res.errorCode == 1000000) {
for (let i = 0; i < res.result.length; i++) {
if (res.result[i].fundItemType == 1) { // 交易收入
this.transIncome += Number(res.result[i].amount)
} else if (res.result[i].fundItemType == 3) { // 消费者退款金额
this.customerOutcome += Number(res.result[i].amount)
} else if (res.result[i].fundItemType == 5) { // 非商责平台售后补贴金额
this.nonSllerDuty += Number(res.result[i].amount)
}
}
} else if (res.error_code == '54001') {
this.isLoading = false
this.isTipDlgShow = true
}
},
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);
}
},
gotoValid() {
window.open("https://seller.kuajingmaihuo.com/labor/bill", '_blank')
}
}
}
</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;
}
.amountItem {
margin: 10px;
}
</style>

View File

@@ -376,7 +376,7 @@ import { DualAxes } from '@antv/g2plot'
computed: {
topSaleAmountSkcList() {
const list = Object.assign([], this.skcList)
list.sort((a, b) => b.amount - a.amount)
list.sort((a, b) => b.saleAmount - a.saleAmount)
return list.slice(0, 10)
},
topSaleProfitSkcList() {
@@ -805,7 +805,7 @@ import { DualAxes } from '@antv/g2plot'
const a = document.createElement('a');
a.style.display = 'none'
a.href = img.src;
a.download = '近60天销售统计.png';
a.download = '销售统计.png';
a.click();
document.body.removeChild(img);
} catch (error) {

View File

@@ -3,7 +3,7 @@
<ai-title
slot="title"
title="物流统计"
tips="请先在当前浏览器登录“拼多多跨境卖家中心”,期间保持登录状态"
tips="数据来源于“履约服务账单->明细->缴费记录"
isShowBottomBorder>
</ai-title>
<template slot="content">

View File

@@ -255,16 +255,26 @@ import { saveAs } from 'file-saver'
}
},
async getPriceInfo(skcList) {
let res = await sendGeiwohuoAPIMessage({
url: `idms/goods-skc/price`,
method: 'POST',
data: skcList})
if (res.code == '0') {
for (let key in res.info) {
for (let i = 0; i < this.list.length; i++) {
if (key == this.list[i].sku) {
this.list[i].price = res.info[key]
break
let i = 0, len = 100
while(i < skcList.length) {
let tempSkcList = []
for (; i < skcList.length; i++) {
tempSkcList.push(skcList[i])
if (tempSkcList.length % len == 0) {
break
}
}
let res = await sendGeiwohuoAPIMessage({
url: `idms/goods-skc/price`,
method: 'POST',
data: tempSkcList})
if (res.code == '0') {
for (let key in res.info) {
for (let i = 0; i < this.list.length; i++) {
if (key == this.list[i].sku) {
this.list[i].price = res.info[key]
break
}
}
}
}
@@ -313,6 +323,7 @@ import { saveAs } from 'file-saver'
this.costDlgShow = true
},
importConfirm() {
this.isLoading = true
this.$refs.costForm.validate((valid) => {
const data = new FormData()
data.append('file', this.costForm.file[0].raw);
@@ -322,8 +333,10 @@ import { saveAs } from 'file-saver'
this.$message.success('导入成功')
this.currentPage = 1
this.list = []
this.isLoading = true
this.isLoading = false
this.getList()
} else {
this.isLoading = false
}
})
})

View File

@@ -199,7 +199,7 @@ import { saveAs } from 'file-saver'
for(let i = 0;i < res.info.data.length; i++) {
let item = res.info.data[i];
let data = {
orderNo: item.orderId,
orderNo: item.sellerOrderNo,
supplierCode: item.goods.supplierCode,
statusName: item.statusName,
skc: item.goods.skcName,

View File

@@ -490,6 +490,8 @@
Message.error("店铺【" + mallInfo.mallName + "】未设置默认发货地址,将无法自动创建发货单")
}
break
} else if (res.error_msg == '没权限访问') {
break
}
}
}
@@ -612,7 +614,23 @@
}
}
} else {
this.getList(data, mallId, mallName, currentPage)
if (res.error_msg == '没权限访问') {
if (this.loadMode == 1) {
this.loadMallIndex ++
if (this.loadMallIndex < this.mallList.length) {
this.getList(data, this.mallList[this.loadMallIndex].mallId, this.mallList[this.loadMallIndex].mallName, 1)
} else {
this.isLoading = false
this.isDlgLoading = false
Message.success("所有店铺备货单已加载完成")
}
} else {
this.isLoading = false
this.isDlgLoading = false
}
} else {
this.getList(data, mallId, mallName, currentPage)
}
}
},
remove(sn) {

View File

@@ -153,7 +153,6 @@ import { saveAs } from 'file-saver'
url: 'bg-visage-mms/labelcode/pageQuery',
needMallId: true,
mallId: this.form.mallId,
anti: true,
data: this.reqData}).then((res) => {
if (res.errorCode == 1000000) {
for(let i = 0;i < res.result.pageItems.length; i++) {

View File

@@ -194,14 +194,16 @@ import { saveAs } from 'file-saver'
for(let i = 0;i < res.result.deliveryOrderDetails.length; i++) {
let item = res.result.deliveryOrderDetails[i]
let specArr = item.secondarySpecVOList.map(item => {
return item.specName
})
let spec = specArr.join(',')
for(let j = 0; j < this.list.length; j++) {
if (this.list[j].productSkuId == item.productSkuId) {
this.list[j].specName = spec
if (item.secondarySpecVOList) {
let specArr = item.secondarySpecVOList.map(item => {
return item.specName
})
let spec = specArr.join(',')
for(let j = 0; j < this.list.length; j++) {
if (this.list[j].productSkuId == item.productSkuId) {
this.list[j].specName = spec
}
}
}
}