2026-01-15 18:16:50 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="excel-import-management">
|
|
|
|
|
|
<!-- 页面头部 -->
|
|
|
|
|
|
<div class="page-header">
|
|
|
|
|
|
<div class="header-content">
|
|
|
|
|
|
<h1>📊 Excel数据导入系统</h1>
|
|
|
|
|
|
<p>管理员专用 - {{ currentLotteryTypeName }}Excel文件数据批量导入</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 权限检查中 -->
|
|
|
|
|
|
<div v-if="permissionChecking" class="permission-checking">
|
|
|
|
|
|
<div class="checking-content">
|
|
|
|
|
|
<div class="loading-spinner"></div>
|
|
|
|
|
|
<p>正在验证权限...</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 主要内容 - 只有有权限时才显示 -->
|
|
|
|
|
|
<div v-else-if="hasPermission" class="import-container">
|
|
|
|
|
|
<!-- 功能区域 -->
|
|
|
|
|
|
<div class="function-area">
|
|
|
|
|
|
<el-row :gutter="20">
|
|
|
|
|
|
<!-- 完整数据导入 -->
|
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
|
<el-card class="function-card">
|
|
|
|
|
|
<template #header>
|
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
|
<el-icon><Document /></el-icon>
|
|
|
|
|
|
<span>完整数据导入</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="card-desc">
|
|
|
|
|
|
<p>上传包含T1-T7工作表的Excel文件,导入红球、蓝球、接续系数和组合系数数据</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="upload-section">
|
|
|
|
|
|
<div class="file-input-container">
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="file"
|
|
|
|
|
|
ref="fullDataFileInput"
|
|
|
|
|
|
@change="handleFileSelect($event, 'fullData')"
|
|
|
|
|
|
accept=".xlsx,.xls"
|
|
|
|
|
|
class="file-input"
|
|
|
|
|
|
id="fullDataFile"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<label for="fullDataFile" class="file-label">
|
|
|
|
|
|
<el-icon class="file-icon"><FolderOpened /></el-icon>
|
|
|
|
|
|
<span class="file-text">
|
|
|
|
|
|
{{ fullDataFile ? fullDataFile.name : '选择Excel文件(包含T1-T7工作表)' }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</label>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
@click="uploadFullData"
|
|
|
|
|
|
:disabled="!fullDataFile || fullDataUploading"
|
|
|
|
|
|
:loading="fullDataUploading"
|
|
|
|
|
|
style="width: 100%; margin-top: 16px"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ fullDataUploading ? '导入中...' : '开始导入' }}
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
|
|
|
|
|
|
<div v-if="fullDataResult" class="result-message" :class="fullDataResult.type">
|
|
|
|
|
|
{{ fullDataResult.message }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 开奖数据导入(覆盖) -->
|
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
|
<el-card class="function-card">
|
|
|
|
|
|
<template #header>
|
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
|
<el-icon><Warning /></el-icon>
|
|
|
|
|
|
<span>开奖数据导入(覆盖)</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="card-desc">
|
|
|
|
|
|
<p>上传包含T10工作表的Excel文件,清空并重新导入开奖数据</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="upload-section">
|
|
|
|
|
|
<div class="file-input-container">
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="file"
|
|
|
|
|
|
ref="lotteryFileInput"
|
|
|
|
|
|
@change="handleFileSelect($event, 'lottery')"
|
|
|
|
|
|
accept=".xlsx,.xls"
|
|
|
|
|
|
class="file-input"
|
|
|
|
|
|
id="lotteryFile"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<label for="lotteryFile" class="file-label">
|
|
|
|
|
|
<el-icon class="file-icon"><FolderOpened /></el-icon>
|
|
|
|
|
|
<span class="file-text">
|
|
|
|
|
|
{{ lotteryFile ? lotteryFile.name : '选择Excel文件(包含T10工作表)' }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</label>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
type="warning"
|
|
|
|
|
|
@click="uploadLotteryData"
|
|
|
|
|
|
:disabled="!lotteryFile || lotteryUploading"
|
|
|
|
|
|
:loading="lotteryUploading"
|
|
|
|
|
|
style="width: 100%; margin-top: 16px"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ lotteryUploading ? '导入中...' : '覆盖导入' }}
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
|
|
|
|
|
|
<div v-if="lotteryResult" class="result-message" :class="lotteryResult.type">
|
|
|
|
|
|
{{ lotteryResult.message }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 开奖数据追加 -->
|
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
|
<el-card class="function-card">
|
|
|
|
|
|
<template #header>
|
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
|
<el-icon><Plus /></el-icon>
|
|
|
|
|
|
<span>开奖数据追加</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="card-desc">
|
|
|
|
|
|
<p>上传包含T10工作表的Excel文件,追加导入开奖数据(跳过重复期号)</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="upload-section">
|
|
|
|
|
|
<div class="file-input-container">
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="file"
|
|
|
|
|
|
ref="appendFileInput"
|
|
|
|
|
|
@change="handleFileSelect($event, 'append')"
|
|
|
|
|
|
accept=".xlsx,.xls"
|
|
|
|
|
|
class="file-input"
|
|
|
|
|
|
id="appendFile"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<label for="appendFile" class="file-label">
|
|
|
|
|
|
<el-icon class="file-icon"><FolderOpened /></el-icon>
|
|
|
|
|
|
<span class="file-text">
|
|
|
|
|
|
{{ appendFile ? appendFile.name : '选择Excel文件(包含T10工作表)' }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</label>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
type="success"
|
|
|
|
|
|
@click="appendLotteryData"
|
|
|
|
|
|
:disabled="!appendFile || appendUploading"
|
|
|
|
|
|
:loading="appendUploading"
|
|
|
|
|
|
style="width: 100%; margin-top: 16px"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ appendUploading ? '追加中...' : '追加导入' }}
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
|
|
|
|
|
|
<div v-if="appendResult" class="result-message" :class="appendResult.type">
|
|
|
|
|
|
{{ appendResult.message }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 导入说明 -->
|
|
|
|
|
|
<el-card class="info-card">
|
|
|
|
|
|
<template #header>
|
|
|
|
|
|
<div class="card-header">
|
|
|
|
|
|
<el-icon><InfoFilled /></el-icon>
|
|
|
|
|
|
<span>导入说明</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<div class="info-content">
|
|
|
|
|
|
<div class="info-item">
|
|
|
|
|
|
<h4>📋 完整数据导入:</h4>
|
|
|
|
|
|
<p>• 需要包含T1、T2、T3、T4、T5、T6、T7工作表的Excel文件</p>
|
|
|
|
|
|
<p>• 导入红球、蓝球、接续系数和组合系数数据到相应的数据库表</p>
|
|
|
|
|
|
<p>• 适用于系统初始化或全量数据更新</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="info-item">
|
|
|
|
|
|
<h4>🎯 开奖数据导入(覆盖):</h4>
|
|
|
|
|
|
<p>• 需要包含T10工作表的Excel文件</p>
|
|
|
|
|
|
<p>• 清空lottery_draws表的现有数据,重新导入</p>
|
|
|
|
|
|
<p>• 适用于完全替换开奖数据</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="info-item">
|
|
|
|
|
|
<h4>➕ 开奖数据追加:</h4>
|
|
|
|
|
|
<p>• 需要包含T10工作表的Excel文件</p>
|
|
|
|
|
|
<p>• 保留现有数据,只添加新的开奖记录</p>
|
|
|
|
|
|
<p>• 自动跳过重复的期号,适用于增量更新</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 错误提示弹窗 -->
|
|
|
|
|
|
<div v-if="showErrorModal" class="modal-overlay" @click="hideErrorModal">
|
|
|
|
|
|
<div class="modal-content error-modal" @click.stop>
|
|
|
|
|
|
<h3>❌ 导入失败</h3>
|
|
|
|
|
|
<p>{{ errorMessage }}</p>
|
|
|
|
|
|
<button class="btn btn-primary" @click="hideErrorModal">确定</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import { lotteryApi } from '../../api/index.js'
|
|
|
|
|
|
import { userStore } from '../../store/user.js'
|
|
|
|
|
|
export default {
|
|
|
|
|
|
name: 'AdminExcelImportManagement',
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
// 权限验证
|
|
|
|
|
|
hasPermission: false,
|
|
|
|
|
|
permissionChecking: true,
|
|
|
|
|
|
|
|
|
|
|
|
// 文件对象
|
|
|
|
|
|
fullDataFile: null,
|
|
|
|
|
|
lotteryFile: null,
|
|
|
|
|
|
appendFile: null,
|
|
|
|
|
|
|
|
|
|
|
|
// 上传状态
|
|
|
|
|
|
fullDataUploading: false,
|
|
|
|
|
|
lotteryUploading: false,
|
|
|
|
|
|
appendUploading: false,
|
|
|
|
|
|
|
|
|
|
|
|
// 结果信息
|
|
|
|
|
|
fullDataResult: null,
|
|
|
|
|
|
lotteryResult: null,
|
|
|
|
|
|
appendResult: null,
|
|
|
|
|
|
|
|
|
|
|
|
// 错误处理
|
|
|
|
|
|
showErrorModal: false,
|
|
|
|
|
|
errorMessage: ''
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
computed: {
|
|
|
|
|
|
// 当前彩票类型名称
|
|
|
|
|
|
currentLotteryTypeName() {
|
|
|
|
|
|
const routePath = this.$route.path
|
|
|
|
|
|
if (routePath.includes('/ssq')) {
|
|
|
|
|
|
return '双色球 - '
|
|
|
|
|
|
} else if (routePath.includes('/dlt')) {
|
|
|
|
|
|
return '大乐透 - '
|
|
|
|
|
|
}
|
|
|
|
|
|
return ''
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
async mounted() {
|
|
|
|
|
|
await this.checkPermission()
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
// 检查用户权限
|
|
|
|
|
|
async checkPermission() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await lotteryApi.getLoginUser()
|
|
|
|
|
|
|
|
|
|
|
|
if (response && response.success && response.data) {
|
|
|
|
|
|
const userRole = response.data.userRole
|
|
|
|
|
|
if (userRole === 'admin' || userRole === 'superAdmin') {
|
|
|
|
|
|
this.hasPermission = true
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.showError('无权限访问此页面,仅限管理员或超级管理员使用')
|
|
|
|
|
|
// 3秒后跳转到管理员登录页
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.$router.push('/cpzsadmin/login')
|
|
|
|
|
|
}, 3000)
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.showError('获取用户信息失败,请重新登录')
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.$router.push('/cpzsadmin/login')
|
|
|
|
|
|
}, 3000)
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('权限检查失败:', error)
|
|
|
|
|
|
this.showError('权限验证失败,请重新登录')
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.$router.push('/cpzsadmin/login')
|
|
|
|
|
|
}, 3000)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.permissionChecking = false
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 文件选择处理
|
|
|
|
|
|
handleFileSelect(event, type) {
|
|
|
|
|
|
const file = event.target.files[0]
|
|
|
|
|
|
if (!file) return
|
|
|
|
|
|
|
|
|
|
|
|
// 验证文件类型
|
|
|
|
|
|
if (!this.validateFileType(file)) {
|
|
|
|
|
|
this.showError('请选择.xlsx或.xls格式的Excel文件')
|
|
|
|
|
|
event.target.value = ''
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证文件大小(限制50MB)
|
|
|
|
|
|
if (file.size > 50 * 1024 * 1024) {
|
|
|
|
|
|
this.showError('文件大小不能超过50MB')
|
|
|
|
|
|
event.target.value = ''
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
|
case 'fullData':
|
|
|
|
|
|
this.fullDataFile = file
|
|
|
|
|
|
this.fullDataResult = null
|
|
|
|
|
|
break
|
|
|
|
|
|
case 'lottery':
|
|
|
|
|
|
this.lotteryFile = file
|
|
|
|
|
|
this.lotteryResult = null
|
|
|
|
|
|
break
|
|
|
|
|
|
case 'append':
|
|
|
|
|
|
this.appendFile = file
|
|
|
|
|
|
this.appendResult = null
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 验证文件类型
|
|
|
|
|
|
validateFileType(file) {
|
|
|
|
|
|
const allowedTypes = [
|
|
|
|
|
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx
|
|
|
|
|
|
'application/vnd.ms-excel' // .xls
|
|
|
|
|
|
]
|
|
|
|
|
|
return allowedTypes.includes(file.type) ||
|
|
|
|
|
|
file.name.endsWith('.xlsx') ||
|
|
|
|
|
|
file.name.endsWith('.xls')
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 上传完整数据
|
|
|
|
|
|
async uploadFullData() {
|
|
|
|
|
|
if (!this.fullDataFile) return
|
|
|
|
|
|
|
|
|
|
|
|
this.fullDataUploading = true
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await lotteryApi.uploadExcelFile(this.fullDataFile)
|
|
|
|
|
|
this.fullDataResult = {
|
|
|
|
|
|
type: 'success',
|
|
|
|
|
|
message: '✅ ' + (response || '完整数据导入成功!')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 清空文件选择
|
|
|
|
|
|
this.fullDataFile = null
|
|
|
|
|
|
this.$refs.fullDataFileInput.value = ''
|
|
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('完整数据导入失败:', error)
|
|
|
|
|
|
this.fullDataResult = {
|
|
|
|
|
|
type: 'error',
|
|
|
|
|
|
message: '❌ ' + (error?.response?.data || error?.message || '导入失败,请重试')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.fullDataUploading = false
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 上传开奖数据(覆盖)
|
|
|
|
|
|
async uploadLotteryData() {
|
|
|
|
|
|
if (!this.lotteryFile) return
|
|
|
|
|
|
|
|
|
|
|
|
this.lotteryUploading = true
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await lotteryApi.uploadLotteryDrawsFile(this.lotteryFile)
|
|
|
|
|
|
this.lotteryResult = {
|
|
|
|
|
|
type: 'success',
|
|
|
|
|
|
message: '✅ ' + (response || '开奖数据导入成功!')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 清空文件选择
|
|
|
|
|
|
this.lotteryFile = null
|
|
|
|
|
|
this.$refs.lotteryFileInput.value = ''
|
|
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('开奖数据导入失败:', error)
|
|
|
|
|
|
this.lotteryResult = {
|
|
|
|
|
|
type: 'error',
|
|
|
|
|
|
message: '❌ ' + (error?.response?.data || error?.message || '导入失败,请重试')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.lotteryUploading = false
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 追加开奖数据
|
|
|
|
|
|
async appendLotteryData() {
|
|
|
|
|
|
if (!this.appendFile) return
|
|
|
|
|
|
|
|
|
|
|
|
this.appendUploading = true
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await lotteryApi.appendLotteryDrawsFile(this.appendFile)
|
|
|
|
|
|
this.appendResult = {
|
|
|
|
|
|
type: 'success',
|
|
|
|
|
|
message: '✅ ' + (response || '开奖数据追加成功!')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 清空文件选择
|
|
|
|
|
|
this.appendFile = null
|
|
|
|
|
|
this.$refs.appendFileInput.value = ''
|
|
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('开奖数据追加失败:', error)
|
|
|
|
|
|
this.appendResult = {
|
|
|
|
|
|
type: 'error',
|
|
|
|
|
|
message: '❌ ' + (error?.response?.data || error?.message || '追加失败,请重试')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.appendUploading = false
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 显示错误信息
|
|
|
|
|
|
showError(message) {
|
|
|
|
|
|
this.errorMessage = message
|
|
|
|
|
|
this.showErrorModal = true
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 隐藏错误弹窗
|
|
|
|
|
|
hideErrorModal() {
|
|
|
|
|
|
this.showErrorModal = false
|
|
|
|
|
|
this.errorMessage = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.excel-import-management {
|
|
|
|
|
|
min-height: 100vh;
|
2026-02-14 11:56:57 +08:00
|
|
|
|
background: var(--color-bg-page, #f0f2f5);
|
2026-01-15 18:16:50 +08:00
|
|
|
|
padding: 24px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 页面头部 */
|
|
|
|
|
|
.page-header {
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
background: linear-gradient(135deg, #3a7bd5, #00d2ff);
|
|
|
|
|
|
padding: 30px;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
color: white;
|
|
|
|
|
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 权限检查样式 */
|
|
|
|
|
|
.permission-checking {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
min-height: 400px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.checking-content {
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
color: white;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.checking-content p {
|
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.loading-spinner {
|
|
|
|
|
|
width: 40px;
|
|
|
|
|
|
height: 40px;
|
|
|
|
|
|
border: 4px solid rgba(255, 255, 255, 0.3);
|
|
|
|
|
|
border-top: 4px solid white;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
animation: spin 1s linear infinite;
|
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@keyframes spin {
|
|
|
|
|
|
0% { transform: rotate(0deg); }
|
|
|
|
|
|
100% { transform: rotate(360deg); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.header-content h1 {
|
|
|
|
|
|
font-size: 32px;
|
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.header-content p {
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 主容器 */
|
|
|
|
|
|
.import-container {
|
|
|
|
|
|
max-width: 1400px;
|
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 功能区域 */
|
|
|
|
|
|
.function-area {
|
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.function-card {
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.card-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.card-header .el-icon {
|
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
|
color: #409EFF;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.card-desc {
|
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.card-desc p {
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 上传区域 */
|
|
|
|
|
|
.upload-section {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-input-container {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-input {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-label {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 12px 16px;
|
|
|
|
|
|
border: 2px dashed #dcdfe6;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: all 0.3s;
|
|
|
|
|
|
background: #fafafa;
|
|
|
|
|
|
min-height: 60px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-label:hover {
|
|
|
|
|
|
border-color: #409eff;
|
|
|
|
|
|
background: #ecf5ff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-icon {
|
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
|
margin-right: 12px;
|
|
|
|
|
|
color: #409eff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-text {
|
|
|
|
|
|
color: #606266;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 减少卡片内边距 */
|
|
|
|
|
|
:deep(.el-card__body) {
|
|
|
|
|
|
padding: 16px !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 结果消息 */
|
|
|
|
|
|
.result-message {
|
|
|
|
|
|
padding: 12px;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.result-message.success {
|
|
|
|
|
|
background: #d4edda;
|
|
|
|
|
|
color: #155724;
|
|
|
|
|
|
border: 1px solid #c3e6cb;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.result-message.error {
|
|
|
|
|
|
background: #f8d7da;
|
|
|
|
|
|
color: #721c24;
|
|
|
|
|
|
border: 1px solid #f5c6cb;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 信息卡片 */
|
|
|
|
|
|
.info-card {
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 信息说明 */
|
|
|
|
|
|
.info-content {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-item h4 {
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-item p {
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
margin: 2px 0;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 模态框 */
|
|
|
|
|
|
.modal-overlay {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.5);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
z-index: 1000;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.modal-content {
|
|
|
|
|
|
background: white;
|
|
|
|
|
|
padding: 30px;
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
max-width: 400px;
|
|
|
|
|
|
width: 90%;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.error-modal h3 {
|
|
|
|
|
|
color: #dc3545;
|
|
|
|
|
|
margin-bottom: 15px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.error-modal p {
|
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.btn {
|
|
|
|
|
|
padding: 10px 20px;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.btn-primary {
|
|
|
|
|
|
background: #409eff;
|
|
|
|
|
|
color: white;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 响应式设计 */
|
|
|
|
|
|
@media (max-width: 1200px) {
|
|
|
|
|
|
.function-area .el-col {
|
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
|
.excel-import-management {
|
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.function-area .el-row {
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.function-area .el-col {
|
|
|
|
|
|
flex: 0 0 100%;
|
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.page-header {
|
|
|
|
|
|
padding: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.page-header h1 {
|
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.card-header span {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.card-desc {
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.card-desc p {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-label {
|
|
|
|
|
|
padding: 8px 10px;
|
|
|
|
|
|
min-height: 45px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-text {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.file-icon {
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
margin-right: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.upload-section {
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-item h4 {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-item p {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|