Files
school-developer/src/views/professional/professionalteacherhonor/index.vue
zhoutianchi 37e709cf7e 1
2026-02-06 14:24:42 +08:00

442 lines
14 KiB
Vue
Executable File
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="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<!-- 搜索表单 -->
<search-form
v-show="showSearch"
:model="search"
ref="searchFormRef"
@keyup-enter="handleFilter"
>
<template #default="{ visible }">
<template v-if="visible">
<el-form-item label="审核状态" prop="state">
<el-select
v-model="search.state"
clearable
placeholder="请选择审核状态"
>
<el-option
v-for="item in professionalState"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="工号" prop="teacherNo">
<el-input
v-model="search.teacherNo"
clearable
placeholder="请输入工号"
/>
</el-form-item>
<el-form-item label="姓名" prop="teacherName">
<el-input
v-model="search.teacherName"
clearable
placeholder="请输入姓名"
/>
</el-form-item>
</template>
</template>
<!-- 操作按钮 -->
<template #actions>
<el-form-item>
<el-button type="primary" @click="handleFilter" icon="Search">查询</el-button>
<el-button @click="resetQuery" icon="Refresh">重置</el-button>
</el-form-item>
</template>
</search-form>
<!-- 操作按钮 -->
<el-row>
<div class="mb15" style="width: 100%;">
<el-button
v-if="hasAuth('professional_professionalteacherhonor_add')"
type="primary"
icon="FolderAdd"
@click="handleAdd">
</el-button>
<el-button
type="warning"
plain
icon="Download"
@click="handleDownLoadWord"
:loading="exportLoading">导出信息
</el-button>
<el-button
v-auth="'professional_teacherinfo_import'"
type="primary"
plain
icon="UploadFilled"
:loading="exportLoading"
@click="handleImportDialog">导入信息
</el-button>
</div>
</el-row>
<!-- 表格 -->
<el-table
ref="tableRef"
:data="state.dataList"
row-key="id"
v-loading="state.loading"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="state" label="审核状态" width="130" align="center">
<template #default="scope">
<DetailPopover
v-if="scope.row.state === '-2' && scope.row.backReason"
title="审核详情"
placement="top"
:width="300"
:items="[
{ label: '审核状态', layout: 'horizontal', content: getAuditStateTagConfig(scope.row.state)?.label },
{ label: '驳回理由', content: scope.row.backReason, contentClass: 'reason-content' }
]"
>
<template #reference>
<ClickableTag
:type="getAuditStateTagConfig(scope.row.state)?.type || 'danger'"
:left-icon="getAuditStateTagConfig(scope.row.state)?.leftIcon"
:effect="getAuditStateTagConfig(scope.row.state)?.effect || 'dark'"
>
{{ getAuditStateTagConfig(scope.row.state)?.label || '已驳回' }}
</ClickableTag>
</template>
<template #content-0>
<ClickableTag
:type="getAuditStateTagConfig(scope.row.state)?.type || 'danger'"
:left-icon="getAuditStateTagConfig(scope.row.state)?.leftIcon"
:effect="getAuditStateTagConfig(scope.row.state)?.effect || 'dark'"
:right-icon="null"
>
{{ getAuditStateTagConfig(scope.row.state)?.label || '已驳回' }}
</ClickableTag>
</template>
<template #content-1>
<div class="reason-content">
<el-icon class="reason-icon"><Warning /></el-icon>
<span>{{ scope.row.backReason }}</span>
</div>
</template>
</DetailPopover>
<AuditState v-else :state="scope.row.state" :options="auditStateOptions" />
</template>
</el-table-column>
<el-table-column label="姓名/工号" min-width="150" align="center">
<template #default="scope">
<TeacherNameNo :name="scope.row.realName" :no="scope.row.teacherNo" />
</template>
</el-table-column>
<el-table-column prop="honor" label="荣誉" min-width="150" align="center" show-overflow-tooltip />
<el-table-column prop="honorCompany" label="表彰单位" min-width="150" align="center" show-overflow-tooltip />
<el-table-column prop="year" label="年份" width="100" align="center" />
<el-table-column label="证明材料" width="120" align="center">
<template #default="scope">
<el-button
v-if="scope.row.attachment && scope.row.attachment !== ''"
type="primary"
link
icon="Document"
@click="showEdvince(scope.row)">查看
</el-button>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="操作" width="280" align="center" fixed="right">
<template #default="scope">
<el-button
v-if="hasAuth('professional_professionalteacherhonor_edit') && (scope.row.state === '0' || scope.row.state === '-2')"
type="primary"
link
icon="edit-pen"
@click="handleEdit(scope.row)">修改
</el-button>
<el-button
v-if="hasAuth('professional_professionalteacherhonor_exam') && scope.row.canExam"
type="success"
link
icon="CircleCheck"
@click="changeState(scope.row, 1)">通过
</el-button>
<el-button
v-if="hasAuth('professional_professionalteacherhonor_exam') && scope.row.canDeptExam"
type="success"
link
icon="CircleCheck"
@click="changeState(scope.row, 1)">部门通过
</el-button>
<el-button
v-if="hasAuth('professional_professionalteacherhonor_exam') && scope.row.canBack"
type="danger"
link
icon="CircleClose"
@click="changeState(scope.row, -2)">驳回
</el-button>
<el-button
v-if="hasAuth('professional_professionalteacherhonor_exam') && scope.row.canDeptBack"
type="danger"
link
icon="CircleClose"
@click="changeState(scope.row, -2)">部门驳回
</el-button>
<el-button
v-if="hasAuth('professional_professionalteacherhonor_del')"
type="danger"
link
icon="delete"
@click="handleDel(scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
v-bind="state.pagination"
@current-change="currentChangeHandle"
@size-change="sizeChangeHandle"
/>
<!-- 材料预览图片直接显示PDF 在组件内部 dialog 中显示 -->
<preview-file
v-for="src in imgUrl"
:key="src.title"
:authSrc="src.url"
dialog-title="综合表彰材料"
/>
<!-- 子组件 -->
<ProfessionalBackResaon ref="backReasonRef" @refreshData="handleFilter" />
<DataForm ref="dataFormRef" @refreshData="handleFilter" />
<import-teacher-other-info ref="importTeacherOtherInfoRef" @refreshData="handleFilter" />
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, nextTick } from 'vue'
import { BasicTableProps, useTable } from '/@/hooks/table'
import { useAuth } from '/@/hooks/auth'
import { useMessage } from '/@/hooks/message'
import { useMessageBox } from '/@/hooks/message'
import { useDict } from '/@/hooks/dict'
import {
fetchList,
examObj,
delObj
} from '/@/api/professional/professionaluser/professionalteacherhonor'
import { PROFESSIONAL_AUDIT_STATE_OPTIONS, getStatusConfig } from '/@/config/global'
import { defineAsyncComponent } from 'vue'
import {makeExportTeacherInfoByTypeTask} from "/@/api/professional/professionalfile";
const TeacherNameNo = defineAsyncComponent(() => import('/@/components/TeacherNameNo/index.vue'))
const AuditState = defineAsyncComponent(() => import('/@/components/AuditState/index.vue'))
const ClickableTag = defineAsyncComponent(() => import('/@/components/ClickableTag/index.vue'))
const DetailPopover = defineAsyncComponent(() => import('/@/components/DetailPopover/index.vue'))
const ProfessionalBackResaon = defineAsyncComponent(() => import('/@/views/professional/common/professional-back-resaon.vue'))
const DataForm = defineAsyncComponent(() => import('./form.vue'))
const previewFile = defineAsyncComponent(() => import('/@/components/tools/preview-file.vue'))
const ImportTeacherOtherInfo = defineAsyncComponent(() => import('/@/views/professional/common/import-teacher-other-info.vue'))
// 消息提示 hooks
const message = useMessage()
const messageBox = useMessageBox()
// 字典数据
const { professional_state: professionalState } = useDict('professional_state')
// 审核状态选项
const auditStateOptions = PROFESSIONAL_AUDIT_STATE_OPTIONS
// 审核状态转 ClickableTag 配置(用 options 的 icon 字符串,与 AuditState 一致为实心)
const getAuditStateTagConfig = (state: string | number) => {
const opt = getStatusConfig(auditStateOptions, state)
if (!opt) return null
return { type: opt.type, label: opt.label, leftIcon: opt.icon, effect: opt.effect || 'dark' }
}
// 无权限即无节点
const { hasAuth } = useAuth()
// 表格引用
const tableRef = ref()
const searchFormRef = ref()
const backReasonRef = ref()
const dataFormRef = ref()
const showSearch = ref(true)
// 搜索表单数据
const search = reactive({
state: '',
teacherNo: '',
teacherName: ''
})
// 材料预览
const imgUrl = ref<Array<{ title: string; url: string }>>([])
// 导出加载状态
const exportLoading = ref(false)
const importTeacherOtherInfoRef = ref()
// 配置 useTable
const state: BasicTableProps = reactive<BasicTableProps>({
pageList: async (params: any) => {
const response = await fetchList(params)
return {
data: {
records: response.data.records || [],
total: response.data.total || 0
}
}
},
queryForm: search
})
const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state)
// 查看证明材料:构造预览列表,交给 auth-img 处理
const showEdvince = (row: any) => {
imgUrl.value = []
nextTick(() => {
const list = row.attachment ? [row.attachment] : []
list.forEach(v => {
imgUrl.value.push({
title: '',
url: v
})
})
})
}
// 审核状态变更
const changeState = (row: any, val: number) => {
if (val === 1) {
// 通过
const str = '通过'
messageBox.confirm(`是否确认${str}${row.teacherName || row.realName}的申请`).then(async () => {
try {
await examObj({
id: row.id,
state: val
})
message.success('操作成功')
getDataList()
} catch (error: any) {
// 处理业务错误
message.error(error.msg)
}
}).catch(() => {})
} else if (val === -2) {
// 驳回
const newRow = JSON.parse(JSON.stringify(row))
backReasonRef.value?.init(newRow, 'honor')
}
}
// 查询
const handleFilter = () => {
getDataList()
}
// 重置查询
const resetQuery = () => {
Object.assign(search, {
state: '',
teacherNo: '',
teacherName: ''
})
getDataList()
}
// 打开新增窗口
const handleAdd = () => {
dataFormRef.value?.openDialog()
}
// 打开编辑窗口
const handleEdit = (row: any) => {
dataFormRef.value?.openDialog(row)
}
// 删除
const handleDel = (row: any) => {
messageBox.confirm('是否确认删除该条记录').then(async () => {
try {
await delObj(row.id)
message.success('删除成功')
// 如果当前页只剩一条数据,且不是第一页,则跳转到上一页
if (state.pagination && state.dataList && state.dataList.length === 1 && state.pagination.current && state.pagination.current > 1) {
state.pagination.current = state.pagination.current - 1
}
getDataList()
} catch (error: any) {
// 处理业务错误
message.error(error.msg)
}
}).catch(() => {})
}
// 导出
const handleDownLoadWord = async () => {
exportLoading.value = true;
let params = Object.assign(search, { type: 'P20006' });
makeExportTeacherInfoByTypeTask(params).then((res: any) => {
message.success('后台下载进行中,请稍后查看任务列表');
});
setTimeout(() => {
exportLoading.value = false;
}, 3000); // 5分钟后自动关闭
}
// 打开导入弹窗
const handleImportDialog = () => {
importTeacherOtherInfoRef.value?.init('honor')
}
// 表格数据由 useTablecreatedIsNeed 默认 true在挂载时自动请求
</script>
<style lang="scss" scoped>
/* 驳回理由展示(与 backSchoolCheckin 一致) */
.reason-content {
display: flex;
align-items: flex-start;
gap: 8px;
padding: 8px 12px;
background-color: #fef0f0;
border-radius: 4px;
border-left: 3px solid #f56c6c;
.reason-icon {
color: #f56c6c;
font-size: 16px;
flex-shrink: 0;
margin-top: 2px;
}
span {
color: #f56c6c;
font-size: 13px;
line-height: 1.6;
word-break: break-all;
flex: 1;
}
}
</style>