ry_app/pages/import/import.vue

493 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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>