493 lines
12 KiB
Vue
493 lines
12 KiB
Vue
<template>
|
||
<view class="container">
|
||
<!-- 顶部导航栏 -->
|
||
<view class="navbar">
|
||
<view class="nav-left" @click="goBack">
|
||
<text class="iconfont">←</text>
|
||
</view>
|
||
<view class="nav-title">批量导入</view>
|
||
<view class="nav-right">
|
||
<text class="iconfont">···</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 选项卡切换 -->
|
||
<view class="tab-bar">
|
||
<view class="tab-item" :class="{active: activeTab === 'import'}" @click="activeTab = 'import'">
|
||
批量导入
|
||
</view>
|
||
<view class="tab-item" :class="{active: activeTab === 'record'}" @click="activeTab = 'record'">
|
||
导入记录
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 批量导入内容区 -->
|
||
<view v-if="activeTab === 'import'" class="content">
|
||
<!-- 方式1:导入进货单【核心带完整上传功能】 -->
|
||
<view class="card">
|
||
<view class="card-header">
|
||
<view class="method-tag">方式1</view>
|
||
<view class="method-title">导入进货单</view>
|
||
</view>
|
||
<view class="upload-area" @click="pickFile" v-if="!uploadFileName">
|
||
<view class="upload-icon">
|
||
<text class="iconfont">↥</text>
|
||
</view>
|
||
<view class="upload-text">点击上传文件</view>
|
||
<view class="upload-desc">(仅支持10M以下,以xls、xlsx或csv结尾的文件类型)</view>
|
||
</view>
|
||
<!-- 已选择文件展示 -->
|
||
<view class="file-selected" v-else>
|
||
<view class="file-name">{{uploadFileName}}</view>
|
||
<view class="file-opt">
|
||
<text class="reupload" @click="pickFile">重新选择</text>
|
||
<text class="cancel" @click="cancelFile">取消</text>
|
||
</view>
|
||
</view>
|
||
<!-- 导入按钮 -->
|
||
<view class="import-btn-box" v-if="uploadFileName">
|
||
<button class="import-btn" :loading="isUploading" @click="uploadAndImport">开始导入商品</button>
|
||
</view>
|
||
<view class="desc-list">
|
||
<text class="desc-item">1. 可导入:供应商进货单、其他收银系统的商品导出明细</text>
|
||
<text class="desc-item">2. 支持录入条形码、商品名称、零售价、库存、进货价、商品品牌</text>
|
||
<text class="desc-item">3. 导入成功后会在原商品基础上增加库存,不会覆盖原库存</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 方式2:通过电脑端录入 -->
|
||
<view class="card">
|
||
<view class="card-header">
|
||
<view class="method-tag">方式2</view>
|
||
<view class="method-title">通过电脑端录入</view>
|
||
</view>
|
||
<view class="card-content">
|
||
<view class="content-text">支持导入更大文件、更多商品数、商品信息</view>
|
||
<view class="link-area" @click="openLink">
|
||
<text class="link-text">请在电脑端访问 http://e.weidian.com/main</text>
|
||
<text class="iconfont">↗</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 方式3:微店商品快速录入 -->
|
||
<view class="card">
|
||
<view class="card-header">
|
||
<view class="method-tag">方式3</view>
|
||
<view class="method-title">微店商品快速录入</view>
|
||
</view>
|
||
<view class="card-content">
|
||
<view class="content-text">如果您没有电脑,可以试试我们的快捷设置功能</view>
|
||
<view class="link-area" @click="goQuickSetup">
|
||
<text class="link-text">3-20分钟快速设置价格 ></text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 导入记录内容区【补充列表骨架+空数据】 -->
|
||
<view v-else class="record-content">
|
||
<view class="empty-state" v-if="recordList.length == 0">
|
||
<text class="empty-icon">📋</text>
|
||
<text class="empty-text">暂无导入记录</text>
|
||
<text class="empty-subtext">导入商品后,记录将展示在这里</text>
|
||
</view>
|
||
<view class="record-list" v-else>
|
||
<view class="record-item" v-for="(item,index) in recordList" :key="index">
|
||
<view class="record-name">{{item.fileName}}</view>
|
||
<view class="record-info">
|
||
<text class="record-time">{{item.createTime}}</text>
|
||
<text class="record-status" :class="item.status">{{item.statusText}}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
// 导入商品相关API
|
||
import { importProductData, getImportRecord } from '@/api/product';
|
||
// 导入门店ID获取工具
|
||
import { getStoreId } from '@/utils/auth';
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
activeTab: 'import', // 默认选中批量导入
|
||
uploadFilePath: '', // 选中的文件临时路径
|
||
uploadFileName: '', // 选中的文件名称
|
||
isUploading: false, // 是否正在上传/导入中
|
||
recordList: [] // 导入记录列表
|
||
}
|
||
},
|
||
onShow() {
|
||
// 每次进入页面刷新导入记录
|
||
if(this.activeTab == 'record'){
|
||
this.getImportRecord();
|
||
}
|
||
},
|
||
methods: {
|
||
// 返回上一页
|
||
goBack() {
|
||
uni.navigateBack({ delta: 1 });
|
||
},
|
||
|
||
// ========== 核心:选择文件并校验 ==========
|
||
pickFile() {
|
||
uni.chooseFile({
|
||
count: 1, // 只允许选择1个文件
|
||
type: 'file',
|
||
sizeLimit: 10 * 1024 * 1024, // 限制10M以内,和页面提示一致
|
||
success: (res) => {
|
||
const tempFile = res.tempFiles[0];
|
||
const fileName = tempFile.name;
|
||
const fileSize = tempFile.size;
|
||
|
||
// 1. 文件大小校验
|
||
if (fileSize > 10 * 1024 * 1024) {
|
||
return uni.showToast({ title: '文件大小不能超过10M', icon: 'none', duration: 2000 });
|
||
}
|
||
|
||
// 2. 文件格式校验 (严格匹配xls/xlsx/csv)
|
||
const suffix = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
|
||
const allowSuffix = ['.xls', '.xlsx', '.csv'];
|
||
if (!allowSuffix.includes(suffix)) {
|
||
return uni.showToast({ title: '仅支持xls、xlsx、csv格式文件', icon: 'none', duration: 2000 });
|
||
}
|
||
|
||
// 校验通过,赋值文件信息
|
||
this.uploadFilePath = tempFile.path;
|
||
this.uploadFileName = fileName;
|
||
uni.showToast({ title: '文件选择成功', icon: 'success', duration: 1500 });
|
||
},
|
||
fail: () => {
|
||
uni.showToast({ title: '文件选择失败,请重新选择', icon: 'none' });
|
||
}
|
||
});
|
||
},
|
||
|
||
// 取消已选择的文件
|
||
cancelFile() {
|
||
this.uploadFilePath = '';
|
||
this.uploadFileName = '';
|
||
},
|
||
|
||
// ========== 核心:文件上传+商品导入 主逻辑 ✅ 已对接你的真实接口 ✅ ==========
|
||
uploadAndImport() {
|
||
if (!this.uploadFilePath) return;
|
||
this.isUploading = true; // 开启加载状态,防止重复点击
|
||
|
||
// 准备表单数据
|
||
const formData = {
|
||
storeId: getStoreId()
|
||
};
|
||
|
||
// 调用API进行商品导入
|
||
importProductData(this.uploadFilePath, formData)
|
||
.then(res => {
|
||
this.isUploading = false;
|
||
|
||
// 业务成功:导入成功 匹配接口 code=200
|
||
if (res.code === 200) {
|
||
uni.showToast({
|
||
title: `导入成功!共导入 ${res.data} 个商品`,
|
||
icon: 'success',
|
||
duration: 2500
|
||
});
|
||
// 重置文件选择状态
|
||
this.cancelFile();
|
||
}
|
||
// 业务失败:导入失败
|
||
else {
|
||
uni.showModal({
|
||
title: '导入失败',
|
||
content: res.msg || '文件内容有误,请检查后重新上传',
|
||
showCancel: false,
|
||
confirmText: '知道了'
|
||
});
|
||
}
|
||
})
|
||
.catch(err => {
|
||
this.isUploading = false;
|
||
console.error('文件上传失败:', err);
|
||
uni.showModal({
|
||
title: '上传异常',
|
||
content: '网络异常或服务器连接失败,请检查接口服务后重试',
|
||
showCancel: false,
|
||
confirmText: '重新上传'
|
||
});
|
||
});
|
||
},
|
||
|
||
// 打开电脑端链接
|
||
openLink() {
|
||
uni.showModal({
|
||
title: '温馨提示',
|
||
content: '将在浏览器中打开电脑端录入地址',
|
||
confirmText: '确认打开',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.openURL({
|
||
url: 'http://e.weidian.com/main',
|
||
fail: () => uni.showToast({ title: '打开失败,请手动复制链接', icon: 'none' })
|
||
});
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
// 跳转快速设置页面
|
||
goQuickSetup() {
|
||
uni.navigateTo({
|
||
url: '/pages/quick-setup/quick-setup'
|
||
});
|
||
},
|
||
|
||
// 获取导入记录
|
||
getImportRecord() {
|
||
uni.showLoading({ title: '加载中...', mask: true });
|
||
|
||
// 调用API获取导入记录
|
||
getImportRecord()
|
||
.then(res => {
|
||
if(res.code == 200){
|
||
this.recordList = res.data || [];
|
||
}
|
||
})
|
||
.catch(err => {
|
||
console.error('获取导入记录失败:', err);
|
||
})
|
||
.finally(() => {
|
||
uni.hideLoading();
|
||
});
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* 全局样式重置 */
|
||
.container {
|
||
background-color: #f5f5f5;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
/* 导航栏 */
|
||
.navbar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
background-color: #e62318;
|
||
color: white;
|
||
padding: 20rpx 30rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
.nav-left, .nav-right {
|
||
width: 60rpx;
|
||
font-size: 32rpx;
|
||
}
|
||
.nav-title {
|
||
font-size: 34rpx;
|
||
font-weight: bold;
|
||
}
|
||
|
||
/* 选项卡 */
|
||
.tab-bar {
|
||
display: flex;
|
||
background-color: white;
|
||
}
|
||
.tab-item {
|
||
flex: 1;
|
||
text-align: center;
|
||
padding: 30rpx 0;
|
||
font-size: 32rpx;
|
||
color: #333;
|
||
border-bottom: 4rpx solid transparent;
|
||
}
|
||
.tab-item.active {
|
||
color: #e62318;
|
||
border-bottom-color: #e62318;
|
||
font-weight: bold;
|
||
}
|
||
|
||
/* 内容区 */
|
||
.content {
|
||
padding: 30rpx;
|
||
}
|
||
|
||
/* 卡片通用样式 */
|
||
.card {
|
||
background-color: white;
|
||
border-radius: 16rpx;
|
||
margin-bottom: 30rpx;
|
||
overflow: hidden;
|
||
}
|
||
.card-header {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 30rpx;
|
||
background-color: white;
|
||
}
|
||
.method-tag {
|
||
background-color: #e62318;
|
||
color: white;
|
||
font-size: 24rpx;
|
||
padding: 8rpx 16rpx;
|
||
border-radius: 12rpx;
|
||
margin-right: 20rpx;
|
||
}
|
||
.method-title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
/* 上传区域 */
|
||
.upload-area {
|
||
margin: 0 30rpx 30rpx;
|
||
border: 2rpx dashed #ccc;
|
||
border-radius: 12rpx;
|
||
padding: 60rpx 30rpx;
|
||
text-align: center;
|
||
}
|
||
.upload-icon {
|
||
font-size: 48rpx;
|
||
color: #999;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
.upload-text {
|
||
font-size: 30rpx;
|
||
color: #333;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
.upload-desc {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
|
||
/* 已选择文件样式 */
|
||
.file-selected {
|
||
margin: 0 30rpx 30rpx;
|
||
padding: 20rpx;
|
||
background: #f9f9f9;
|
||
border-radius: 12rpx;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
.file-name {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
flex: 1;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.file-opt {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
}
|
||
.reupload {
|
||
font-size: 26rpx;
|
||
color: #e62318;
|
||
}
|
||
.cancel {
|
||
font-size: 26rpx;
|
||
color: #999;
|
||
}
|
||
|
||
/* 导入按钮样式 */
|
||
.import-btn-box {
|
||
padding: 0 30rpx 20rpx;
|
||
}
|
||
.import-btn {
|
||
width: 100%;
|
||
background: #e62318;
|
||
color: white;
|
||
font-size: 30rpx;
|
||
padding: 20rpx 0;
|
||
border-radius: 12rpx;
|
||
}
|
||
|
||
/* 描述列表 */
|
||
.desc-list {
|
||
padding: 0 30rpx 30rpx;
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
line-height: 1.6;
|
||
}
|
||
.desc-item {
|
||
display: block;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
/* 卡片内容区 */
|
||
.card-content {
|
||
padding: 0 30rpx 30rpx;
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
line-height: 1.5;
|
||
}
|
||
.content-text {
|
||
margin-bottom: 20rpx;
|
||
display: block;
|
||
}
|
||
.link-area {
|
||
display: flex;
|
||
align-items: center;
|
||
color: #e62318;
|
||
}
|
||
.link-text {
|
||
font-size: 28rpx;
|
||
margin-right: 10rpx;
|
||
}
|
||
|
||
/* 导入记录样式 */
|
||
.record-content {
|
||
padding: 30rpx;
|
||
}
|
||
.empty-state {
|
||
text-align: center;
|
||
padding: 100rpx 0;
|
||
color: #999;
|
||
}
|
||
.empty-icon {
|
||
font-size: 60rpx;
|
||
display: block;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
.empty-text {
|
||
font-size: 30rpx;
|
||
margin-bottom: 10rpx;
|
||
display: block;
|
||
}
|
||
.empty-subtext {
|
||
font-size: 24rpx;
|
||
}
|
||
.record-list {
|
||
background: white;
|
||
border-radius: 16rpx;
|
||
}
|
||
.record-item {
|
||
padding: 30rpx;
|
||
border-bottom: 1rpx solid #f5f5f5;
|
||
}
|
||
.record-name {
|
||
font-size: 30rpx;
|
||
color: #333;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
.record-info {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
.record-status.success {
|
||
color: #07c160;
|
||
}
|
||
.record-status.fail {
|
||
color: #e62318;
|
||
}
|
||
</style> |