背景
记录一下后台系统中通用的功能点,以便在后续工作中直接copy使用。
当前项目技术栈: vue2 + element-ui
el-table 第一行高亮
<el-table :key="keys" :data="tableData" :border="true" :row-class-name="tableRowClassName">
<el-table-column v-for="i in tableColumns" :key="i.prop" :label="i.label" :prop="i.prop" :width="i.width" />
</el-table>
methods: {
tableRowClassName({ row, rowIndex }) {
console.log(rowIndex)
if (rowIndex === 0) {
return 'first-row'
}
return ''
},
}
<style lang="scss" scoped>
/deep/.first-row {
td {
font-weight: bold;
color: red;
background-color: #f0f9eb;
}
}
</style>
多选表格数据,不符合条件的数据禁止选择
<el-table :data="tableData" :border="true" @selection-change="handleSelectionChange">
<!----折叠行数据-->
<el-table-column type="expand">
<template #default="{ row }"></template>
</el-table-column>
<!--多选行代码-->
<el-table-column type="selection" width="55" :selectable="handleSelectable" />
<!--表格数据-->
<el-table-column v-for="i in tableColumns" :key="i.prop" :label="i.label" :prop="i.prop" :width="i.width">
<template #default="{ row }">
{{ row[i.prop] }}
</template>
</el-table-column>
</el-table>
// 符合条件的数据允许选择
handleSelectable(row) {
return row.audit_status === 0
}
导入excel文件,并以表格的形式展示数据
<template>
<div style="display: inline-block;">
<el-upload
ref="upload"
action="#"
:limit="1"
:show-file-list="false"
accept=".xls,.xlsx"
:on-change="fileChange"
:auto-upload="false"
style="display: inline-block;"
class="ml16"
>
<buttons is-import>批量导入模板</buttons>
</el-upload>
<el-dialog title="" width="1000px" :visible.sync="dialogVisible" :close-on-click-modal="false" top="5vh">
<el-form ref="form" inline :model="form" label-width="130px" :rules="formRules">
<el-form-item label="上传文件所属项目">
<el-input v-model="form.sourceProject" disabled />
</el-form-item>
<el-form-item label="上传文件渠道包">
<el-input v-model="form.sourcePackage" disabled />
</el-form-item>
<el-form-item label="当前所属项目">
<el-input v-model="form.curProject" disabled />
</el-form-item>
<el-form-item label="当前渠道包" prop="curPackage">
<el-select v-model="form.curPackage" placeholder="请选择渠道包" filterable style="width:240px">
<el-option v-for="({ id, name }) in packageList" :key="id" :value="id" :label="name" />
</el-select>
</el-form-item>
</el-form>
<!-- 提示信息 -->
<div class="total">
<span v-if="form.sourceProject !== form.curProject" class="tips-red fl">
<i class="el-icon-info" />
上传文件所属项目和当前项目不完全一致,请仔细确认导入数据
</span>
<span>共 <b class="blue ml16 mr16">{{ tableData.length }}</b>条数据</span>
</div>
<el-table :data="tableData" :border="true" height="calc(100vh - 400px)">
<el-table-column
v-for="{ label, prop } in columns1"
:key="prop"
:label="label"
:prop="prop"
show-overflow-tooltip
/>
</el-table>
<span slot="footer">
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" :loading="importLoading" :disabled="importLoading" @click="handleSubmit">确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import * as XLSX from 'xlsx'
import { batchImportSubscribeTemplate } from '@/api/product/messageSubcribe'
export default {
name: '',
props: {
packageList: {
type: Array,
default: () => []
}
},
data() {
return {
columns: [
{ label: '项目ID', prop: 'curPid' },
{ label: '项目名称', prop: 'appName' },
{ label: '渠道包ID', prop: 'package_id' },
{ label: '模版名称', prop: 'name' },
{ label: '订阅模版ID', prop: 'template_id' },
{ label: '订阅模板默认内容', prop: 'data' },
{ label: '模版卡片跳转页面', prop: 'page' },
{ label: '默认延时计算方式', prop: 'delay_type' },
{ label: '延迟发送时间', prop: 'delay_time' },
{ label: '状态', prop: 'status' }
],
formRules: {
curPackage: [{ required: true, message: '请选择渠道包', trigger: 'blur' }]
},
form: {
sourceProject: '', // 上传文件所属项目
sourcePackage: '', // 上传文件所属集群
curProject: '', // 当前项目
curPackage: '' // 当前渠道包
},
dialogVisible: false,
tableData: [],
importLoading: false
}
},
computed: {
columns1() {
const arr = ['curPid', 'appName', 'package_id']
return this.columns.filter(item => !arr.includes(item.prop))
}
},
methods: {
// 使用箭头函数,增加空数据校验
fileChange(file) {
if (!file || !file.raw) {
this.$message.error('文件无效')
return
}
const reader = new FileReader()
reader.onload = (e) => {
try {
const data = new Uint8Array(e.target.result)
const workbook = XLSX.read(data, {
type: 'array',
cellDates: true, // 将日期单元格解析为 Date 对象
cellNF: true, // 保留数字格式
cellText: true // 保留单元格文本
})
const firstSheet = workbook.Sheets[workbook.SheetNames[0]]
const jsonData = XLSX.utils.sheet_to_json(firstSheet, {
header: 1,
raw: true,
defval: '',
dateNF: 'yyyy-mm-dd hh:mm:ss' // 指定日期输出格式
})
this.dealData(jsonData)
} catch (error) {
this.$message.error('文件解析失败,请检查文件格式')
}
}
reader.readAsArrayBuffer(file.raw)
},
dealData(data) {
if (!data || data.length <= 1) {
this.$message.warning('导入文件无有效数据')
return false
}
// 处理数据,将列名映射到 prop
this.tableData = data.slice(1).map(item => {
const obj = {}
for (const key in item) {
if (this.columns[key]) {
obj[this.columns[key].prop] = item[key]
}
}
return obj
})
// 数据中含有不同渠道包则返回报错信息
const packageIds = new Set(this.tableData.map(item => item.package_id))
if (packageIds.size > 1) {
this.$message.error('导入文件中包含不同渠道包,无法导入,请检查数据')
return
}
// 安全访问第一个元素
if (this.tableData.length > 0) {
this.form.sourceProject = `[${this.tableData[0].curPid}]${this.tableData[0].appName}`
const packageItem = this.packageList.find(item => item.id === this.tableData[0].package_id)
this.form.sourcePackage = packageItem?.name
const { curPid, appName } = this.$store.getters
this.form.curProject = `[${curPid}]${appName}`
this.form.curPackage = this.packageList[0]?.id || ''
this.dialogVisible = true
}
},
async handleSubmit() {
try {
this.importLoading = true
const project = this.form.curProject
const packages = this.packageList.find(item => item.id === this.form.curPackage)?.name || ''
await this.$refs.form.validate()
await this.$confirm('当前导入项目为【' + project + '】,导入渠道包为【' + packages + '】,导入数据会对有数据进行覆盖,是否继续', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
const list = JSON.parse(JSON.stringify(this.tableData))
list.forEach((item, index) => {
item.package_id = this.form.curPackage
delete item.curPid
delete item.appName
try {
item.data = item.data ? JSON.parse(item.data) : {}
} catch (e) {
throw new Error(`第 ${index + 1} 行订阅模板默认内容格式错误`)
}
})
await batchImportSubscribeTemplate({ list })
this.$message.success('导入成功')
this.dialogVisible = false
this.$emit('update')
this.$refs.upload?.clearFiles()
} catch (error) {
console.log(error.message || '导入失败,请稍后重试')
} finally {
this.importLoading = false
}
},
// 新增取消处理方法
handleCancel() {
this.dialogVisible = false
this.$refs.upload?.clearFiles()
}
}
}
</script>
<style lang="scss" scoped>
.total {
margin-bottom: 16px;
margin-left: 20px;
text-align: right;
.tips-red {
font-weight: bold;
}
}
</style>