..
This commit is contained in:
@@ -127,10 +127,10 @@
|
||||
|
||||
<el-card shadow="never" class="mt10">
|
||||
<table-tool :label="currentGroupName">
|
||||
<el-button @click="doExportUser" icon="el-icon-printer" size="medium" type="primary" :disabled="!pageForm.groupId">
|
||||
<el-button @click="doExportUser" icon="el-icon-printer" size="small" type="primary" :disabled="!pageForm.groupId">
|
||||
导出活动分组人员xlsx
|
||||
</el-button>
|
||||
<el-button @click="doDelete(null)" type="danger" size="medium" :disabled="tableData.length === 0">
|
||||
<el-button @click="doDelete(null)" type="danger" size="small" :disabled="tableData.length === 0">
|
||||
删除{{ currentGroupName }}
|
||||
</el-button>
|
||||
</table-tool>
|
||||
@@ -238,7 +238,7 @@ module.exports = {
|
||||
components: {},
|
||||
methods: {
|
||||
viewGroupName() {
|
||||
if (this.pageForm.groupId) {
|
||||
if (this.pageForm?.groupId) {
|
||||
const group = this.activityGroupList.find((v) => v.groupId === this.pageForm.groupId)
|
||||
this.currentGroupName = group.groupName + "人员"
|
||||
return
|
||||
@@ -282,14 +282,16 @@ module.exports = {
|
||||
async getActivityGroup(id) {
|
||||
const resp = await $.get("/platform/activity/basic/scope/getActivityUserScopeGroup")
|
||||
this.activityGroupList = resp.data
|
||||
if (id) {
|
||||
if (this.activityGroupList.some((v) => v.groupId === id)) {
|
||||
this.$set(this.pageForm, "groupId", id)
|
||||
if (resp.data && resp.data.length > 0) {
|
||||
if (id) {
|
||||
if (this.activityGroupList.some((v) => v.groupId === id)) {
|
||||
this.$set(this.pageForm, "groupId", id)
|
||||
} else {
|
||||
this.$set(this.pageForm, "groupId", resp.data[0].groupId)
|
||||
}
|
||||
} else {
|
||||
this.$set(this.pageForm, "groupId", resp.data[0].groupId)
|
||||
}
|
||||
} else {
|
||||
this.$set(this.pageForm, "groupId", resp.data[0].groupId)
|
||||
}
|
||||
await this.doSearch()
|
||||
},
|
||||
|
||||
@@ -0,0 +1,994 @@
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "excelImport",
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
required: true,
|
||||
description: "后端接收文件的URL"
|
||||
},
|
||||
extra_params: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
description: "上传时附加的参数"
|
||||
},
|
||||
template_url: {
|
||||
type: String,
|
||||
default: "",
|
||||
description: "下载导入模板的URL地址"
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: "dialog",
|
||||
validator: (value) => ["dialog", "direct"].includes(value),
|
||||
description: "显示模式:dialog-弹窗模式(默认),direct-直接显示"
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: "Excel 数据导入",
|
||||
description: "弹窗标题(dialog模式下有效)"
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: "800px",
|
||||
description: "弹窗宽度(dialog模式下有效)"
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
description: "是否显示弹窗(dialog模式下有效)"
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedFile: null,
|
||||
importing: false,
|
||||
importComplete: false,
|
||||
progress: 0,
|
||||
progressText: "",
|
||||
stats: {
|
||||
totalRecords: 0,
|
||||
successCount: 0,
|
||||
failedCount: 0
|
||||
},
|
||||
errorDetails: [],
|
||||
excelData: [],
|
||||
dialogVisible: this.visible,
|
||||
downloading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
progressColor() {
|
||||
if (this.progress < 30) return "#F56C6C"
|
||||
if (this.progress < 70) return "#E6A23C"
|
||||
return "#67C23A"
|
||||
},
|
||||
successRate() {
|
||||
if (this.stats.totalRecords === 0) return 0
|
||||
return Math.round((this.stats.successCount / this.stats.totalRecords) * 100)
|
||||
},
|
||||
showTemplateBtn() {
|
||||
return !!this.template_url
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
this.dialogVisible = val
|
||||
},
|
||||
dialogVisible(val) {
|
||||
if (val !== this.visible) {
|
||||
this.$emit("update:visible", val)
|
||||
}
|
||||
if (!val) {
|
||||
this.resetUpload()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleFileChange(file) {
|
||||
this.selectedFile = file.raw
|
||||
this.resetStats()
|
||||
},
|
||||
|
||||
beforeUpload(file) {
|
||||
const isExcel =
|
||||
file.type === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ||
|
||||
file.type === "application/vnd.ms-excel" ||
|
||||
file.name.toLowerCase().endsWith(".xlsx") ||
|
||||
file.name.toLowerCase().endsWith(".xls")
|
||||
|
||||
if (!isExcel) {
|
||||
this.$message.error("只能上传 Excel 文件!")
|
||||
return false
|
||||
}
|
||||
|
||||
const isLt10M = file.size / 1024 / 1024 < 10
|
||||
if (!isLt10M) {
|
||||
this.$message.error("文件大小不能超过 10MB!")
|
||||
return false
|
||||
}
|
||||
|
||||
return false // 阻止自动上传
|
||||
},
|
||||
|
||||
async startImport() {
|
||||
if (!this.selectedFile) {
|
||||
this.$message.warning("请先选择文件!")
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.url) {
|
||||
this.$message.error("未配置上传URL!")
|
||||
return
|
||||
}
|
||||
|
||||
this.importing = true
|
||||
this.progress = 0
|
||||
this.progressText = "准备上传文件..."
|
||||
|
||||
try {
|
||||
// 创建FormData对象
|
||||
const formData = new FormData()
|
||||
formData.append("file", this.selectedFile)
|
||||
|
||||
// 添加额外参数
|
||||
if (this.extra_params) {
|
||||
Object.keys(this.extra_params).forEach((key) => {
|
||||
formData.append(key, this.extra_params[key])
|
||||
})
|
||||
}
|
||||
|
||||
// 设置上传进度
|
||||
await this.updateProgress(20, "正在上传文件...")
|
||||
|
||||
// 发送请求到后端
|
||||
const { code, data } = await this.$axios.post(this.url, formData, {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data"
|
||||
},
|
||||
onUploadProgress: (progressEvent) => {
|
||||
const percentCompleted = Math.round((progressEvent.loaded * 40) / progressEvent.total)
|
||||
this.updateProgress(20 + percentCompleted, "上传中...")
|
||||
}
|
||||
})
|
||||
|
||||
// 处理后端返回的结果
|
||||
await this.updateProgress(70, "处理后端导入结果...")
|
||||
|
||||
if (code === 0 && data) {
|
||||
// 处理成功
|
||||
const result = data || {}
|
||||
|
||||
this.stats.totalRecords = result.totalRecords || 0
|
||||
this.stats.successCount = result.successCount || 0
|
||||
this.stats.failedCount = result.failedCount || 0
|
||||
this.errorDetails = result.errorDetails || []
|
||||
|
||||
await this.updateProgress(100, "导入完成!")
|
||||
|
||||
setTimeout(() => {
|
||||
this.importing = false
|
||||
this.importComplete = true
|
||||
this.$message.success("Excel 导入完成!")
|
||||
this.$emit("import-success", result)
|
||||
}, 500)
|
||||
} else {
|
||||
// 处理失败
|
||||
throw new Error(msg || "导入失败")
|
||||
}
|
||||
} catch (error) {
|
||||
this.importing = false
|
||||
this.$message.error("导入失败:" + (error.message || "未知错误"))
|
||||
this.$emit("import-error", error)
|
||||
}
|
||||
},
|
||||
|
||||
async downloadTemplate() {
|
||||
if (!this.template_url) {
|
||||
this.$message.warning("未配置模板下载地址!")
|
||||
return
|
||||
}
|
||||
|
||||
this.downloading = true
|
||||
|
||||
try {
|
||||
// 下载模板文件
|
||||
const response = await this.$axios({
|
||||
url: this.template_url,
|
||||
method: "GET",
|
||||
responseType: "blob"
|
||||
})
|
||||
|
||||
// 创建下载链接
|
||||
const blob = new Blob([response.data])
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
|
||||
// 获取文件名,从Content-Disposition中提取或使用默认名称
|
||||
let filename = "导入模板.xlsx"
|
||||
const disposition = response.headers["content-disposition"]
|
||||
if (disposition && disposition.indexOf("attachment") !== -1) {
|
||||
const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/
|
||||
const matches = filenameRegex.exec(disposition)
|
||||
if (matches != null && matches[1]) {
|
||||
filename = matches[1].replace(/['"]/g, "")
|
||||
// 解决中文乱码问题
|
||||
try {
|
||||
filename = decodeURIComponent(filename)
|
||||
} catch (e) {
|
||||
// 解码失败时使用原始文件名
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 触发下载
|
||||
const link = document.createElement("a")
|
||||
link.href = url
|
||||
link.download = filename
|
||||
link.style.display = "none"
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
|
||||
this.$message.success("模板下载成功!")
|
||||
} catch (error) {
|
||||
this.$message.error("模板下载失败:" + (error.message || "未知错误"))
|
||||
this.$emit("template-error", error)
|
||||
} finally {
|
||||
this.downloading = false
|
||||
}
|
||||
},
|
||||
|
||||
readExcelFile(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
try {
|
||||
const data = new Uint8Array(e.target.result)
|
||||
const workbook = XLSX.read(data, { type: "array" })
|
||||
const firstSheet = workbook.Sheets[workbook.SheetNames[0]]
|
||||
const jsonData = XLSX.utils.sheet_to_json(firstSheet, { header: 1 })
|
||||
resolve(jsonData)
|
||||
} catch (error) {
|
||||
reject(new Error("文件解析失败"))
|
||||
}
|
||||
}
|
||||
reader.onerror = () => reject(new Error("文件读取失败"))
|
||||
reader.readAsArrayBuffer(file)
|
||||
})
|
||||
},
|
||||
|
||||
async processData(data) {
|
||||
this.excelData = data
|
||||
this.stats.totalRecords = data.length - 1 // 减去表头
|
||||
this.errorDetails = []
|
||||
|
||||
let successCount = 0
|
||||
let failedCount = 0
|
||||
|
||||
// 模拟数据处理过程
|
||||
for (let i = 1; i < data.length; i++) {
|
||||
await this.updateProgress(40 + Math.round((i / (data.length - 1)) * 50), `处理第 ${i} 条记录...`)
|
||||
|
||||
// 模拟数据验证和处理
|
||||
const row = data[i]
|
||||
const isValid = this.validateRow(row, i + 1)
|
||||
|
||||
if (isValid) {
|
||||
successCount++
|
||||
} else {
|
||||
failedCount++
|
||||
}
|
||||
|
||||
// 添加延迟以显示进度效果
|
||||
if (i % 10 === 0) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 50))
|
||||
}
|
||||
}
|
||||
|
||||
this.stats.successCount = successCount
|
||||
this.stats.failedCount = failedCount
|
||||
},
|
||||
|
||||
validateRow(row, rowIndex) {
|
||||
// 模拟数据验证逻辑
|
||||
if (!row || row.length === 0) {
|
||||
this.errorDetails.push({
|
||||
row: rowIndex,
|
||||
message: "空行数据"
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
// 随机生成一些错误(用于演示)
|
||||
if (Math.random() < 0.1) {
|
||||
// 10% 的错误率
|
||||
const errors = ["必填字段为空", "数据格式不正确", "数据长度超出限制", "重复数据", "外键约束违反"]
|
||||
this.errorDetails.push({
|
||||
row: rowIndex,
|
||||
message: errors[Math.floor(Math.random() * errors.length)]
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
|
||||
updateProgress(percentage, text) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
this.progress = percentage
|
||||
this.progressText = text
|
||||
resolve()
|
||||
}, 100)
|
||||
})
|
||||
},
|
||||
|
||||
getSuccessRateColor(rate) {
|
||||
if (rate >= 90) return "#67C23A"
|
||||
if (rate >= 70) return "#E6A23C"
|
||||
return "#F56C6C"
|
||||
},
|
||||
|
||||
formatFileSize(bytes) {
|
||||
if (bytes === 0) return "0 Bytes"
|
||||
const k = 1024
|
||||
const sizes = ["Bytes", "KB", "MB", "GB"]
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]
|
||||
},
|
||||
|
||||
downloadReport() {
|
||||
if (this.errorDetails.length === 0) return
|
||||
|
||||
let content = "错误报告\n\n"
|
||||
content += `总记录数: ${this.stats.totalRecords}\n`
|
||||
content += `成功数: ${this.stats.successCount}\n`
|
||||
content += `失败数: ${this.stats.failedCount}\n`
|
||||
content += `成功率: ${this.successRate}%\n\n`
|
||||
content += "错误详情:\n"
|
||||
|
||||
this.errorDetails.forEach((error) => {
|
||||
content += `第${error.row}行: ${error.message}\n`
|
||||
})
|
||||
|
||||
const blob = new Blob([content], { type: "text/plain;charset=utf-8" })
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const a = document.createElement("a")
|
||||
a.href = url
|
||||
a.download = "导入错误报告.txt"
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url)
|
||||
|
||||
this.$message.success("错误报告下载完成!")
|
||||
},
|
||||
|
||||
resetUpload() {
|
||||
this.selectedFile = null
|
||||
this.importing = false
|
||||
this.importComplete = false
|
||||
this.progress = 0
|
||||
this.progressText = ""
|
||||
this.resetStats()
|
||||
if (this.$refs.upload) {
|
||||
this.$refs.upload.clearFiles()
|
||||
}
|
||||
this.$emit("reset")
|
||||
},
|
||||
|
||||
resetStats() {
|
||||
this.stats = {
|
||||
totalRecords: 0,
|
||||
successCount: 0,
|
||||
failedCount: 0
|
||||
}
|
||||
this.errorDetails = []
|
||||
this.excelData = []
|
||||
},
|
||||
|
||||
closeDialog() {
|
||||
if (this.mode === "dialog") {
|
||||
this.dialogVisible = false
|
||||
}
|
||||
},
|
||||
|
||||
showDialog() {
|
||||
if (this.mode === "dialog") {
|
||||
this.dialogVisible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Dialog模式 -->
|
||||
<el-dialog
|
||||
v-if="mode === 'dialog'"
|
||||
:visible.sync="dialogVisible"
|
||||
:title="title"
|
||||
:width="width"
|
||||
@close="resetUpload"
|
||||
:append-to-body="true"
|
||||
:destroy-on-close="true"
|
||||
>
|
||||
<div class="excel-import-container dialog-mode">
|
||||
<div class="content">
|
||||
<!-- 文件上传区域 -->
|
||||
<div class="upload-area" v-if="!importing && !importComplete">
|
||||
<el-upload
|
||||
ref="upload"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:on-change="handleFileChange"
|
||||
:before-upload="beforeUpload"
|
||||
accept=".xlsx,.xls"
|
||||
drag
|
||||
:show-file-list="false"
|
||||
>
|
||||
<div class="upload-icon">📁</div>
|
||||
<div class="upload-text">将 Excel 文件拖拽到此处,或点击选择文件</div>
|
||||
<div class="upload-hint">支持 .xlsx、.xls 格式,文件大小不超过 10MB</div>
|
||||
</el-upload>
|
||||
</div>
|
||||
|
||||
<!-- 下载模板按钮 -->
|
||||
<div v-if="showTemplateBtn && !importing && !importComplete" class="template-download">
|
||||
<el-button type="text" @click="downloadTemplate" :loading="downloading" icon="el-icon-download">下载导入模板</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 已选择文件信息 -->
|
||||
<div v-if="selectedFile && !importing && !importComplete" class="file-info">
|
||||
<div class="file-info-item">
|
||||
<span><strong>文件名:</strong></span>
|
||||
<span>{{ selectedFile.name }}</span>
|
||||
</div>
|
||||
<div class="file-info-item">
|
||||
<span><strong>文件大小:</strong></span>
|
||||
<span>{{ formatFileSize(selectedFile.size) }}</span>
|
||||
</div>
|
||||
<div class="file-info-item">
|
||||
<span><strong>文件类型:</strong></span>
|
||||
<span>{{ selectedFile.type || "未知" }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 导入按钮 -->
|
||||
<div v-if="selectedFile && !importing && !importComplete" class="actions">
|
||||
<el-button type="primary" size="large" @click="startImport" :loading="importing">
|
||||
<i class="el-icon-upload2"></i>
|
||||
开始导入
|
||||
</el-button>
|
||||
<el-button size="large" @click="resetUpload">
|
||||
<i class="el-icon-refresh"></i>
|
||||
重新选择
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 导入进度 -->
|
||||
<div v-if="importing" class="progress-section">
|
||||
<h3 style="text-align: center; color: #409eff; margin-bottom: 20px">
|
||||
<i class="el-icon-loading"></i>
|
||||
正在处理文件...
|
||||
</h3>
|
||||
<el-progress :percentage="progress" :color="progressColor" :stroke-width="8" text-inside></el-progress>
|
||||
<p style="text-align: center; margin-top: 15px; color: #606266">
|
||||
{{ progressText }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 导入结果 -->
|
||||
<div v-if="importComplete" class="result-section">
|
||||
<div class="result-header">
|
||||
<div class="result-icon">
|
||||
<i class="el-icon-circle-check"></i>
|
||||
</div>
|
||||
<h3>导入完成</h3>
|
||||
</div>
|
||||
|
||||
<div class="stats-container">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon total-icon">
|
||||
<i class="el-icon-document"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-number total">{{ stats.totalRecords }}</div>
|
||||
<div class="stat-label">总记录数</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon success-icon">
|
||||
<i class="el-icon-check"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-number success">{{ stats.successCount }}</div>
|
||||
<div class="stat-label">导入成功</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon failed-icon">
|
||||
<i class="el-icon-close"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-number failed">{{ stats.failedCount }}</div>
|
||||
<div class="stat-label">导入失败</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 成功率展示 -->
|
||||
<div class="success-rate-section">
|
||||
<div class="success-rate-header">
|
||||
<span class="success-rate-label">导入成功率</span>
|
||||
<span class="success-rate-value" :style="{ color: getSuccessRateColor(successRate) }">{{ successRate }}%</span>
|
||||
</div>
|
||||
<el-progress :percentage="successRate" :color="getSuccessRateColor(successRate)" :stroke-width="6"></el-progress>
|
||||
</div>
|
||||
|
||||
<!-- 错误详情 -->
|
||||
<div v-if="errorDetails.length > 0" class="error-details-section">
|
||||
<el-collapse>
|
||||
<el-collapse-item name="errors">
|
||||
<template slot="title">
|
||||
<div class="error-title">
|
||||
<i class="el-icon-warning-outline"></i>
|
||||
<span>查看错误详情 ({{ errorDetails.length }}条)</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="error-list">
|
||||
<div v-for="(error, index) in errorDetails" :key="index" class="error-item">
|
||||
<div class="error-row">第 {{ error.row }} 行</div>
|
||||
<div class="error-message">{{ error.errMsg }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="actions">
|
||||
<el-button type="primary" @click="downloadReport" v-if="stats.failedCount > 0">
|
||||
<i class="el-icon-download"></i>
|
||||
下载错误报告
|
||||
</el-button>
|
||||
<el-button @click="resetUpload">
|
||||
<i class="el-icon-refresh"></i>
|
||||
重新导入
|
||||
</el-button>
|
||||
<el-button @click="closeDialog">
|
||||
<i class="el-icon-close"></i>
|
||||
关闭
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 直接显示模式 -->
|
||||
<div v-else class="excel-import-container">
|
||||
<div class="header">
|
||||
<h1>📊 Excel 文件导入</h1>
|
||||
<p>支持 .xlsx、.xls 格式文件,自动解析并统计导入结果</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<!-- 文件上传区域 -->
|
||||
<div class="upload-area" v-if="!importing && !importComplete">
|
||||
<el-upload
|
||||
ref="upload"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:on-change="handleFileChange"
|
||||
:before-upload="beforeUpload"
|
||||
accept=".xlsx,.xls"
|
||||
drag
|
||||
:show-file-list="false"
|
||||
>
|
||||
<div class="upload-icon">📁</div>
|
||||
<div class="upload-text">将 Excel 文件拖拽到此处,或点击选择文件</div>
|
||||
<div class="upload-hint">支持 .xlsx、.xls 格式,文件大小不超过 10MB</div>
|
||||
</el-upload>
|
||||
</div>
|
||||
|
||||
<!-- 下载模板按钮 -->
|
||||
<div v-if="showTemplateBtn && !importing && !importComplete" class="template-download">
|
||||
<el-button type="text" @click="downloadTemplate" :loading="downloading" icon="el-icon-download">下载导入模板</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 已选择文件信息 -->
|
||||
<div v-if="selectedFile && !importing && !importComplete" class="file-info">
|
||||
<div class="file-info-item">
|
||||
<span><strong>文件名:</strong></span>
|
||||
<span>{{ selectedFile.name }}</span>
|
||||
</div>
|
||||
<div class="file-info-item">
|
||||
<span><strong>文件大小:</strong></span>
|
||||
<span>{{ formatFileSize(selectedFile.size) }}</span>
|
||||
</div>
|
||||
<div class="file-info-item">
|
||||
<span><strong>文件类型:</strong></span>
|
||||
<span>{{ selectedFile.type || "未知" }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 导入按钮 -->
|
||||
<div v-if="selectedFile && !importing && !importComplete" class="actions">
|
||||
<el-button type="primary" size="large" @click="startImport" :loading="importing">
|
||||
<i class="el-icon-upload2"></i>
|
||||
开始导入
|
||||
</el-button>
|
||||
<el-button size="large" @click="resetUpload">
|
||||
<i class="el-icon-refresh"></i>
|
||||
重新选择
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 导入进度 -->
|
||||
<div v-if="importing" class="progress-section">
|
||||
<h3 style="text-align: center; color: #409eff; margin-bottom: 20px">
|
||||
<i class="el-icon-loading"></i>
|
||||
正在处理文件...
|
||||
</h3>
|
||||
<el-progress :percentage="progress" :color="progressColor" :stroke-width="8" text-inside></el-progress>
|
||||
<p style="text-align: center; margin-top: 15px; color: #606266">
|
||||
{{ progressText }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 导入结果 -->
|
||||
<div v-if="importComplete" class="result-section">
|
||||
<div class="result-header">
|
||||
<div class="result-icon">
|
||||
<i class="el-icon-circle-check"></i>
|
||||
</div>
|
||||
<h3>导入完成</h3>
|
||||
</div>
|
||||
|
||||
<div class="stats-container">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon total-icon">
|
||||
<i class="el-icon-document"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-number total">{{ stats.totalRecords }}</div>
|
||||
<div class="stat-label">总记录数</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon success-icon">
|
||||
<i class="el-icon-check"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-number success">{{ stats.successCount }}</div>
|
||||
<div class="stat-label">导入成功</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon failed-icon">
|
||||
<i class="el-icon-close"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-number failed">{{ stats.failedCount }}</div>
|
||||
<div class="stat-label">导入失败</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 错误详情 -->
|
||||
<div v-if="errorDetails.length > 0" class="error-details-section">
|
||||
<el-collapse>
|
||||
<el-collapse-item name="errors">
|
||||
<template slot="title">
|
||||
<div class="error-title">
|
||||
<i class="el-icon-warning-outline"></i>
|
||||
<span>查看错误详情 ({{ errorDetails.length }}条)</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="error-list">
|
||||
<div v-for="(error, index) in errorDetails" :key="index" class="error-item">
|
||||
<div class="error-row">第 {{ error.row }} 行</div>
|
||||
<div class="error-message">{{ error.message }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="actions">
|
||||
<el-button type="primary" @click="downloadReport" v-if="stats.failedCount > 0">
|
||||
<i class="el-icon-download"></i>
|
||||
下载错误报告
|
||||
</el-button>
|
||||
<el-button @click="resetUpload">
|
||||
<i class="el-icon-refresh"></i>
|
||||
重新导入
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.excel-import-container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.excel-import-container.dialog-mode {
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: linear-gradient(135deg, #409eff 0%, #67c23a 100%);
|
||||
color: white;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin: 0;
|
||||
font-size: 28px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.header p {
|
||||
margin: 10px 0 0 0;
|
||||
opacity: 0.9;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.template-download {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.upload-area {
|
||||
border: 2px dashed #d9d9d9;
|
||||
border-radius: 8px;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
background: #fafafa;
|
||||
transition: all 0.3s ease;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.upload-area:hover {
|
||||
border-color: #409eff;
|
||||
background: #f0f9ff;
|
||||
}
|
||||
|
||||
.upload-icon {
|
||||
font-size: 48px;
|
||||
color: #c0c4cc;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.upload-text {
|
||||
color: #606266;
|
||||
font-size: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.upload-hint {
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.progress-section {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.stats-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 15px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
flex: 1;
|
||||
background: linear-gradient(135deg, #f6f8fa 0%, #ffffff 100%);
|
||||
border: 1px solid #e1e8ed;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
transition: transform 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.total-icon {
|
||||
background-color: rgba(64, 158, 255, 0.1);
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.success-icon {
|
||||
background-color: rgba(103, 194, 58, 0.1);
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.failed-icon {
|
||||
background-color: rgba(245, 108, 108, 0.1);
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.total {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.success {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.failed {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: #606266;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.result-section {
|
||||
margin-top: 30px;
|
||||
padding: 30px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.result-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.result-icon {
|
||||
font-size: 48px;
|
||||
color: #67c23a;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.result-header h3 {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.success-rate-section {
|
||||
margin-top: 30px;
|
||||
background: #f9f9f9;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.success-rate-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.success-rate-label {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.success-rate-value {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.error-details-section {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.error-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #f56c6c;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.error-title i {
|
||||
margin-right: 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.error-list {
|
||||
max-height: 250px;
|
||||
overflow-y: auto;
|
||||
background: #fff5f5;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.error-item {
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid #fde2e2;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.error-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.error-row {
|
||||
color: #f56c6c;
|
||||
font-weight: bold;
|
||||
background: rgba(245, 108, 108, 0.1);
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: #606266;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.file-info {
|
||||
background: #e8f4fd;
|
||||
border: 1px solid #b3d8ff;
|
||||
border-radius: 6px;
|
||||
padding: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.file-info-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.file-info-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.actions {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
}
|
||||
</style>
|
||||
@@ -430,6 +430,7 @@
|
||||
Vue.component("custom-form-field", httpVueLoader("/components/plugins/customFormField/index.vue?v=" + new Date().getTime()))
|
||||
Vue.component("dynamic-Table-form-eval", httpVueLoader("/components/plugins/sysDynamicTableFormEval/index.vue?v=" + new Date().getTime()))
|
||||
Vue.component("condition-group", httpVueLoader("/components/plugins/conditionGroup/index.vue?v=" + new Date().getTime()))
|
||||
Vue.component("excel-import", httpVueLoader("/components/plugins/sysImport/excelImport.vue?v=" + new Date().getTime()))
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -3,28 +3,47 @@ const activity = {
|
||||
<div class="home-activity">
|
||||
<el-card shadow="never">
|
||||
<div class="card-title" slot="header">最新活动</div>
|
||||
<div v-if="activityOptions && activityOptions.length > 0" v-for="item in activityOptions" :key="item.id">
|
||||
<div style="width: 100%">
|
||||
<div style="background-color: #fff; position: relative; margin-bottom: 10px">
|
||||
<div style="display: flex; justify-content: center; align-items: center">
|
||||
<el-image :src="item.cover" alt="" fit="contain" style="margin: 3px 14px 0 0; width: 100px; height: 70px; border-radius: 8px">
|
||||
<div v-if="activityOptions && activityOptions.length > 0" v-for="item in activityOptions"
|
||||
:key="item.id">
|
||||
<div style="width: 100%">
|
||||
<div style="background-color: #fff; position: relative; margin-bottom: 10px">
|
||||
<div style="display: flex; justify-content: center; align-items: center;border-bottom: 1px solid var(--border-color-lighter);padding-bottom: 8px">
|
||||
<div
|
||||
style="position: relative;flex-shrink: 1;width: 150px; height: 90px;margin-right: 10px; ">
|
||||
<el-image :src="item.cover" alt="" fit="cover"
|
||||
style="width: 100%; height: 100%; border-radius: 8px;">
|
||||
<div slot="error" class="image-slot">
|
||||
<i class="el-icon-picture-outline"></i>
|
||||
</div>
|
||||
</el-image>
|
||||
<div style="flex: 1">
|
||||
<div style="line-height: 22px; color: #111">
|
||||
<i class="el-icon-star-on" style="color: red; font-size: 17px"></i>{{item.name}}
|
||||
</div>
|
||||
<div style="font-size: 12px; line-height: 20px; color: #999">开始时间:{{$moment(item.startDate).format('MM-DD HH:mm')}}</div>
|
||||
<div style="font-size: 12px; line-height: 20px; color: #999">结束时间:{{$moment(item.endDate).format('MM-DD HH:mm')}}</div>
|
||||
<el-tag class="act-item-tag" type="danger" size="mini"
|
||||
v-if="$moment().unix() > $moment(item.endDate).unix()">
|
||||
已结束
|
||||
</el-tag>
|
||||
|
||||
<el-tag class="act-item-tag" type="success" size="mini"
|
||||
v-else-if="$moment().unix() < $moment(item.endDate).unix()">
|
||||
进行中
|
||||
</el-tag>
|
||||
</div>
|
||||
<div style="flex: 1;height: 90px;display: flex;flex-direction: column;justify-content: space-between;">
|
||||
<div style="color: #111">
|
||||
<i class="el-icon-star-on" style="color: red; font-size: 17px"></i>{{item.name}}
|
||||
</div>
|
||||
<div @click="enterActivity(item)">
|
||||
<el-tag style="cursor: pointer">查看<i class="el-icon-s-promotion"></i></el-tag>
|
||||
<div style="font-size: 12px; color: #999">
|
||||
开始时间:{{$moment(item.startDate).format('MM-DD HH:mm')}}
|
||||
</div>
|
||||
<div style="font-size: 12px; color: #999">
|
||||
结束时间:{{$moment(item.endDate).format('MM-DD HH:mm')}}
|
||||
</div>
|
||||
</div>
|
||||
<div @click="enterActivity(item)">
|
||||
<el-button size="mini" type="primary">查看<i class="el-icon-s-promotion"></i>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-if="activityOptions.length === 0" description="暂无活动"></el-empty>
|
||||
</el-card>
|
||||
@@ -63,5 +82,12 @@ const activity = {
|
||||
.home-activity .el-empty {
|
||||
padding: 40px 0 !important;
|
||||
}
|
||||
|
||||
.home-activity .act-item-tag{
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ const SYS_MENU_QUICK_ENTRY_COMPONENT = {
|
||||
<el-table-column label="名称" prop="name"></el-table-column>
|
||||
<el-table-column label="图标" prop="icon">
|
||||
<template scope="{row}">
|
||||
<svg-icon :name="row.icon"></svg-icon>
|
||||
<svg-icon :name="row.icon" v-if="platform==='PC'"></svg-icon>
|
||||
<img v-else :src="row.icon" alt="" style="width: 30px;height: 30px">
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
@@ -31,10 +31,10 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
|
||||
style="width: 100%;"
|
||||
v-model="formData.projectTypeCode">
|
||||
<el-option
|
||||
:key="item.id"
|
||||
:label="item.name+' ('+item.code+')'"
|
||||
:value="item.code"
|
||||
v-for="item in projectTypeList">
|
||||
:key="item.id"
|
||||
:label="item.name+' ('+item.code+')'"
|
||||
:value="item.code"
|
||||
v-for="item in projectTypeList">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -68,10 +68,10 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
|
||||
reserve-keyword
|
||||
style="width: 100%" v-model="formData.userId">
|
||||
<el-option
|
||||
:key="item.userId"
|
||||
:label="item.userName+'-'+item.loginName+'-'+item.unitName"
|
||||
:value="item.userId"
|
||||
v-for="item in userList">
|
||||
:key="item.userId"
|
||||
:label="item.userName+'-'+item.loginName+'-'+item.unitName"
|
||||
:value="item.userId"
|
||||
v-for="item in userList">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -84,48 +84,48 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<!-- <el-col :span="12"-->
|
||||
<!-- v-if="formData.isEnrollSystem === true">-->
|
||||
<!-- <el-form-item label="活动计划时间" prop="plannedDate">-->
|
||||
<!-- <el-date-picker-->
|
||||
<!-- @change="plannedDateChange"-->
|
||||
<!-- end-placeholder="结束日期"-->
|
||||
<!-- range-separator="-"-->
|
||||
<!-- start-placeholder="开始日期"-->
|
||||
<!-- style="width: 100%"-->
|
||||
<!-- type="daterange"-->
|
||||
<!-- v-model="formData.plannedDate" value-format="yyyy-MM-dd">-->
|
||||
<!-- </el-date-picker>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :span="12"-->
|
||||
<!-- v-if="formData.isEnrollSystem === true">-->
|
||||
<!-- <el-form-item label="活动计划时间" prop="plannedDate">-->
|
||||
<!-- <el-date-picker-->
|
||||
<!-- @change="plannedDateChange"-->
|
||||
<!-- end-placeholder="结束日期"-->
|
||||
<!-- range-separator="-"-->
|
||||
<!-- start-placeholder="开始日期"-->
|
||||
<!-- style="width: 100%"-->
|
||||
<!-- type="daterange"-->
|
||||
<!-- v-model="formData.plannedDate" value-format="yyyy-MM-dd">-->
|
||||
<!-- </el-date-picker>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </el-col>-->
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item label="实际活动时间" prop="time">
|
||||
<el-date-picker
|
||||
end-placeholder="结束日期"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
style="width: 100%"
|
||||
type="daterange"
|
||||
v-model="formData.time"
|
||||
value-format="yyyy-MM-dd">
|
||||
end-placeholder="结束日期"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
style="width: 100%"
|
||||
type="daterange"
|
||||
v-model="formData.time"
|
||||
value-format="yyyy-MM-dd">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item
|
||||
:rules="[{required:[1,2].includes(formData.signUpMethod),message:'请选择报名日期',trigger:['change','blur']}]"
|
||||
label="报名日期"
|
||||
prop="applyTime2">
|
||||
:rules="[{required:[1,2].includes(formData.signUpMethod),message:'请选择报名日期',trigger:['change','blur']}]"
|
||||
label="报名日期"
|
||||
prop="applyTime2">
|
||||
<el-date-picker
|
||||
end-placeholder="结束时间"
|
||||
range-separator="-"
|
||||
start-placeholder="开始时间"
|
||||
style="width: 100%"
|
||||
type="datetimerange"
|
||||
v-model="formData.applyTime2"
|
||||
value-format="yyyy-MM-dd HH:mm">
|
||||
end-placeholder="结束时间"
|
||||
range-separator="-"
|
||||
start-placeholder="开始时间"
|
||||
style="width: 100%"
|
||||
type="datetimerange"
|
||||
v-model="formData.applyTime2"
|
||||
value-format="yyyy-MM-dd HH:mm">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -133,9 +133,9 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
|
||||
<el-col :span="12" v-if="!formData.isEnrollSystem">
|
||||
<el-form-item label="活动人数" prop="peopleNum">
|
||||
<el-input
|
||||
maxlength="10"
|
||||
placeholder="请填写活动人数"
|
||||
v-model="formData.peopleNum"></el-input>
|
||||
maxlength="10"
|
||||
placeholder="请填写活动人数"
|
||||
v-model="formData.peopleNum"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
@@ -156,8 +156,8 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
|
||||
</div>
|
||||
<div>
|
||||
<el-button
|
||||
@click="$refs.drawerUserScope.userScopeDialog = true"
|
||||
type="primary">设置
|
||||
@click="$refs.drawerUserScope.userScopeDialog = true"
|
||||
type="primary">设置
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -181,7 +181,9 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
|
||||
<el-form-item label="报名人数限制" prop="userNumberLimit">
|
||||
<el-radio-group v-model="formData.userNumberLimit" size="small">
|
||||
<el-radio border :label="1">总人数限制</el-radio>
|
||||
<el-radio border :label="2" v-if="activity_type===40001">分工会人数限制</el-radio>
|
||||
<el-radio border :label="2" v-if="activity_type===40001">
|
||||
分工会人数限制
|
||||
</el-radio>
|
||||
<el-radio border :label="null">不限制</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
@@ -315,10 +317,10 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
|
||||
<el-col :span="24" v-if="formData.isEnrollSystem === false">
|
||||
<el-form-item label="活动考核内容" prop="activityExamineContent">
|
||||
<el-input
|
||||
:rows="7"
|
||||
placeholder="请输入活动考核内容"
|
||||
type="textarea"
|
||||
v-model="formData.activityExamineContent">
|
||||
:rows="7"
|
||||
placeholder="请输入活动考核内容"
|
||||
type="textarea"
|
||||
v-model="formData.activityExamineContent">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -428,16 +430,16 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
|
||||
<el-table :data="userData" border height="500" size="small"
|
||||
stripe v-loading="userTabLoading" row-key="userId">
|
||||
<el-table-column
|
||||
label="序号" type="index"
|
||||
width="100">
|
||||
label="序号" type="index"
|
||||
width="100">
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:key="column.prop"
|
||||
:sortable="column.sortable"
|
||||
v-for="column in userColumns">
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:key="column.prop"
|
||||
:sortable="column.sortable"
|
||||
v-for="column in userColumns">
|
||||
</el-table-column>
|
||||
<el-table-column label="操作"
|
||||
prop="userOnline"
|
||||
@@ -455,26 +457,25 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
|
||||
</table-tool>
|
||||
<el-form-item label="" prop="activitySummary">
|
||||
<el-input
|
||||
:disabled="!formData.time || Date.now() < Date.parse(formData.time[1])"
|
||||
:rows="5"
|
||||
placeholder="请输入活动内容"
|
||||
type="textarea"
|
||||
v-model="formData.activitySummary">
|
||||
:disabled="!formData.time || Date.now() < Date.parse(formData.time[1])"
|
||||
:rows="5"
|
||||
placeholder="请输入活动内容"
|
||||
type="textarea"
|
||||
v-model="formData.activitySummary">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-alert
|
||||
class="mb20"
|
||||
show-icon
|
||||
style="margin-left: 130px"
|
||||
title="活动结束后可输入活动总结"
|
||||
type="info"
|
||||
v-if="!formData.time || Date.now() < Date.parse(formData.time[1])">
|
||||
class="mb20"
|
||||
show-icon
|
||||
style="margin-left: 130px"
|
||||
title="活动结束后可输入活动总结"
|
||||
type="info"
|
||||
v-if="!formData.time || Date.now() < Date.parse(formData.time[1])">
|
||||
</el-alert>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div style="padding: 20px;text-align: center;">
|
||||
<el-button @click="operation" style="width: 300px" type="primary" :disabled="formLoading">
|
||||
确 定
|
||||
@@ -484,12 +485,12 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
|
||||
</el-card>
|
||||
|
||||
<drawer-user-scope
|
||||
@group_change="getActivityGroup"
|
||||
ref="drawerUserScope"
|
||||
:group_id.sync="formData.groupId"
|
||||
@group_change="getActivityGroup"
|
||||
ref="drawerUserScope"
|
||||
:group_id.sync="formData.groupId"
|
||||
></drawer-user-scope>
|
||||
|
||||
<el-dialog :close-on-click-modal="false" :visible.sync="userDialogVisible" title="添加人员" width="40%">
|
||||
<el-dialog :close-on-click-modal="false" :visible.sync="userDialogVisible" append-to-body title="添加人员" width="40%">
|
||||
|
||||
<el-form :model="formData" label-width="100px">
|
||||
<el-form-item label="参加人员" prop="userId">
|
||||
@@ -501,11 +502,11 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = {
|
||||
style="width: 100%"
|
||||
v-model="formData.tissuePersonList">
|
||||
<el-option
|
||||
:disabled="userData.map(v=>v.userId).includes(item.userId)"
|
||||
:key="item.userId"
|
||||
:label="item.userName+'-'+item.loginName+'-'+item.unitName"
|
||||
:value="item.userId"
|
||||
v-for="item in userList">
|
||||
:disabled="userData.map(v=>v.userId).includes(item.userId)"
|
||||
:key="item.userId"
|
||||
:label="item.userName+'-'+item.loginName+'-'+item.unitName"
|
||||
:value="item.userId"
|
||||
v-for="item in userList">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
@@ -99,11 +99,14 @@ layout("/layouts/platform.html"){
|
||||
</search-item>
|
||||
|
||||
<search-item label="聘用方式:">
|
||||
<dict-select clearable code="USER_PREPARED_BY_TYPE"
|
||||
multiple
|
||||
collapse-tags
|
||||
placeholder="请选择聘用方式"
|
||||
v-model="pageForm.preparedBys"></dict-select>
|
||||
<dict-select
|
||||
clearable
|
||||
code="USER_PREPARED_BY_TYPE"
|
||||
multiple
|
||||
collapse-tags
|
||||
placeholder="请选择聘用方式"
|
||||
v-model="pageForm.preparedBys"
|
||||
></dict-select>
|
||||
</search-item>
|
||||
|
||||
<search-item label="在职状态:">
|
||||
@@ -201,9 +204,22 @@ layout("/layouts/platform.html"){
|
||||
|
||||
<add_welfare_list_by_select ref="addWelfareListUser" @search="doSearch"></add_welfare_list_by_select>
|
||||
|
||||
<!--添加福利人员-->
|
||||
<add-user ref="addUserRef" @refresh="doSearch"></add-user>
|
||||
|
||||
<!--人员备注-->
|
||||
<update-remark ref="updateRemarkRef" @refresh="doSearch"></update-remark>
|
||||
|
||||
<!--导入福利名单-->
|
||||
<excel-import
|
||||
ref="excelImportRef"
|
||||
url="/platform/welfare/list/mange/importByExcel"
|
||||
template_url="/platform/welfare/list/mange/downloadTemplate"
|
||||
:visible.sync="showImportDialog"
|
||||
title="导入用户数据"
|
||||
width="700px"
|
||||
:extra_params="{projectId:pageForm.projectId}"
|
||||
></excel-import>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
@@ -260,7 +276,9 @@ layout("/layouts/platform.html"){
|
||||
importLoading: false,
|
||||
importDialog: false,
|
||||
|
||||
welfareListUserDrawer: false
|
||||
welfareListUserDrawer: false,
|
||||
|
||||
showImportDialog: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -275,12 +293,14 @@ layout("/layouts/platform.html"){
|
||||
this.doSearch()
|
||||
this.importDialog = false
|
||||
},
|
||||
|
||||
// 打开导入名单
|
||||
openImport() {
|
||||
this.importData = {
|
||||
isfg: 1,
|
||||
fileList: []
|
||||
}
|
||||
this.importDialog = true
|
||||
this.showImportDialog = true
|
||||
},
|
||||
doDelete(row) {
|
||||
this.$confirm("您确定要删除吗?", "提示", {
|
||||
|
||||
@@ -16,12 +16,13 @@ const home = {
|
||||
|
||||
<!--快捷入口-->
|
||||
<div class="quick-grid">
|
||||
<div class="section-title">快速入口</div>
|
||||
<van-grid :column-num="4" icon-size="46" square clickable :border="false">
|
||||
<van-grid-item v-for="item in quickEntries"
|
||||
@click="menuClick(item)"
|
||||
:key="item.id"
|
||||
:text="item.name">
|
||||
<img src="https://staticfile.org/images/banners/Home/7@2x.png"
|
||||
<img :src="item.icon"
|
||||
slot="icon"
|
||||
style="width: 40px; height: 40px"/>
|
||||
</van-grid-item>
|
||||
@@ -30,8 +31,17 @@ const home = {
|
||||
|
||||
<!--活动列表-->
|
||||
<div class="act-list">
|
||||
<div class="section-title">最新活动</div>
|
||||
<template v-if="activityOptions && activityOptions.length > 0">
|
||||
<div v-for="item in activityOptions" :key="item.id" class="act-item" @click="enterAct(item)">
|
||||
<van-tag class="act-item-wrapper-tag" v-if="$moment().unix() > $moment(item.endDate).unix()">
|
||||
已结束
|
||||
</van-tag>
|
||||
|
||||
<van-tag class="act-item-wrapper-tag" type="success" v-else-if="$moment().unix() < $moment(item.endDate).unix()">
|
||||
进行中
|
||||
</van-tag>
|
||||
|
||||
<div class="act-item-cover">
|
||||
<van-image v-if="item.cover" width="120" height="84" :src="item.cover"></van-image>
|
||||
</div>
|
||||
@@ -93,6 +103,8 @@ const home = {
|
||||
.quick-grid {
|
||||
background-color: #fff;
|
||||
margin: 10px;
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.quick-grid .van-grid-item .van-grid-item__text {
|
||||
@@ -101,6 +113,7 @@ const home = {
|
||||
text-overflow: ellipsis;
|
||||
width: 85%;
|
||||
text-align: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.act-list {
|
||||
@@ -115,17 +128,22 @@ const home = {
|
||||
width: 100%;
|
||||
padding: 6px;
|
||||
background-color: #fff;
|
||||
position: relative;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.act-item-cover {
|
||||
margin: 0 14px 0 0;
|
||||
width: 120px;
|
||||
height: 84px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.act-item-cover .van-image{
|
||||
width: 120px;
|
||||
height: 84px;
|
||||
/*width: 120px;*/
|
||||
/*height: 84px;*/
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.act-item-wrapper{
|
||||
@@ -135,6 +153,13 @@ const home = {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.act-item-wrapper-tag{
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.act-item-wrapper .content-title{
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
@@ -151,5 +176,10 @@ const home = {
|
||||
color: grey;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.section-title{
|
||||
padding: 10px 0 5px 10px;
|
||||
}
|
||||
|
||||
`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user