222
This commit is contained in:
parent
90ecbd90cd
commit
1388d39376
@ -209,6 +209,7 @@ public class BanmaOrderController extends BaseController {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private String getTrackingInfo(String trackingNumber) {
|
||||
try {
|
||||
R<Map<String, Object>> sagawaResult = sagawaExpressController.getTrackingInfo(trackingNumber);
|
||||
if (sagawaResult != null && sagawaResult.getCode() == 200) {
|
||||
Map<String, Object> sagawaData = sagawaResult.getData();
|
||||
@ -223,23 +224,31 @@ public class BanmaOrderController extends BaseController {
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
String url = String.format(TRACKING_URL, trackingNumber);
|
||||
ResponseEntity<Map> response = restTemplate.getForEntity(url, Map.class);
|
||||
Map<String, Object> responseBody = response.getBody();
|
||||
if (responseBody != null && Integer.valueOf(0).equals(responseBody.get("code"))) {
|
||||
return Optional.ofNullable(responseBody.get("data"))
|
||||
.map(data -> (List<Map<String, Object>>) data)
|
||||
.filter(list -> !list.isEmpty())
|
||||
.map(list -> list.get(0))
|
||||
.map(track -> (String) track.get("track"))
|
||||
.orElse(null);
|
||||
|
||||
// 如果从佐川获取失败,尝试从斑马API获取
|
||||
try {
|
||||
String url = String.format(TRACKING_URL, trackingNumber);
|
||||
ResponseEntity<Map> response = restTemplate.getForEntity(url, Map.class);
|
||||
Map<String, Object> responseBody = response.getBody();
|
||||
if (responseBody != null && Integer.valueOf(0).equals(responseBody.get("code"))) {
|
||||
return Optional.ofNullable(responseBody.get("data"))
|
||||
.map(data -> (List<Map<String, Object>>) data)
|
||||
.filter(list -> !list.isEmpty())
|
||||
.map(list -> list.get(0))
|
||||
.map(track -> (String) track.get("track"))
|
||||
.orElse(null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("从斑马API获取物流信息失败: {}", e.getMessage());
|
||||
// 继续处理,不中断流程
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("获取物流信息失败: {}", e.getMessage());
|
||||
// 继续处理,不中断流程
|
||||
}
|
||||
|
||||
return null;
|
||||
// 如果所有尝试都失败,返回默认值
|
||||
return "暂无物流信息";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -298,7 +307,8 @@ public class BanmaOrderController extends BaseController {
|
||||
|
||||
// 计算分页信息
|
||||
int totalPages = (int) Math.ceil((double) totalCount / DEFAULT_PAGE_SIZE);
|
||||
boolean hasMore = orders.size() == DEFAULT_PAGE_SIZE;
|
||||
// 修改hasMore判断逻辑,根据当前页数和总页数判断
|
||||
boolean hasMore = totalCount > 0 && 1 < totalPages;
|
||||
|
||||
// 构建结果
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
@ -340,12 +350,26 @@ public class BanmaOrderController extends BaseController {
|
||||
@ApiParam("开始日期(yyyy-MM-dd)") @RequestParam(required = false) String startDate,
|
||||
@ApiParam("结束日期(yyyy-MM-dd)") @RequestParam(required = false) String endDate) {
|
||||
|
||||
DeferredResult<R<Map<String, Object>>> deferredResult = new DeferredResult<>(120000L);
|
||||
DeferredResult<R<Map<String, Object>>> deferredResult = new DeferredResult<>(999999999L);
|
||||
|
||||
Thread thread = new Thread(() -> {
|
||||
try {
|
||||
if (deferredResult.isSetOrExpired()) return;
|
||||
|
||||
// 获取总页数信息
|
||||
HttpEntity<String> entity = createHttpEntity();
|
||||
String url = buildApiUrl(1, DEFAULT_PAGE_SIZE, startDate, endDate);
|
||||
ResponseEntity<Map> countResponse = restTemplate.exchange(url, HttpMethod.GET, entity, Map.class);
|
||||
Map<String, Object> countResponseBody = countResponse.getBody();
|
||||
int totalPages = 1;
|
||||
|
||||
if (countResponseBody != null && countResponseBody.containsKey("data")) {
|
||||
Map<String, Object> dataMap = (Map<String, Object>) countResponseBody.get("data");
|
||||
int totalCount = ((Number) dataMap.getOrDefault("total", 0)).intValue();
|
||||
totalPages = (int) Math.ceil((double) totalCount / DEFAULT_PAGE_SIZE);
|
||||
}
|
||||
|
||||
// 获取当前页数据
|
||||
R<List<Map<String, Object>>> pageResult = fetchOrdersFromApi(page, DEFAULT_PAGE_SIZE, startDate, endDate);
|
||||
|
||||
if (pageResult.getCode() != 200) {
|
||||
@ -355,10 +379,14 @@ public class BanmaOrderController extends BaseController {
|
||||
|
||||
List<Map<String, Object>> pageData = pageResult.getData();
|
||||
|
||||
// 修改hasMore判断逻辑,根据当前页数和总页数判断
|
||||
boolean hasMore = page < totalPages;
|
||||
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
resultMap.put("orders", pageData);
|
||||
resultMap.put("hasMore", pageData != null && pageData.size() == DEFAULT_PAGE_SIZE);
|
||||
resultMap.put("hasMore", hasMore);
|
||||
resultMap.put("nextPage", page + 1);
|
||||
resultMap.put("totalPages", totalPages);
|
||||
|
||||
setDeferredResult(deferredResult, R.ok(resultMap));
|
||||
} catch (Exception e) {
|
||||
@ -369,7 +397,6 @@ public class BanmaOrderController extends BaseController {
|
||||
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
|
||||
return deferredResult;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,160 @@
|
||||
package com.ruoyi.web.controller.tool;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.io.IOException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
|
||||
import us.codecraft.webmagic.Page;
|
||||
import us.codecraft.webmagic.Site;
|
||||
import us.codecraft.webmagic.Spider;
|
||||
import us.codecraft.webmagic.processor.PageProcessor;
|
||||
|
||||
/**
|
||||
* 佐川急便物流查询控制器
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Api("佐川急便物流查询接口")
|
||||
@RestController
|
||||
@RequestMapping("/tool/sagawa")
|
||||
@Anonymous
|
||||
public class SagawaExpressController extends BaseController implements PageProcessor {
|
||||
private static final Logger logger = LoggerFactory.getLogger(SagawaExpressController.class);
|
||||
|
||||
// 站点配置
|
||||
private final Site site = Site.me()
|
||||
.setRetryTimes(3)
|
||||
.setSleepTime(1000)
|
||||
.setTimeOut(99999999)
|
||||
.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36");
|
||||
|
||||
// 查询结果
|
||||
private Map<String, Object> resultMap = new HashMap<>();
|
||||
@Override
|
||||
public void process(Page page) {
|
||||
try {
|
||||
String pageContent = page.getHtml().toString();
|
||||
|
||||
// 检查是否有包裹数据未登记的信息
|
||||
boolean hasNoData = pageContent.contains("お荷物データが登録されておりません");
|
||||
|
||||
if (hasNoData) {
|
||||
resultMap.put("status", "notFound");
|
||||
resultMap.put("message", "没有找到对应的包裹信息");
|
||||
return;
|
||||
}
|
||||
|
||||
String trackingTableRegex = "<table[^>]*class=\"table_basic table_okurijo_detail2\"[^>]*>\\s*<tbody>\\s*(?:<tr>.*?<th[^>]*>荷物状況</th>.*?</tr>.*?<tr>.*?</tr>.*?)+\\s*</tbody>\\s*</table>";
|
||||
Pattern pattern = Pattern.compile(trackingTableRegex, Pattern.DOTALL);
|
||||
Matcher matcher = pattern.matcher(pageContent);
|
||||
|
||||
if (matcher.find()) {
|
||||
String trackingTable = matcher.group(0);
|
||||
|
||||
// 提取表格中的最后一行
|
||||
String rowRegex = "<tr>\\s*<td>\\s*([^<]*?)\\s*</td>\\s*<td>\\s*([^<]*?)\\s*</td>\\s*<td>\\s*([^<]*?)\\s*</td>\\s*</tr>";
|
||||
Pattern rowPattern = Pattern.compile(rowRegex, Pattern.DOTALL);
|
||||
Matcher rowMatcher = rowPattern.matcher(trackingTable);
|
||||
|
||||
String status = "";
|
||||
String dateTime = "";
|
||||
String office = "";
|
||||
|
||||
// 找到所有匹配项,保留最后一个
|
||||
while (rowMatcher.find()) {
|
||||
status = rowMatcher.group(1).trim();
|
||||
dateTime = rowMatcher.group(2).trim();
|
||||
office = rowMatcher.group(3).trim();
|
||||
}
|
||||
|
||||
if (!status.isEmpty()) {
|
||||
Map<String, String> trackInfo = new HashMap<>();
|
||||
trackInfo.put("status", status);
|
||||
trackInfo.put("dateTime", dateTime);
|
||||
trackInfo.put("office", office);
|
||||
|
||||
resultMap.put("status", "success");
|
||||
resultMap.put("trackInfo", trackInfo);
|
||||
} else {
|
||||
resultMap.put("status", "noRecords");
|
||||
resultMap.put("message", "没有物流记录");
|
||||
}
|
||||
} else {
|
||||
resultMap.put("status", "noTable");
|
||||
resultMap.put("message", "未找到物流跟踪表格");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("解析页面失败", e);
|
||||
resultMap.put("status", "error");
|
||||
resultMap.put("message", "解析页面失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Site getSite() {
|
||||
return site;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建佐川急便查询URL
|
||||
*/
|
||||
private String buildSagawaUrl(String trackingNumber) {
|
||||
return "https://k2k.sagawa-exp.co.jp/p/web/okurijosearch.do?okurijoNo=" + trackingNumber.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询佐川急便物流信息
|
||||
*/
|
||||
@ApiOperation("查询佐川急便物流信息")
|
||||
@GetMapping("/tracking/{trackingNumber}")
|
||||
public R<Map<String, Object>> getTrackingInfo(@PathVariable("trackingNumber") String trackingNumber) {
|
||||
try {
|
||||
if (trackingNumber == null || trackingNumber.trim().isEmpty()) {
|
||||
return R.fail("运单号不能为空");
|
||||
}
|
||||
|
||||
resultMap = new HashMap<>();
|
||||
String url = buildSagawaUrl(trackingNumber);
|
||||
|
||||
try {
|
||||
Spider spider = Spider.create(this)
|
||||
.addUrl(url)
|
||||
.thread(1);
|
||||
spider.run();
|
||||
} catch (Exception e) {
|
||||
logger.error("爬取物流信息失败,运单号:" + trackingNumber, e);
|
||||
Map<String, String> defaultTrackInfo = new HashMap<>();
|
||||
defaultTrackInfo.put("status", "处理中");
|
||||
defaultTrackInfo.put("dateTime", "");
|
||||
defaultTrackInfo.put("office", "");
|
||||
|
||||
resultMap.put("status", "success");
|
||||
resultMap.put("trackInfo", defaultTrackInfo);
|
||||
}
|
||||
|
||||
return R.ok(resultMap);
|
||||
} catch (Exception e) {
|
||||
logger.error("查询物流信息失败", e);
|
||||
Map<String, Object> errorResult = new HashMap<>();
|
||||
errorResult.put("status", "error");
|
||||
errorResult.put("message", "查询物流信息失败: " + e.getMessage());
|
||||
return R.ok(errorResult);
|
||||
}
|
||||
}
|
||||
}
|
@ -17,11 +17,13 @@ const service = axios.create({
|
||||
// axios中请求配置有baseURL选项,表示请求URL公共部分
|
||||
baseURL: process.env.VUE_APP_BASE_API,
|
||||
// 超时
|
||||
timeout: 10000
|
||||
timeout: 99999999
|
||||
})
|
||||
|
||||
// request拦截器
|
||||
service.interceptors.request.use(config => {
|
||||
config.timeout = 99999999;
|
||||
|
||||
// 是否需要设置 token
|
||||
const isToken = (config.headers || {}).isToken === false
|
||||
// 是否需要防止数据重复提交
|
||||
|
@ -6,7 +6,7 @@
|
||||
<span class="card-title">斑马订单数据</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<!-- 时间范围选择器和操作按钮区域 -->
|
||||
<div class="filter-container">
|
||||
<el-form :inline="true" class="filter-form">
|
||||
@ -62,7 +62,7 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 数据统计信息 -->
|
||||
<el-alert
|
||||
v-if="orderData.length > 0"
|
||||
@ -77,30 +77,28 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-alert>
|
||||
|
||||
|
||||
<!-- 加载进度条 -->
|
||||
<el-row v-if="loading">
|
||||
<el-col :span="24">
|
||||
<div class="progress-container">
|
||||
<el-progress
|
||||
:percentage="loadProgress.percentage"
|
||||
:format="progressFormat"
|
||||
status="primary"
|
||||
<el-progress
|
||||
:percentage="loadProgress.percentage"
|
||||
:stroke-width="12"
|
||||
class="load-progress">
|
||||
</el-progress>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<el-table v-loading="loading" :data="orderData" border style="width: 100%">
|
||||
<el-table-column label="下单时间" align="center" prop="orderedAt" min-width="140" header-align="center" />
|
||||
<el-table-column label="商品图片" align="center" prop="productImage" min-width="90" header-align="center">
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
v-if="scope.row.productImage"
|
||||
:src="scope.row.productImage"
|
||||
<el-image
|
||||
v-if="scope.row.productImage"
|
||||
:src="scope.row.productImage"
|
||||
style="width: 60px; height: 60px; object-fit: contain;"
|
||||
:preview-src-list="[scope.row.productImage]"
|
||||
@error="handleImageError($event, scope.row)">
|
||||
@ -147,7 +145,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
|
||||
<!-- 无更多数据提示 -->
|
||||
<div v-if="orderData.length > 0 && !hasMore && !loading" class="no-more-data">
|
||||
已加载全部数据
|
||||
@ -232,7 +230,7 @@ export default {
|
||||
console.error('加载缓存数据失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/** 保存数据到本地缓存 */
|
||||
saveToCache() {
|
||||
try {
|
||||
@ -253,13 +251,13 @@ export default {
|
||||
this.$message.warning('保存数据到本地缓存失败');
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/** 清空缓存数据 */
|
||||
clearCache() {
|
||||
localStorage.removeItem('banma_orders_data');
|
||||
this.$message.success('缓存数据已清除');
|
||||
},
|
||||
|
||||
|
||||
/** 获取订单列表 */
|
||||
loadData() {
|
||||
this.loading = true;
|
||||
@ -273,11 +271,11 @@ export default {
|
||||
total: 100,
|
||||
percentage: 0
|
||||
};
|
||||
|
||||
|
||||
this.clearCache();
|
||||
this.loadFirstPage();
|
||||
},
|
||||
|
||||
|
||||
/** 加载第一页数据 */
|
||||
loadFirstPage() {
|
||||
// 准备请求参数,添加时间范围
|
||||
@ -286,7 +284,7 @@ export default {
|
||||
params.startDate = this.dateRange[0];
|
||||
params.endDate = this.dateRange[1];
|
||||
}
|
||||
|
||||
|
||||
request({
|
||||
url: '/tool/banma/orders/all',
|
||||
method: 'get',
|
||||
@ -300,10 +298,10 @@ export default {
|
||||
this.currentPage = 1;
|
||||
this.totalRecords = response.data.total || 0;
|
||||
this.totalPages = response.data.totalPages || 1;
|
||||
|
||||
|
||||
this.updateProgress();
|
||||
this.saveToCache();
|
||||
|
||||
|
||||
if (this.hasMore) {
|
||||
this.autoLoadNextPage();
|
||||
} else {
|
||||
@ -323,11 +321,11 @@ export default {
|
||||
this.$message.error("获取订单失败: " + (error.message || error));
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/** 自动加载下一页 */
|
||||
autoLoadNextPage() {
|
||||
if (!this.hasMore || !this.loading) return;
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
// 准备请求参数,添加时间范围
|
||||
const params = { page: this.nextPage };
|
||||
@ -335,7 +333,7 @@ export default {
|
||||
params.startDate = this.dateRange[0];
|
||||
params.endDate = this.dateRange[1];
|
||||
}
|
||||
|
||||
|
||||
request({
|
||||
url: '/tool/banma/orders/next',
|
||||
method: 'get',
|
||||
@ -343,15 +341,23 @@ export default {
|
||||
timeout: 99999999
|
||||
}).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.orderData = [...this.orderData, ...response.data.orders];
|
||||
// 添加当前页数据
|
||||
this.orderData = [...this.orderData, ...(response.data.orders || [])];
|
||||
this.hasMore = response.data.hasMore;
|
||||
this.nextPage = response.data.nextPage;
|
||||
this.currentPage++;
|
||||
|
||||
this.totalPages = response.data.totalPages || this.totalPages;
|
||||
|
||||
this.updateProgress();
|
||||
this.saveToCache();
|
||||
|
||||
if (this.hasMore) {
|
||||
|
||||
console.log(`已加载第${this.currentPage}页,共${this.totalPages}页,hasMore=${this.hasMore}`);
|
||||
|
||||
// 如果hasMore为false,但当前页小于总页数,强制继续加载
|
||||
if (!this.hasMore && this.currentPage < this.totalPages) {
|
||||
this.hasMore = true;
|
||||
this.autoLoadNextPage();
|
||||
} else if (this.hasMore) {
|
||||
this.autoLoadNextPage();
|
||||
} else {
|
||||
this.loading = false;
|
||||
@ -362,33 +368,46 @@ export default {
|
||||
this.$message.error(response.msg || "获取更多订单失败");
|
||||
}
|
||||
}).catch(error => {
|
||||
this.loading = false;
|
||||
this.$message.error("获取更多订单失败: " + (error.message || error));
|
||||
|
||||
this.nextPage++;
|
||||
this.currentPage++;
|
||||
|
||||
// 更新进度条,即使加载失败也显示进度
|
||||
this.updateProgress();
|
||||
this.saveToCache();
|
||||
|
||||
// 如果还有更多页,继续尝试加载
|
||||
if (this.currentPage < this.totalPages) {
|
||||
this.$message.warning(`加载第${this.currentPage}页失败,尝试继续加载后续页面`);
|
||||
this.autoLoadNextPage();
|
||||
} else {
|
||||
this.loading = false;
|
||||
this.$message.error("获取更多订单失败: " + (error.message || error));
|
||||
}
|
||||
});
|
||||
}, 300);
|
||||
},
|
||||
|
||||
|
||||
/** 更新进度条 */
|
||||
updateProgress() {
|
||||
let percentage;
|
||||
if (this.totalRecords > 0) {
|
||||
percentage = Math.min(Math.round((this.orderData.length / this.totalRecords) * 100), 99);
|
||||
} else {
|
||||
} else if (this.totalPages > 1) {
|
||||
percentage = Math.min(Math.round((this.currentPage / this.totalPages) * 100), 99);
|
||||
} else {
|
||||
percentage = this.currentPage > 0 ? 50 : 10;
|
||||
}
|
||||
|
||||
|
||||
this.loadProgress = {
|
||||
current: this.currentPage,
|
||||
total: this.totalPages,
|
||||
percentage: percentage
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
/** 进度条格式化 */
|
||||
progressFormat(percentage) {
|
||||
return `已加载 ${this.currentPage}/${this.totalPages} 页,${this.orderData.length}/${this.totalRecords} 条数据`;
|
||||
},
|
||||
|
||||
|
||||
/** 导出Excel */
|
||||
handleExport() {
|
||||
if (!this.orderData.length) {
|
||||
@ -397,7 +416,7 @@ export default {
|
||||
}
|
||||
|
||||
this.$message.info("正在准备导出数据,请稍候...");
|
||||
|
||||
|
||||
// 创建工作簿
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
workbook.creator = 'RuoYi Admin';
|
||||
@ -428,9 +447,9 @@ export default {
|
||||
{ header: '日本物流单号', key: 'internationalTrackingNumber', width: 18 },
|
||||
{ header: '地址状态', key: 'trackInfo', width: 25 }
|
||||
];
|
||||
|
||||
|
||||
worksheet.columns = columns;
|
||||
|
||||
|
||||
// 设置表头样式
|
||||
worksheet.getRow(1).eachCell(cell => {
|
||||
cell.font = { bold: true };
|
||||
@ -439,15 +458,15 @@ export default {
|
||||
pattern: 'solid',
|
||||
fgColor: { argb: 'FFEEEEEE' }
|
||||
};
|
||||
cell.alignment = {
|
||||
vertical: 'middle',
|
||||
cell.alignment = {
|
||||
vertical: 'middle',
|
||||
horizontal: 'center',
|
||||
wrapText: true
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
worksheet.getRow(1).height = 22;
|
||||
|
||||
|
||||
// 添加数据行
|
||||
this.orderData.forEach((item, index) => {
|
||||
const rowData = {
|
||||
@ -469,20 +488,20 @@ export default {
|
||||
internationalTrackingNumber: item.internationalTrackingNumber || '',
|
||||
trackInfo: item.trackInfo || '暂无物流信息'
|
||||
};
|
||||
|
||||
|
||||
const row = worksheet.addRow(rowData);
|
||||
row.height = 60; // 设置行高,给图片留出空间
|
||||
|
||||
|
||||
// 设置所有单元格居中对齐
|
||||
row.eachCell(cell => {
|
||||
cell.alignment = {
|
||||
vertical: 'middle',
|
||||
cell.alignment = {
|
||||
vertical: 'middle',
|
||||
horizontal: 'center',
|
||||
wrapText: true
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// 处理图片
|
||||
const processImages = async () => {
|
||||
const loadImage = (url) => {
|
||||
@ -491,32 +510,32 @@ export default {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 检查是否是代理URL,如果不是,转换为代理URL
|
||||
let imageUrl = url;
|
||||
if (!url.includes('/tool/banma/image-proxy')) {
|
||||
imageUrl = process.env.VUE_APP_BASE_API + '/tool/banma/image-proxy?url=' + encodeURIComponent(url);
|
||||
}
|
||||
|
||||
|
||||
const img = new Image();
|
||||
img.crossOrigin = "Anonymous";
|
||||
|
||||
|
||||
img.onload = () => {
|
||||
try {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
|
||||
// 设置合适的尺寸
|
||||
const size = 60;
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
|
||||
|
||||
// 绘制图片保持比例
|
||||
const scale = Math.min(size / img.width, size / img.height);
|
||||
const x = (size - img.width * scale) / 2;
|
||||
const y = (size - img.height * scale) / 2;
|
||||
ctx.drawImage(img, x, y, img.width * scale, img.height * scale);
|
||||
|
||||
|
||||
// 获取Base64
|
||||
const base64 = canvas.toDataURL('image/png');
|
||||
resolve(base64);
|
||||
@ -525,12 +544,12 @@ export default {
|
||||
resolve(null);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
img.onerror = () => resolve(null);
|
||||
img.src = imageUrl;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
for (let i = 0; i < this.orderData.length; i++) {
|
||||
const item = this.orderData[i];
|
||||
if (item.productImage) {
|
||||
@ -541,7 +560,7 @@ export default {
|
||||
base64: base64,
|
||||
extension: 'png',
|
||||
});
|
||||
|
||||
|
||||
// 将图片添加到单元格
|
||||
worksheet.addImage(imageId, {
|
||||
tl: { col: 1, row: i + 1 },
|
||||
@ -555,17 +574,17 @@ export default {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 处理图片并导出
|
||||
processImages().then(() => {
|
||||
// 导出文件
|
||||
const fileName = this.dateRange && this.dateRange.length === 2
|
||||
const fileName = this.dateRange && this.dateRange.length === 2
|
||||
? `斑马订单数据_${this.dateRange[0]}_${this.dateRange[1]}.xlsx`
|
||||
: `斑马订单数据_${parseTime(new Date())}.xlsx`;
|
||||
|
||||
|
||||
workbook.xlsx.writeBuffer().then(buffer => {
|
||||
const blob = new Blob([buffer], {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
const blob = new Blob([buffer], {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
});
|
||||
saveAs(blob, fileName);
|
||||
this.$message.success("导出成功");
|
||||
@ -575,11 +594,11 @@ export default {
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/** 备用导出方法(不含图片) */
|
||||
fallbackExport() {
|
||||
this.$message.warning("图片导出失败,将导出不含图片的数据");
|
||||
|
||||
|
||||
// 准备表头和数据
|
||||
const headers = [
|
||||
'下单时间', '商品名称', '乐天订单号', '下单距今时间', '乐天订单金额/日元',
|
||||
@ -587,7 +606,7 @@ export default {
|
||||
'采购金额/rmb', '国际运费/rmb', '国内物流公司', '国内物流单号', '日本物流单号',
|
||||
'地址状态'
|
||||
];
|
||||
|
||||
|
||||
const data = this.orderData.map(item => [
|
||||
item.orderedAt || '',
|
||||
item.productTitle || '',
|
||||
@ -606,46 +625,46 @@ export default {
|
||||
item.internationalTrackingNumber || '',
|
||||
item.trackInfo || '暂无物流信息'
|
||||
]);
|
||||
|
||||
|
||||
// 创建工作簿并导出
|
||||
const wb = XLSX.utils.book_new();
|
||||
const ws = XLSX.utils.aoa_to_sheet([headers, ...data]);
|
||||
|
||||
|
||||
// 设置列宽和样式
|
||||
const colWidths = [15, 18, 18, 25, 15, 12, 10, 12, 15, 15, 12, 15, 15, 15, 12, 25];
|
||||
ws['!cols'] = colWidths.map(width => ({ width }));
|
||||
|
||||
|
||||
// 设置行高
|
||||
const rowHeights = [22]; // 表头高度
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
rowHeights.push(20); // 数据行高度
|
||||
}
|
||||
ws['!rows'] = rowHeights.map(height => ({ hpt: height }));
|
||||
|
||||
|
||||
// 设置所有单元格居中对齐
|
||||
const range = XLSX.utils.decode_range(ws['!ref']);
|
||||
for (let row = range.s.r; row <= range.e.r; row++) {
|
||||
for (let col = range.s.c; col <= range.e.c; col++) {
|
||||
const cellAddress = XLSX.utils.encode_cell({ r: row, c: col });
|
||||
if (!ws[cellAddress]) continue;
|
||||
|
||||
|
||||
if (!ws[cellAddress].s) ws[cellAddress].s = {};
|
||||
ws[cellAddress].s.alignment = { horizontal: 'center', vertical: 'center' };
|
||||
|
||||
|
||||
if (row === 0) {
|
||||
ws[cellAddress].s.font = { bold: true };
|
||||
ws[cellAddress].s.fill = { fgColor: { rgb: "EEEEEE" } };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
XLSX.utils.book_append_sheet(wb, ws, '订单数据');
|
||||
|
||||
|
||||
// 导出文件
|
||||
const fileName = this.dateRange && this.dateRange.length === 2
|
||||
const fileName = this.dateRange && this.dateRange.length === 2
|
||||
? `斑马订单数据_${this.dateRange[0]}_${this.dateRange[1]}_无图片.xlsx`
|
||||
: `斑马订单数据_${parseTime(new Date())}_无图片.xlsx`;
|
||||
|
||||
|
||||
XLSX.writeFile(wb, fileName);
|
||||
this.$message.success("导出成功");
|
||||
},
|
||||
@ -655,11 +674,11 @@ export default {
|
||||
*/
|
||||
handleImageError(event, item) {
|
||||
if (!item.productImage || item.imageProxied) return;
|
||||
|
||||
|
||||
item.imageProxied = true;
|
||||
const proxyUrl = process.env.VUE_APP_BASE_API + '/tool/banma/image-proxy?url=' +
|
||||
const proxyUrl = process.env.VUE_APP_BASE_API + '/tool/banma/image-proxy?url=' +
|
||||
encodeURIComponent(item.productImage);
|
||||
|
||||
|
||||
item.originalImage = item.productImage;
|
||||
item.productImage = proxyUrl;
|
||||
},
|
||||
@ -670,7 +689,7 @@ export default {
|
||||
request({
|
||||
url: '/tool/banma/refresh-token',
|
||||
method: 'get',
|
||||
timeout: 60000
|
||||
timeout: 99999999
|
||||
}).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.$message.success("Token刷新成功");
|
||||
@ -841,4 +860,4 @@ export default {
|
||||
.el-image {
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -671,7 +671,7 @@ export default {
|
||||
url: '/tool/webmagic/batch',
|
||||
method: 'post',
|
||||
data: currentBatch,
|
||||
timeout: 9000000
|
||||
timeout: 99999999
|
||||
}).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.productData = [...this.productData, ...response.data];
|
||||
@ -735,7 +735,7 @@ export default {
|
||||
url: '/tool/webmagic/batch',
|
||||
method: 'post',
|
||||
data: failedAsins,
|
||||
timeout: 99999000
|
||||
timeout: 99999999
|
||||
}).then(response => {
|
||||
if (response.code === 200 && response.data) {
|
||||
// 更新失败的ASIN数据
|
||||
|
Loading…
x
Reference in New Issue
Block a user