This commit is contained in:
刘仕伟
2024-01-10 01:27:15 +08:00
parent b304fb53a0
commit fc6cec2eca
12 changed files with 393 additions and 159 deletions

View File

@@ -483,3 +483,7 @@ img {
width: 240px;
}
}
.el-table__fixed-body-wrapper .el-table__body {
padding-bottom: 6px; // 6px为横向滚动条高度
}

View File

@@ -3,7 +3,7 @@
<el-alert
title="采集一个商品添加进草稿箱将消耗50金币"
type="success"
:closable="false">
:closable="false" style="margin-bottom: 10px;">
</el-alert>
<el-form class="ai-form" :model="form" label-width="150px" ref="form">
<el-form-item v-if="!isMultiCopy" label="商品地址:" style="width: 100%;" prop="url" :rules="[{ required: true, message: '请输入商品地址', trigger: 'blur' }]">
@@ -35,9 +35,13 @@
</template>
<script>
import {sendChromeAPIMessage, sendTemuAPIMessage, sendChromeWebReqMessage} from '@/api/chromeApi'
import {sendChromeAPIMessage, sendChromeWebReqMessage} from '@/api/chromeApi'
import AiLazyCascader from "@/components/AiLazyCascader.vue"
import { getImageMd5 } from "@/utils/image.js"
import { getImageMd5, uploadImage } from "@/utils/image.js"
import { extractImagesAndText } from "@/utils/html.js"
import { transformAliExpress } from "@/utils/product.js"
import { formatDate } from "@/utils/date.js"
import { createFolderApi } from "@/utils/folder.js"
import { Message } from 'element-ui'
import { MessageBox } from 'element-ui';
@@ -129,6 +133,7 @@ export default {
})
},
async addToDraft() {
// let test = await createFolderApi(formatDate(new Date()).split('-'),this.form.targetMallId)
this.isCopying = true
let res = await sendChromeWebReqMessage({
type: 'aliexpress',
@@ -145,35 +150,41 @@ export default {
str = str.substring(str.indexOf('data'))
str = str.substring(5)
console.log(str)
let res2 = await getImageMd5('https://ae01.alicdn.com/kf/S654a8881c4e84117b3ea74c563ff61b4Q.jpg')
//console.log(res2)
let obj = JSON.parse(str)
/*let res1 = await sendChromeWebReqMessage({
let folderId = await createFolderApi(formatDate(new Date()).split('-'),this.form.targetMallId)
let carouselImageUrls = [], detailImageUrls = []
let imageConponent = obj.imageComponent
for (let i = 0; i < imageConponent.imagePathList.length; i++) {
let img = await uploadImage(folderId, imageConponent.imagePathList[i], this.form.targetMallId)
carouselImageUrls.push(img)
}
let res1 = await sendChromeWebReqMessage({
type: 'aliexpress',
url: obj.productDescComponent.descriptionUrl,
})
res1 = res1.substring(0, res1.indexOf("<script>"))
let str1 = res1.replace(/<img[^>]+src="([^">]+)"[^>]+>/g, '$1\n').replace(/<.*?>/g, '[||]')
let arr = str1.split('[||]')
for (let i = 0; i < arr.length; i++) {
console.log(arr[i])
}*/
res1 = extractImagesAndText(res1)
res1 = JSON.parse(res1)
console.log(res1)
for (let i = 0; i < res1.images.length; i++) {
let img = await uploadImage(folderId, res1.images[i], this.form.targetMallId)
detailImageUrls.push(img)
}
this.createDraft(transformAliExpress({
title: obj.productInfoComponent.subject,
carouselImageUrls,
detailImageList: detailImageUrls,
text: res1.text
}))
},
async createDraft(data) {
let catId = null;
if (this.form.isSameCategory) {
catId = this.catId;
} else {
catId = this.form.targetCatId[this.form.targetCatId.length - 1]
}
let catId = this.form.targetCatId[this.form.targetCatId.length - 1]
let res = await sendChromeAPIMessage({
url: 'bg-visage-mms/product/draft/add',
needMallId: true,
@@ -186,60 +197,8 @@ export default {
let draftId = res.result.productDraftId
let content = data
let i = 0
if (this.form.isSameCategory) {
let res2 = await this.$http.post('/api/innerCategory/fullById',null , {
params: {
id: catId
}
})
for (; i < res2.data.length; i++) {
content['cat' + (i+1) + 'Id'] = res2.data[i]
}
let res3 = await sendChromeAPIMessage({
url: 'bg-anniston-mms/category/template/query',
needMallId: true,
mallId: this.form.targetMallId,
data: {
catId: catId,
productCreateTime: null,
langList: [
"en"
]
}})
content.productPropertyReqs = []
for (let j = 0; j < this.goodsProperty.length; j++) {
let temp = {}
for (let k = 0; k < res3.result.properties.length; k++) {
if (this.goodsProperty[j].key == res3.result.properties[k].lang2Name.en) {
temp.templatePid = res3.result.properties[k].templatePid
temp.pid = res3.result.properties[k].pid
temp.refPid = res3.result.properties[k].refPid
temp.propName = res3.result.properties[k].name
for (let x = 0; x < this.goodsProperty[j].values.length; x++) {
if (null == res3.result.properties[k].values) break
for (let l = 0; l < res3.result.properties[k].values.length; l++) {
if (res3.result.properties[k].values[l].lang2Value.en == this.goodsProperty[j].values[x]) {
temp.vid = res3.result.properties[k].values[l].vid
temp.propValue = res3.result.properties[k].values[l].value
temp.valueUnit = ''
temp.valueExtendInfo = ''
temp.controlType = res3.result.properties[k].values[l].controlType
content.productPropertyReqs.push({...temp})
break
}
}
}
}
}
}
} else {
for (; i < this.form.targetCatId.length; i++) {
content['cat' + (i+1) + 'Id'] = this.form.targetCatId[i]
}
for (; i < this.form.targetCatId.length; i++) {
content['cat' + (i+1) + 'Id'] = this.form.targetCatId[i]
}
for (; i < 10; i++) {
content['cat' + (i+1) + 'Id'] = ''
@@ -259,26 +218,13 @@ export default {
url: 'bg-visage-mms/product/draft/save',
needMallId: true,
mallId: this.form.targetMallId,
data: {
...content
}}).then((res) => {
data: content
}).then((res) => {
if (res.errorCode == 1000000) {
this.successList.push(this.currentUrl)
this.saveInfo()
if (this.isMultiCopy) {
this.currentIndex ++
if (this.currentIndex == this.params.urlList.length) {
this.isCopying = false
this.$emit('onSuccess')
MessageBox.alert(`成功添加${this.successList.length}个商品进入草稿箱`)
} else {
this.currentUrl = this.params.urlList[this.currentIndex]
this.execAddToDraft()
}
} else {
this.isCopying = false
Message.success("成功添加到草稿箱")
}
this.isCopying = false
Message.success("成功添加到草稿箱")
} else {
setTimeout(() => {
@@ -336,33 +282,6 @@ export default {
}
}
})
},
parseURL(url) {
let a = document.createElement('a');
a.href = url;
return {
source: url,
protocol: a.protocol.replace(':',''),
host: a.hostname,
port: a.port,
query: a.search,
params: (function(){
var ret = {},
seg = a.search.replace(/^\?/,'').split('&'),
len = seg.length, i = 0, s;
for (;i<len;i++) {
if (!seg[i]) { continue; }
s = seg[i].split('=');
ret[s[0]] = s[1];
}
return ret;
})(),
file: (a.pathname.match(/\/([^\/?#]+)$/i) || [,''])[1],
hash: a.hash.replace('#',''),
path: a.pathname.replace(/^([^\/])/,'/$1'),
relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [,''])[1],
segments: a.pathname.replace(/^\//,'').split('/')
};
}
}
}

View File

@@ -3,7 +3,7 @@
<el-alert
title="采集一个商品添加进草稿箱将消耗20金币"
type="success"
:closable="false">
:closable="false" style="margin-bottom: 10px;">
</el-alert>
<el-form class="ai-form" :model="form" label-width="150px" ref="form">
<el-form-item v-if="!isMultiCopy" label="商品地址:" style="width: 100%;" prop="url" :rules="[{ required: true, message: '请输入商品地址', trigger: 'blur' }]">

View File

@@ -2,7 +2,7 @@
利用chrome的fetch来避免跨域
**/
import {getImageMd5} from "@/utils/image";
import {getImageBlob} from "@/utils/image";
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.type == 'api') {
@@ -36,7 +36,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
}
headers.cookie = getCookie();
if (request.isFormData && !!request.data.image) {//针对图片上传特殊的处理办法
getImageMd5(request.data.image).then(res => data.append('image', res.image.blobData)).then(resolve)
getImageBlob(request.data.image).then(res => data.append('image', res.image.blobData)).then(resolve)
} else resolve()
}).then(() => fetch(request.url, {
headers, 'method': 'POST', 'referrerPolicy': 'no-referrer', 'credentials': 'include', 'body': data, 'mode': 'cors'

48
src/utils/folder.js Normal file
View File

@@ -0,0 +1,48 @@
import {sendChromeAPIMessage} from '@/api/chromeApi'
export async function createFolderApi(folderArr, mallId) {
let res1 = await queryAllFolders(mallId)
return await createFolder(res1, folderArr, 0, mallId)
}
async function createFolder(folders, folderArr, index, mallId) {
for (let i = 0; i < folders.childFolderList.length; i++) {
if (folders.childFolderList[i].folderName == folderArr[index]) {
if (folders.childFolderList[i].childFolderList.length == 0) {
if (index == (folderArr.length - 1)) {
return folders.childFolderList[i].folderId
} else {
return await createFolderFunc(folders.childFolderList[i].folderId, folderArr, ++index, mallId)
}
} else {
return createFolder(folders.childFolderList[i], folderArr, ++index, mallId)
}
}
}
return await createFolderFunc(folders.folderId, folderArr, index, mallId)
}
async function createFolderFunc(folderId, folderArr, index, mallId) {
let tempFolderId = folderId
await sendChromeAPIMessage({
url: 'marvel-mms/cn/api/kiana/gmp/bg/phoenix/api/material/create-folder',
needMallId: true,
mallId: mallId,
data: {
parentId: tempFolderId,
folderName: folderArr[index]
}})
let res1 = await queryAllFolders(mallId)
return await createFolder(res1, folderArr, 0, mallId)
}
async function queryAllFolders(mallId) {
let folders = await sendChromeAPIMessage({
url: 'marvel-mms/cn/api/kiana/gmp/bg/phoenix/api/material/query-folders',
needMallId: true,
mallId: mallId,
data: {}})
return folders.result.rootFolder
}

12
src/utils/html.js Normal file
View File

@@ -0,0 +1,12 @@
export function extractImagesAndText(htmlString) {
const parser = new DOMParser();
const doc = parser.parseFromString(htmlString, "text/html");
const images = Array.from(doc.querySelectorAll("img")).map(img => img.src);
const text = doc.body.textContent.trim();
const data = {
images,
text
};
const jsonData = JSON.stringify(data);
return jsonData;
}

View File

@@ -1,30 +1,7 @@
import SparkMd5 from 'spark-md5'
import {sendChromeAPIMessage} from '@/api/chromeApi'
export function getImageMd5(imageUrl) {
/*return new Promise((resolve, reject) => {
fetch(imageUrl).then((response) => response.blob()) // 将响应转换为Blob对象
.then((blobData) => {
const reader = new FileReader();
// 读取Blob对象的内容
reader.onloadend = function() {
let spark = new SparkMd5()
spark.appendBinary(this.result);
let md5 = spark.end()
console.log(md5)
console.log(this.result)
//resolve({md5, imageData: new File([blobData], 'test.jpeg', {type: blobData.type})});
resolve({md5, imageData: this.result});
};
reader.readAsBinaryString(blobData); // 将Blob对象作为参数传递给FileReader的readAsArrayBuffer()方法
})
});*/
return new Promise((resolve) => {
fetch(imageUrl).then((response) => response.blob()) // 将响应转换为Blob对象
.then((blobData) => {
@@ -35,12 +12,84 @@ export function getImageMd5(imageUrl) {
const spark = new SparkMd5.ArrayBuffer()
spark.append(reader.result);
const md5 = spark.end()
// formData.append('md5', md5)
//resolve({md5, imageData: new File([blobData], 'test.jpeg', {type: blobData.type})});
const image = {blobData, fileName}
resolve({md5, image});
resolve({md5, fileName});
};
reader.readAsArrayBuffer(blobData); // 将Blob对象作为参数传递给FileReader的readAsArrayBuffer()方法
})
});
}
export function getImageBlob(imageUrl) {
return new Promise((resolve) => {
fetch(imageUrl).then((response) => response.blob()) // 将响应转换为Blob对象
.then((blobData) => {
const fileName = imageUrl.match(/\/([^/]+)$/).at(-1)
const reader = new FileReader();
// 读取Blob对象的内容
reader.onloadend = function () {
const image = {blobData, fileName}
resolve({image});
};
reader.readAsArrayBuffer(blobData); // 将Blob对象作为参数传递给FileReader的readAsArrayBuffer()方法
})
});
}
export async function uploadImage(folderId, imageUrl, mallId) {
let res1 = await getImageMd5(imageUrl)
let detailList = []
detailList.push({
materialMd5: res1.md5,
materialName: res1.fileName,
materialType: 1
})
let res2 = await sendChromeAPIMessage({
url: 'marvel-mms/cn/api/kiana/gmp/bg/phoenix/api/material/create',
needMallId: true,
mallId: mallId,
data: {
createDetailList: detailList,
folderId: folderId
}})
if (res2.success) {
if (res2.result.responseDetailList[0].alreadyExists) {
return res2.result.responseDetailList[0].imgUrl
} else {
let res3 = await sendChromeAPIMessage({
url: 'galerie/business/get_signature?sdk_version=js-0.0.16-alpha.0&tag_name=product-material-tag',
needMallId: true,
mallId: mallId,
data: {
bucket_tag: "product-material-tag"
}
})
let res4 = await sendChromeAPIMessage({
url: 'https://kuajing-file.pinduoduo.com/api/galerie/v3/store_image?sdk_version=js-0.0.16-alpha.0&tag_name=product-material-tag',
isFormData: true,
data: {
url_width_height: true,
image: imageUrl,
upload_sign: res3.result.signature
}
})
let res5 = await sendChromeAPIMessage({
url: 'marvel-mms/cn/api/kiana/gmp/bg/phoenix/api/material/edit',
needMallId: true,
mallId: mallId,
data: {
id: res2.result.responseDetailList[0].id,
materialName: res2.result.responseDetailList[0].materialName,
materialType: 1,
uploadStatus: 3,
url: res4.url
}
})
return res5.result.imgUrl
}
}
}

View File

@@ -137,4 +137,85 @@ export function transform(leftData) {
rightData.productDraftId = "";
return JSON.stringify(rightData);
}
export function transformAliExpress(content) {
let template = {
cat1Id: 0,
cat2Id: 0,
cat3Id: 0,
cat4Id: 0,
cat5Id: 0,
cat6Id: 0,
cat7Id: 0,
cat8Id: 0,
cat9Id: 0,
cat10Id: 0,
materialMultiLanguages: [],
productName: content.title,
productPropertyReqs: [],
productSkcReqs: [],
productSpecPropertyReqs: [],
carouselImageUrls: content.carouselImageUrls,
carouselImageI18nReqs: [],
materialImgUrl: content.carouselImageUrls[0],
goodsLayerDecorationReqs: [],
sizeTemplateIds: [],
showSizeTemplateIds: [],
goodsModelReqs: [],
productWhExtAttrReq: {
outerGoodsUrl: "",
productOrigin: {
countryShortName: "CN"
}
},
productCarouseVideoReqList: [],
goodsAdvantageLabelTypes: [],
productDetailVideoReqList: [],
productOuterPackageImageReqs: [],
productOuterPackageReq: {},
sensitiveTransNormalFileReqs: [],
productGuideFileI18nReqs: [],
productSaleExtAttrReq: {},
productNonAuditExtAttrReq: {
california65WarningInfoReq: {}
},
productDraftId: 0
}
if (!!content.text) {
template.goodsLayerDecorationReqs.push({
floorId: null,
lang: "zh",
key: "DecImage",
type: "text",
priority: 0,
contentList: [
{
text: content.text,
textModuleDetails: {
fontSize: 12,
fontColor: "#333333",
backgroundColor: "#ffffff",
align: "left"
}
}
]
})
}
for (let i = 0; i < content.detailImageList.length; i++) {
let imgList = []
imgList.push({imgUrl: content.detailImageList[i]})
template.goodsLayerDecorationReqs.push({
floorId: null,
lang: "zh",
key: "DecImage",
type: "image",
priority: 0,
contentList: imgList
})
}
return template
}

View File

@@ -78,6 +78,16 @@
worksheet="销售统计">
<el-button type="primary">导出数据</el-button>
</json-excel>
<json-excel
:data="skuSaleNumberList"
v-show="false"
:fields="skuSaleNumberFields"
:before-generate = "startSkuSaleNumberDownload"
name="SKU历史销量.xls"
worksheet="SKU历史销量">
<el-button type="primary" id="downloadSkuSaleNumber"></el-button>
</json-excel>
<el-button v-if="type === '0' && mallId" type="primary" @click="toDownload">导出SKU历史销量</el-button>
</template>
<ai-table
ref="table0"
@@ -141,6 +151,30 @@
</ai-table>
</ai-card>
</template>
<ai-dialog
title="导出SKU历史销量"
:visible.sync="downloadSkuSaleNumberDlg"
:close-on-click-modal="false"
width="790px"
customFooter
@close="handleClose">
<!--<el-form class="ai-form" :model="skuDownloadForm" label-width="160px" ref="skuDownloadForm">
<el-form-item label="日期范围" style="width: 100%;" prop="date" :rules="[{ required: true, message: '请选择时间范围', trigger: 'blur' }]">
<el-date-picker
v-model="skuDownloadForm.date"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</el-form-item>
</el-form>-->
<div class="dialog-footer" slot="footer">
<el-button @click="downloadSkuSaleNumberDlg = false"> </el-button>
<el-button type="primary" @click="toDownloadSkuSaleNumber">确定</el-button>
</div>
</ai-dialog>
</ai-list>
</template>
@@ -224,7 +258,15 @@ import { Message } from 'element-ui'
"库存货值(CNY)": "productTotalPrice",
"店铺名称": "mallName",
"评分": "mark",
"是否热销": 'hotTag'
"是否热销": 'hotTag',
"生产建议信息 - 建议生产数": 'adviceProduceNum',
"生产建议信息 - 剩余件数": 'availableProduceNum'
},
skuSaleNumberList: [],
downloadSkuSaleNumberDlg: true,
skuDownloadForm: {
date: ''
}
}
},
@@ -341,7 +383,7 @@ import { Message } from 'element-ui'
return -1
}
}
},
}
]
@@ -530,6 +572,21 @@ import { Message } from 'element-ui'
jsonFields[dateStr] = dateStr
}
return jsonFields
},
skuSaleNumberFields () {
let jsonFields = {
"日期": "date"
}
let date = new Date()
date.setDate(date.getDate() )
for (let i = 0; i < 30; i++) {
date.setDate(date.getDate() - 1)
let dateStr = formatDate(date)
jsonFields[dateStr] = dateStr
}
return jsonFields
}
},
@@ -651,6 +708,8 @@ import { Message } from 'element-ui'
this.inventoryTotal += item.skuQuantityDetailList[j].inventoryNumInfo.warehouseInventoryNum
this.inventoryMoeny += new Number(((item.skuQuantityDetailList[j].supplierPrice / 100) * item.skuQuantityDetailList[j].inventoryNumInfo.warehouseInventoryNum).toFixed(2))
this.inventoryMoeny = new Number(this.inventoryMoeny.toFixed(2))
this.adviceProduceNum = item.skuQuantityDetailList[j].adviceProduceNum || '-'
this.availableProduceNum = item.skuQuantityDetailList[j].availableProduceNum || '-'
this.list.push(data);
// 计算已发货货值
@@ -775,6 +834,61 @@ import { Message } from 'element-ui'
}
}
})
},
toDownload() {
console.log(111)
this.downloadSkuSaleNumberDlg = true
},
startSkuSaleNumberDownload() {
},
handleClose() {
downloadSkuSaleNumberDlg = false
},
async toDownloadSkuSaleNumber() {
this.isLoading = true
if (!this.skuDownloadForm.date) {
Message.error("请选择时间")
return
}
let beginDateStr = formatDate(this.skuDownloadForm.date[0])
let endDateStyr = formatDate(this.skuDownloadForm.date[1])
let beginDate = this.skuDownloadForm.date[0]
let endDate = this.skuDownloadForm.date[1]
this.skuSaleNumberFields = {
"日期": "date"
}
for (; beginDate.getTime() < endDate.getTime(); ) {
let dateStr = formatDate(endDate)
this.skuSaleNumberFields[dateStr] = dateStr
endDate.setDate(endDate.getDate() - 1)
}
let tempSkuList = this.list.filter(item => {
return item.onSalesDurationOffline != '-天'
})
let tempIds = []
tempSkuId.map(i => {
tempIds.push(i.productSkuId)
})
let res = await sendChromeAPIMessage({
url: 'oms/bg/venom/api/supplier/sales/management/querySkuSalesNumber',
needMallId: true,
mallId: this.mallId,
data: {
"productSkuIds": tempIds,
"startDate": beginDateStr,
"endDate": endDateStyr
}})
console.log(res)
}
}
}

