generated from Leo_Ding/web-template
286 lines
6.3 KiB
Vue
286 lines
6.3 KiB
Vue
<template>
|
||
<!-- 绑定本地visible变量 -->
|
||
<a-modal
|
||
v-model="localVisible"
|
||
title="批量导入"
|
||
:maskClosable="false"
|
||
@cancel="handleCancel"
|
||
:footer="null"
|
||
width="500px"
|
||
>
|
||
<!-- 原有内容不变 -->
|
||
<div class="import-container">
|
||
<div class="import-section" :class="{ 'section-top': true }">
|
||
<h3 class="section-title">
|
||
<span class="required-mark">*</span>下载模板填写表格信息
|
||
</h3>
|
||
<p class="section-desc">
|
||
请按照数据模板格式准备数据,模板中的表头名称不可更改,表头行不能删除,单次导入的数据不超过1000条。
|
||
</p>
|
||
|
||
<div class="file-info">
|
||
<a-icon type="file-excel-o" class="file-icon" theme="outlined" />
|
||
<div class="file-detail">
|
||
<p class="file-format">支持格式:.xlsx .xls</p>
|
||
<a-button
|
||
type="primary"
|
||
class="action-btn"
|
||
@click="handleDownloadTemplate"
|
||
>
|
||
下载模板
|
||
</a-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="import-section upload-section" :class="{ 'section-bottom': true }">
|
||
<h3 class="section-title">
|
||
<span class="required-mark">*</span>上传填好的表格信息
|
||
</h3>
|
||
<p class="section-desc">
|
||
文件后缀名必须为xls或xlsx(即Excel格式),文件大小不得超过10M
|
||
</p>
|
||
|
||
<div class="file-info">
|
||
<a-icon type="file-excel-o" class="file-icon" theme="filled" />
|
||
<div class="file-detail">
|
||
<p class="file-format">支持格式:.xlsx .xls</p>
|
||
<a-upload
|
||
:showUploadList="false"
|
||
:beforeUpload="beforeUpload"
|
||
:customRequest="handleFileUpload"
|
||
>
|
||
<a-button
|
||
type="primary"
|
||
class="action-btn"
|
||
>
|
||
选择表格
|
||
</a-button>
|
||
</a-upload>
|
||
|
||
<div v-if="selectedFile" class="selected-file">
|
||
<span class="file-name">{{ selectedFile.name }}</span>
|
||
<a-icon
|
||
type="close-circle"
|
||
class="remove-icon"
|
||
@click="clearSelectedFile"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modal-footer">
|
||
<a-button @click="handleCancel">取消</a-button>
|
||
<a-button
|
||
type="primary"
|
||
@click="handleConfirmUpload"
|
||
:disabled="!selectedFile"
|
||
>
|
||
上传
|
||
</a-button>
|
||
</div>
|
||
</a-modal>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, defineProps, defineEmits, watch } from 'vue';
|
||
import { message } from 'ant-design-vue';
|
||
|
||
// 1. 定义接收父组件的prop(遵循v-model默认命名规范)
|
||
const props = defineProps({
|
||
modelValue: { // 注意这里改名为modelValue,这是v-model的默认绑定名
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
});
|
||
|
||
// 2. 定义事件(通知父组件更新值)
|
||
const emit = defineEmits(['update:modelValue', 'upload', 'download']);
|
||
|
||
// 3. 创建本地变量,用于模态框的v-model绑定
|
||
const localVisible = ref(props.modelValue);
|
||
const selectedFile = ref(null);
|
||
|
||
// 4. 监听父组件传入的modelValue变化,同步到本地变量
|
||
watch(
|
||
() => props.modelValue,
|
||
(newVal) => {
|
||
localVisible.value = newVal;
|
||
}
|
||
);
|
||
|
||
// 5. 监听本地变量变化,通知父组件(实现双向绑定)
|
||
watch(
|
||
() => localVisible.value,
|
||
(newVal) => {
|
||
emit('update:modelValue', newVal);
|
||
}
|
||
);
|
||
|
||
// 处理取消
|
||
const handleCancel = () => {
|
||
localVisible.value = false; // 修改本地变量
|
||
selectedFile.value = null;
|
||
};
|
||
|
||
// 处理下载模板
|
||
const handleDownloadTemplate = () => {
|
||
emit('download');
|
||
};
|
||
|
||
// 上传前的校验
|
||
const beforeUpload = (file) => {
|
||
const isExcel = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||
|| file.type === 'application/vnd.ms-excel';
|
||
if (!isExcel) {
|
||
message.error('请上传Excel格式的文件(.xls或.xlsx)');
|
||
return false;
|
||
}
|
||
|
||
const isLt10M = file.size / 1024 / 1024 < 10;
|
||
if (!isLt10M) {
|
||
message.error('文件大小不能超过10M');
|
||
return false;
|
||
}
|
||
|
||
selectedFile.value = file;
|
||
return false;
|
||
};
|
||
|
||
// 处理文件上传
|
||
const handleFileUpload = () => {
|
||
// 自定义上传逻辑
|
||
};
|
||
|
||
// 清除选中的文件
|
||
const clearSelectedFile = () => {
|
||
selectedFile.value = null;
|
||
};
|
||
|
||
// 确认上传
|
||
const handleConfirmUpload = () => {
|
||
if (selectedFile.value) {
|
||
emit('upload', selectedFile.value);
|
||
localVisible.value = false; // 上传后关闭模态框
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.import-container {
|
||
background-color: #ffffff;
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.import-section {
|
||
padding: 16px 16px;
|
||
background-color: #fafafa;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.import-section.section-top {
|
||
/* border-bottom: 1px solid #e8e8e8; */
|
||
margin-bottom: 8px;
|
||
border-radius: 4px 4px 0 0;
|
||
}
|
||
|
||
.import-section.section-bottom {
|
||
border-radius: 0 0 4px 4px;
|
||
}
|
||
|
||
.section-title {
|
||
margin: 0 0 8px 0;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
color: rgba(0, 0, 0, 0.85);
|
||
}
|
||
|
||
.required-mark {
|
||
color: #ff4d4f;
|
||
margin-right: 4px;
|
||
}
|
||
|
||
.section-desc {
|
||
margin: 0 0 16px 0;
|
||
font-size: 12px;
|
||
color: rgba(0, 0, 0, 0.65);
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.file-info {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 12px;
|
||
/* background-color: #f5f5f5; */
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.file-icon {
|
||
font-size: 48px;
|
||
color: #00b42a;
|
||
margin-right: 16px;
|
||
}
|
||
|
||
.file-detail {
|
||
flex: 1;
|
||
}
|
||
|
||
.file-format {
|
||
margin: 0 0 8px 0;
|
||
font-size: 12px;
|
||
color: rgba(0, 0, 0, 0.75);
|
||
}
|
||
|
||
.action-btn {
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.selected-file {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 12px;
|
||
color: rgba(0, 0, 0, 0.9);
|
||
max-width: 300px;
|
||
padding: 4px 0;
|
||
}
|
||
|
||
.file-name {
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
margin-right: 8px;
|
||
background-color: #fff;
|
||
padding: 2px 6px;
|
||
border-radius: 2px;
|
||
border: 1px solid #e8e8e8;
|
||
}
|
||
|
||
.remove-icon {
|
||
color: rgba(0, 0, 0, 0.45);
|
||
cursor: pointer;
|
||
transition: color 0.3s;
|
||
}
|
||
|
||
.remove-icon:hover {
|
||
color: #ff4d4f;
|
||
}
|
||
|
||
.modal-footer {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
padding: 16px;
|
||
border-top: 1px solid #f0f0f0;
|
||
margin-top: 8px;
|
||
}
|
||
|
||
.modal-footer > button:not(:last-child) {
|
||
margin-right: 8px;
|
||
}
|
||
</style>
|
||
|
||
|
||
|