Files
education/frontend/src/views/student/shiftTeam/index.vue
T

872 lines
31 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="app-container shift-team-page">
<!-- ==================== 1. 查询条件 ==================== -->
<el-card shadow="never" class="search-card">
<el-form ref="searchFormRef" :model="searchForm" label-width="90px" class="search-form">
<el-row :gutter="24">
<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
<el-form-item label="学员队名称">
<el-input v-model="searchForm.xydmc" placeholder="请输入学员队名称" clearable @keyup.enter.native="handleSearch" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
<el-form-item label="年级">
<el-input v-model="searchForm.nj" placeholder="请输入年级" clearable @keyup.enter.native="handleSearch" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
<el-form-item label="专业代号">
<el-input v-model="searchForm.zydh" placeholder="请输入专业代号" clearable @keyup.enter.native="handleSearch" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
<el-form-item label="学员队类型">
<el-select v-model="searchForm.xydlx" placeholder="请选择学员队类型" clearable filterable class="w-full">
<el-option
v-for="item in teamTypeOptions"
:key="item.dictCode || item.dictValue"
:label="item.dictLabel"
:value="item.dictLabel"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
<el-form-item label="任务类别">
<el-input v-model="searchForm.rwlb" placeholder="请输入任务类别" clearable @keyup.enter.native="handleSearch" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="8">
<el-form-item label="虚实类型">
<el-select v-model="searchForm.xslx" placeholder="请选择虚实类型" clearable class="w-full">
<el-option label="否" :value="0" />
<el-option label="是" :value="1" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row class="search-actions-row">
<el-col :span="24" class="search-actions">
<el-button type="primary" icon="el-icon-search" @click="handleSearch">查询</el-button>
<el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
</el-col>
</el-row>
</el-form>
</el-card>
<!-- ==================== 2. 工具栏:新增 / 导入 / 导出 ==================== -->
<div class="toolbar">
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">新增学员队</el-button>
<el-button icon="el-icon-download" @click="handleExport">导出 Excel</el-button>
<el-button icon="el-icon-download" @click="handleDownloadTemplate">模板下载</el-button>
<div class="file-input">
<input ref="fileInputRef" type="file" accept=".xls,.xlsx" style="display: none" @change="handleFileChange" />
<el-button icon="el-icon-folder-opened" @click="handleChooseFile">选择文件</el-button>
<span class="file-name">{{ fileName }}</span>
<el-button type="primary" icon="el-icon-upload2" :loading="uploading" @click="handleUpload">上传数据</el-button>
</div>
</div>
<!-- ==================== 3. 列表 ==================== -->
<el-card shadow="never" class="table-card">
<div class="table-header">
<span class="table-title">教学班次列表:</span>
</div>
<el-table v-loading="loading" :data="tableData" stripe border highlight-current-row>
<el-table-column prop="xydbh" label="学员队编号" min-width="120" show-overflow-tooltip />
<el-table-column prop="xydmc" label="学员队名称" min-width="130" show-overflow-tooltip />
<el-table-column prop="nj" label="年级" width="90" align="center" />
<el-table-column prop="zydh" label="专业代号" min-width="110" show-overflow-tooltip />
<el-table-column prop="rwlb" label="任务类别" width="100" align="center" />
<el-table-column label="虚实类型" width="90" align="center">
<template slot-scope="{ row }">
<el-tag :type="Number(row.xslx) === 1 ? 'success' : 'info'" size="mini">
{{ Number(row.xslx) === 1 ? '是' : '否' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="在校状态" width="90" align="center">
<template slot-scope="{ row }">
<el-tag :type="Number(row.zxzt) === 1 ? 'success' : 'danger'" size="mini">
{{ Number(row.zxzt) === 1 ? '在校' : '毕业' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="xydrs" label="人数" width="80" align="center" />
<el-table-column label="入学日期" width="110" align="center">
<template slot-scope="{ row }">{{ formatDate(row.rxrq) }}</template>
</el-table-column>
<el-table-column label="毕业日期" width="110" align="center">
<template slot-scope="{ row }">{{ formatDate(row.byrq) }}</template>
</el-table-column>
<el-table-column label="操作" width="190" align="center" fixed="right">
<template slot-scope="{ row }">
<el-button type="text" size="small" icon="el-icon-view" @click="handleDetail(row)">详情</el-button>
<el-button type="text" size="small" icon="el-icon-edit" @click="handleEdit(row)">编辑</el-button>
<el-button type="text" size="small" class="text-danger" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
:current-page="pageNum"
:page-size="pageSize"
:total="total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
class="pagination-wrapper"
@current-change="handlePageChange"
@size-change="handleSizeChange"
/>
</el-card>
<!-- ==================== 4. 新增/编辑对话框 ==================== -->
<el-dialog
:visible="formDialogVisible"
:title="dialogTitle"
width="760px"
:close-on-click-modal="false"
@update:visible="val => formDialogVisible = val"
>
<el-form ref="formRef" :model="form" :rules="formRules" label-width="130px" class="form-dialog-form">
<el-divider content-position="left">基本信息</el-divider>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="学员队编号" prop="xydbh">
<el-input v-model="form.xydbh" placeholder="留空自动生成(XYD+年级+四位序号)" :disabled="!isAdd" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="学员队名称" prop="xydmc">
<el-input v-model="form.xydmc" placeholder="请输入学员队名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="学员队人数" prop="xydrs">
<el-input-number v-model="form.xydrs" :min="0" controls-position="right" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="年级">
<el-input v-model="form.nj" placeholder="请输入年级" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="专业代号" prop="zydh">
<el-select
v-model="form.zydh"
:loading="majorOptionsLoading"
placeholder="请选择专业"
clearable
filterable
class="w-full"
>
<el-option
v-for="item in majorOptions"
:key="item.zydh"
:label="formatMajorLabel(item)"
:value="item.zydh"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="专业教室编号">
<el-input v-model="form.zyjsbh" placeholder="请输入专业教室编号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="学员队类型" prop="xydlx">
<el-select v-model="form.xydlx" placeholder="请选择学员队类型" filterable class="w-full">
<el-option
v-for="item in teamTypeOptions"
:key="item.dictCode || item.dictValue"
:label="item.dictLabel"
:value="item.dictLabel"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="节次类别" prop="jclb">
<el-select v-model="form.jclb" placeholder="请选择节次类别" filterable class="w-full">
<el-option
v-for="item in sessionCategoryOptions"
:key="item.dictCode || item.dictValue"
:label="item.dictLabel"
:value="item.dictLabel"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="系统模式" prop="xtms">
<el-select v-model="form.xtms" placeholder="请选择系统模式" filterable class="w-full">
<el-option
v-for="item in systemModeOptions"
:key="item.dictCode || item.dictValue"
:label="item.dictLabel"
:value="item.dictLabel"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="简称">
<el-input v-model="form.jc" placeholder="请输入简称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="所属单位">
<el-select v-model="form.ssdw" placeholder="请选择所属单位" clearable filterable class="w-full">
<el-option
v-for="item in unitCategoryOptions"
:key="item.dictCode || item.dictValue"
:label="item.dictLabel"
:value="item.dictLabel"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-divider content-position="left">培训与状态</el-divider>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="任务类别" prop="rwlb">
<el-input v-model="form.rwlb" placeholder="请输入任务类别" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="虚实类型" prop="xslx">
<el-select v-model="form.xslx" class="w-full">
<el-option label="否" :value="0" />
<el-option label="是" :value="1" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="主体培训任务" prop="ztpxrw">
<el-select v-model="form.ztpxrw" class="w-full" @change="handlePrimaryTrainingTaskChange">
<el-option label="是" :value="1" />
<el-option label="否" :value="0" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="培训任务标识号">
<el-select
v-model="form.pxrwbsh"
:loading="trainingTaskLoading"
placeholder="请选择培训任务"
clearable
filterable
class="w-full"
>
<el-option
v-for="item in trainingTaskOptions"
:key="item.bsh"
:label="formatTrainingTaskLabel(item)"
:value="item.bsh"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="在校状态">
<el-select v-model="form.zxzt" placeholder="请选择在校状态" clearable class="w-full">
<el-option label="在校" :value="1" />
<el-option label="毕业" :value="0" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="入学日期" prop="rxrq">
<el-date-picker v-model="form.rxrq" type="date" value-format="yyyy-MM-dd" placeholder="请选择入学日期" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="毕业日期" prop="byrq">
<el-date-picker v-model="form.byrq" type="date" value-format="yyyy-MM-dd" placeholder="请选择毕业日期" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="备注">
<el-input v-model="form.bz" type="textarea" :rows="2" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="formDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="formSaving" @click="handleFormSubmit">确定</el-button>
</div>
</el-dialog>
<!-- ==================== 5. 详情对话框 ==================== -->
<el-dialog title="学员队详情" :visible="detailDialogVisible" width="720px" :close-on-click-modal="false"
@update:visible="val => detailDialogVisible = val">
<div v-loading="detailLoading" class="detail-body">
<el-descriptions :column="2" border>
<el-descriptions-item label="学员队编号">{{ detailForm.xydbh || '-' }}</el-descriptions-item>
<el-descriptions-item label="学员队名称">{{ detailForm.xydmc || '-' }}</el-descriptions-item>
<el-descriptions-item label="学员队人数">{{ fmtValue(detailForm.xydrs) }}</el-descriptions-item>
<el-descriptions-item label="年级">{{ detailForm.nj || '-' }}</el-descriptions-item>
<el-descriptions-item label="专业代号">{{ detailForm.zydh || '-' }}</el-descriptions-item>
<el-descriptions-item label="专业教室编号">{{ detailForm.zyjsbh || '-' }}</el-descriptions-item>
<el-descriptions-item label="学员队类型">{{ detailForm.xydlx || '-' }}</el-descriptions-item>
<el-descriptions-item label="节次类别">{{ detailForm.jclb || '-' }}</el-descriptions-item>
<el-descriptions-item label="系统模式">{{ detailForm.xtms || '-' }}</el-descriptions-item>
<el-descriptions-item label="简称">{{ detailForm.jc || '-' }}</el-descriptions-item>
<el-descriptions-item label="所属单位">{{ detailForm.ssdw || '-' }}</el-descriptions-item>
<el-descriptions-item label="任务类别">{{ detailForm.rwlb || '-' }}</el-descriptions-item>
<el-descriptions-item label="虚实类型">
<el-tag :type="Number(detailForm.xslx) === 1 ? 'success' : 'info'" size="mini">
{{ Number(detailForm.xslx) === 1 ? '是' : '否' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="主体培训任务">
<el-tag :type="Number(detailForm.ztpxrw) === 1 ? 'success' : 'info'" size="mini">
{{ Number(detailForm.ztpxrw) === 1 ? '是' : '否' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="培训任务标识号">{{ detailForm.pxrwbsh || '-' }}</el-descriptions-item>
<el-descriptions-item label="在校状态">
<el-tag :type="Number(detailForm.zxzt) === 1 ? 'success' : 'danger'" size="mini">
{{ Number(detailForm.zxzt) === 1 ? '在校' : '毕业' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="入学日期">{{ detailForm.rxrq || '-' }}</el-descriptions-item>
<el-descriptions-item label="毕业日期">{{ detailForm.byrq || '-' }}</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">{{ detailForm.bz || '-' }}</el-descriptions-item>
</el-descriptions>
</div>
<div slot="footer">
<el-button @click="detailDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { saveAs } from 'file-saver'
import {
listTeam,
getTeam,
addTeam,
updateTeam,
delTeam,
listTrainingTasks,
importTeam,
exportTeam,
downloadTeamTemplate
} from '@/api/studentRecords/team'
import { getDicts } from '@/api/system/dict/data'
import { listMajor } from '@/api/subjectMajor/major'
const TEAM_TYPE_DICT_CODE = 'stu_type'
const SESSION_CATEGORY_DICT_CODE = 'session_category'
const SYSTEM_MODE_DICT_CODE = 'system_mode'
const UNIT_CATEGORY_DICT_CODE = 'sys_unit_category'
const MAJOR_OPTION_PAGE_SIZE = 10000
export default {
name: 'ShiftTeamIndex',
data() {
return {
// ==================== 查询条件 ====================
searchForm: {
xydmc: '',
nj: '',
zydh: '',
xydlx: '',
rwlb: '',
xslx: undefined
},
teamTypeOptions: [],
sessionCategoryOptions: [],
systemModeOptions: [],
unitCategoryOptions: [],
majorOptions: [],
majorOptionsLoading: false,
trainingTaskOptions: [],
trainingTaskLoading: false,
// ==================== 文件导入 ====================
selectedFile: null,
uploading: false,
// ==================== 列表数据 ====================
loading: false,
tableData: [],
total: 0,
pageNum: 1,
pageSize: 20,
// ==================== 新增/编辑 ====================
formDialogVisible: false,
dialogTitle: '新增学员队',
isAdd: true,
formSaving: false,
form: this.createEmptyForm(),
formRules: {
xydbh: [{ required: false }],
xydmc: [{ required: true, message: '请输入学员队名称', trigger: 'blur' }],
xydrs: [{ required: true, message: '请输入学员队人数', trigger: 'change' }],
zydh: [{ required: true, message: '请选择专业', trigger: 'change' }],
rwlb: [{ required: true, message: '请输入任务类别', trigger: 'blur' }],
xslx: [{ required: true, message: '请选择虚实类型', trigger: 'change' }],
rxrq: [{ required: true, message: '请选择入学日期', trigger: 'change' }],
byrq: [{ required: true, message: '请选择毕业日期', trigger: 'change' }],
xydlx: [{ required: true, message: '请选择学员队类型', trigger: 'change' }],
jclb: [{ required: true, message: '请选择节次类别', trigger: 'change' }],
xtms: [{ required: true, message: '请选择系统模式', trigger: 'change' }],
ztpxrw: [{ required: true, message: '请选择主体培训任务', trigger: 'change' }]
},
// ==================== 详情 ====================
detailDialogVisible: false,
detailLoading: false,
detailForm: {}
}
},
computed: {
fileName() {
return (this.selectedFile && this.selectedFile.name) || '未选择任何文件'
}
},
mounted() {
this.loadTeamDictionaries()
this.loadMajorOptions()
this.fetchList()
},
methods: {
/** 加载学员队相关字典。 */
async loadTeamDictionaries() {
const emptyResponse = { data: [] }
const [teamTypeResponse, sessionCategoryResponse, systemModeResponse, unitCategoryResponse] = await Promise.all([
getDicts(TEAM_TYPE_DICT_CODE).catch(() => emptyResponse),
getDicts(SESSION_CATEGORY_DICT_CODE).catch(() => emptyResponse),
getDicts(SYSTEM_MODE_DICT_CODE).catch(() => emptyResponse),
getDicts(UNIT_CATEGORY_DICT_CODE).catch(() => emptyResponse)
])
this.teamTypeOptions = teamTypeResponse.data || []
this.sessionCategoryOptions = sessionCategoryResponse.data || []
this.systemModeOptions = systemModeResponse.data || []
this.unitCategoryOptions = unitCategoryResponse.data || []
},
/** 加载专业下拉选项,标签同时包含名称、代码和代号,便于直接模糊搜索。 */
async loadMajorOptions() {
this.majorOptionsLoading = true
try {
const response = await listMajor({
pageNum: 1,
pageSize: MAJOR_OPTION_PAGE_SIZE,
ty: false
})
const data = response.data || {}
this.majorOptions = Array.isArray(data.records) ? data.records : []
} catch (error) {
this.majorOptions = []
} finally {
this.majorOptionsLoading = false
}
},
formatMajorLabel(item) {
const identifiers = [item.zydm, item.zydh].filter(Boolean).join(' / ')
if (!item.zymc) {
return identifiers
}
return identifiers ? `${item.zymc}(${identifiers})` : item.zymc
},
createEmptyForm() {
return {
xydbh: '',
xydmc: '',
xydrs: undefined,
nj: '',
zydh: '',
zyjsbh: '',
bz: '',
zxzt: undefined,
rwlb: '',
xslx: 1,
rxrq: '',
byrq: '',
xydlx: '',
pxrwbsh: '',
jclb: '',
xtms: '',
jsonzd: '',
jc: '',
ssdw: '',
ztpxrw: 1
}
},
// ==================== 日期/数值展示 ====================
formatDate(val) {
if (!val) return ''
return String(val).substring(0, 10)
},
fmtValue(val) {
return val === null || val === undefined || val === '' ? '-' : val
},
normalizeSchoolStatus(value) {
if (value === 1 || value === '1' || value === '在校') {
return 1
}
if (value === 0 || value === '0' || value === '毕业') {
return 0
}
return undefined
},
/** 构建查询条件(不含分页),供列表查询与导出共用 */
buildQuery() {
const params = {}
;['xydmc', 'nj', 'zydh', 'xydlx', 'rwlb'].forEach(key => {
const value = this.searchForm[key]
if (value !== '' && value !== null && value !== undefined) {
params[key] = String(value).trim()
}
})
if (this.searchForm.xslx !== undefined && this.searchForm.xslx !== '' && this.searchForm.xslx !== null) {
params.xslx = this.searchForm.xslx
}
return params
},
// ==================== 查询列表 ====================
fetchList() {
this.loading = true
const params = {
pageNum: this.pageNum,
pageSize: this.pageSize,
...this.buildQuery()
}
listTeam(params).then(response => {
const data = response.data || {}
this.tableData = data.records || []
this.total = data.total || 0
this.loading = false
}).catch(() => {
this.tableData = []
this.total = 0
this.loading = false
})
},
handleSearch() {
this.pageNum = 1
this.fetchList()
},
handleReset() {
this.$refs.searchFormRef.resetFields()
this.pageNum = 1
this.fetchList()
},
handlePageChange(current) {
this.pageNum = current
this.fetchList()
},
handleSizeChange(size) {
this.pageSize = size
this.pageNum = 1
this.fetchList()
},
// ==================== 新增/编辑 ====================
handleAdd() {
this.isAdd = true
this.dialogTitle = '新增学员队'
this.form = this.createEmptyForm()
this.loadTrainingTaskOptions(this.form.ztpxrw)
this.formDialogVisible = true
this.$nextTick(() => {
this.$refs.formRef && this.$refs.formRef.clearValidate()
})
},
handleEdit(row) {
this.isAdd = false
this.dialogTitle = '编辑学员队'
this.form = this.createEmptyForm()
this.formDialogVisible = true
this.$nextTick(() => {
this.$refs.formRef && this.$refs.formRef.clearValidate()
})
getTeam(row.xydbh).then(response => {
const d = response.data || {}
this.form = {
xydbh: d.xydbh || '',
xydmc: d.xydmc || '',
xydrs: d.xydrs !== null && d.xydrs !== undefined ? d.xydrs : undefined,
nj: d.nj || '',
zydh: d.zydh || '',
zyjsbh: d.zyjsbh || '',
bz: d.bz || '',
zxzt: this.normalizeSchoolStatus(d.zxzt),
rwlb: d.rwlb || '',
xslx: d.xslx !== null && d.xslx !== undefined ? Number(d.xslx) : 1,
rxrq: this.formatDate(d.rxrq),
byrq: this.formatDate(d.byrq),
xydlx: d.xydlx || '',
pxrwbsh: d.pxrwbsh || '',
jclb: d.jclb || '',
xtms: d.xtms || '',
jsonzd: d.jsonzd || '',
jc: d.jc || '',
ssdw: d.ssdw || '',
ztpxrw: d.ztpxrw !== null && d.ztpxrw !== undefined ? Number(d.ztpxrw) : 1
}
this.loadTrainingTaskOptions(this.form.ztpxrw)
}).catch(() => {})
},
/** 按主体培训任务标志加载后端培训任务选项。 */
async loadTrainingTaskOptions(ztpxrw) {
this.trainingTaskLoading = true
try {
const response = await listTrainingTasks(ztpxrw)
this.trainingTaskOptions = Array.isArray(response.data) ? response.data : []
} catch (error) {
this.trainingTaskOptions = []
} finally {
this.trainingTaskLoading = false
}
},
handlePrimaryTrainingTaskChange(ztpxrw) {
this.form.pxrwbsh = ''
this.loadTrainingTaskOptions(ztpxrw)
},
formatTrainingTaskLabel(item) {
if (!item.mc) {
return item.bsh
}
return `${item.mc}(${item.bsh})`
},
buildPayload() {
const f = this.form
const payload = {
xydbh: f.xydbh,
xydmc: f.xydmc,
zydh: f.zydh,
rwlb: f.rwlb,
xydlx: f.xydlx,
jclb: f.jclb,
xtms: f.xtms,
xslx: Number(f.xslx),
ztpxrw: Number(f.ztpxrw)
}
// 数值字段
if (f.xydrs !== '' && f.xydrs !== null && f.xydrs !== undefined) payload.xydrs = Number(f.xydrs)
// 字符串字段
if (f.zxzt !== '' && f.zxzt !== null && f.zxzt !== undefined) payload.zxzt = String(f.zxzt)
// 后端字段为 LocalDateTime,日期选择器值统一补齐当天零点。
if (f.rxrq) payload.rxrq = `${this.formatDate(f.rxrq)}T00:00:00`
if (f.byrq) payload.byrq = `${this.formatDate(f.byrq)}T00:00:00`
// 其余文本字段非空才传
;['nj', 'zyjsbh', 'pxrwbsh', 'jsonzd', 'jc', 'ssdw', 'bz'].forEach(key => {
if (f[key] !== '' && f[key] !== null && f[key] !== undefined) {
payload[key] = String(f[key]).trim()
}
})
return payload
},
handleFormSubmit() {
this.$refs.formRef.validate(valid => {
if (!valid) return
this.formSaving = true
const payload = this.buildPayload()
const request = this.isAdd ? addTeam(payload) : updateTeam(payload)
request.then(() => {
this.$message.success(this.isAdd ? '新增成功' : '修改成功')
this.formDialogVisible = false
this.fetchList()
}).catch(() => {
}).finally(() => {
this.formSaving = false
})
})
},
// ==================== 删除 ====================
handleDelete(row) {
this.$confirm(`确定要删除「${row.xydmc || row.xydbh}」吗?`, '系统提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
return delTeam(row.xydbh)
}).then(() => {
this.$message.success('删除成功')
this.fetchList()
}).catch(() => {})
},
// ==================== 详情 ====================
handleDetail(row) {
this.detailDialogVisible = true
this.detailLoading = true
this.detailForm = {}
getTeam(row.xydbh).then(response => {
this.detailForm = response.data || {}
this.detailLoading = false
}).catch(() => {
this.detailLoading = false
})
},
// ==================== 导出 ====================
handleExport() {
exportTeam(this.buildQuery()).then(blob => {
saveAs(blob, '班次(学员队)管理.xlsx')
this.$message.success('导出成功')
}).catch(() => {})
},
// ==================== 模板下载 / 导入 ====================
handleDownloadTemplate() {
downloadTeamTemplate().then(blob => {
saveAs(blob, '学员队导入模板.xlsx')
this.$message.success('模板下载成功')
}).catch(() => {})
},
handleChooseFile() {
this.$refs.fileInputRef && this.$refs.fileInputRef.click()
},
handleFileChange(e) {
const input = e.target
this.selectedFile = (input.files && input.files[0]) || null
},
handleUpload() {
if (!this.selectedFile) {
this.$message.warning('请先选择文件')
return
}
this.uploading = true
importTeam(this.selectedFile).then(res => {
const count = res && res.data
if (count !== null && count !== undefined) {
this.$message.success(`导入成功,共 ${count} 条记录`)
} else {
this.$message.success((res && res.msg) || '导入成功')
}
this.selectedFile = null
this.$refs.fileInputRef && (this.$refs.fileInputRef.value = '')
this.pageNum = 1
this.fetchList()
}).catch(() => {}).finally(() => {
this.uploading = false
})
}
}
}
</script>
<style scoped lang="scss">
.app-container {
padding: 20px;
}
.shift-team-page {
.w-full {
width: 100%;
}
.search-card {
margin-bottom: 16px;
.search-form {
.search-actions-row {
margin-top: 2px;
border-top: 1px dashed #ebeef5;
padding-top: 14px;
}
.search-actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
}
}
}
.toolbar {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
margin-bottom: 16px;
.file-input {
display: flex;
align-items: center;
gap: 8px;
.file-name {
font-size: 12px;
color: #909399;
max-width: 180px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
.table-card {
.table-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
.table-title {
font-size: 16px;
font-weight: 600;
color: #303133;
}
}
.pagination-wrapper {
display: flex;
justify-content: flex-end;
margin-top: 16px;
}
}
.form-dialog-form {
max-height: 62vh;
overflow-y: auto;
padding-right: 6px;
::v-deep .el-divider--horizontal {
margin: 4px 0 16px;
}
}
.text-danger {
color: #f56c6c;
padding: 0;
}
}
</style>