View File

@@ -56,7 +56,7 @@
</ai-list>
<ai-dialog
title="采集"
title="速卖通采集"
:visible.sync="copyFromDlgShow"
:close-on-click-modal="false"
width="790px"
@@ -103,7 +103,7 @@ export default {
methods: {
async getList() {
const image = 'http://temu.jjcp52.com/dist/test.png'
/*const image = 'http://temu.jjcp52.com/dist/test.png'
let res2 = await getImageMd5(image), res4
Promise.all([
sendChromeAPIMessage({
@@ -137,11 +137,13 @@ export default {
image,
upload_sign: res4.result.signature
}
})).then(() => this.$http.post('/api/copyProduct/myPage', null, {
}))*/
this.$http.post('/api/copyProduct/myPage', null, {
params: {
...this.search
}
})).then(res => {
}).then(res => {
this.tableData = res.data.records
this.total = res.data.total
})

View File

@@ -56,7 +56,7 @@
</ai-list>
<ai-dialog
title="采集"
title="TEMU采集"
:visible.sync="copyFromDlgShow"
:close-on-click-modal="false"
width="790px"

View File

@@ -442,7 +442,12 @@ import {sendTemuAPIMessage, sendSheinAPIMessage} from '@/api/chromeApi'
res.goods.map(item => {
let total = 0
if (item.pretreatInfo?.sellingPointUniversalLabels) {
total = item.pretreatInfo.sellingPointUniversalLabels[0].starComment?.comment_num || 0
for (let i = 0; i < item.pretreatInfo.sellingPointUniversalLabels.length; i++) {
if (item.pretreatInfo.sellingPointUniversalLabels[i].starComment) {
total = item.pretreatInfo.sellingPointUniversalLabels[i].starComment?.comment_num || 0
break
}
}
}
reqData.details.push({