Files
school-developer/src/views/professional/professionaltitlerelation/index.vue
guochunsi c6da6e286f a
2026-01-30 16:29:15 +08:00

460 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="realName">
<el-input
v-model="search.realName"
clearable
placeholder="请输入姓名"
/>
</el-form-item>
<el-form-item label="职称" prop="professionalTitleConfigId">
<el-select
v-model="search.professionalTitleConfigId"
clearable
filterable
placeholder="请选择职称"
>
<el-option
v-for="item in professionalTitleList"
:key="item.id"
:label="item.professionalTitle"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="专业技术职务" prop="majorStation">
<el-select
v-model="search.majorStation"
clearable
filterable
placeholder="请选择专业技术职务"
>
<el-option
v-for="item in majorStationList"
:key="item.id"
:label="item.majorStationName"
:value="item.id"
/>
</el-select>
</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_professionaltitlerelation_add')"
type="primary"
icon="FolderAdd"
@click="handleAdd">
</el-button>
<el-button
v-if="hasAuth('professional_teacherbase_export')"
class="ml10"
type="warning"
plain
icon="Download"
@click="handleDownLoadWord"
:loading="exportLoading">导出信息
</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="120" align="center">
<template #default="scope">
<AuditState :state="scope.row.state" />
</template>
</el-table-column>
<el-table-column label="姓名/工号" min-width="150" align="center" show-overflow-tooltip>
<template #default="scope">
<TeacherNameNo :name="scope.row.realName" :no="scope.row.teacherNo" />
</template>
</el-table-column>
<el-table-column prop="professionalTitleConfigId" label="职称" min-width="150" align="center" show-overflow-tooltip>
<template #default="scope">
{{ getProfessionalTitleName(scope.row.professionalTitleConfigId) }}
</template>
</el-table-column>
<el-table-column prop="majorStation" label="专业技术职务" min-width="150" align="center" show-overflow-tooltip>
<template #default="scope">
{{ getMajorStationName(scope.row.majorStation) }}
</template>
</el-table-column>
<el-table-column label="证明材料" width="120" align="center">
<template #default="scope">
<el-button
v-if="scope.row.evidence && scope.row.evidence !== ''"
type="primary"
link
icon="Document"
@click="handlePreview(scope.row.srcList)">查看
</el-button>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column prop="backReason" label="驳回理由" min-width="150" align="center" show-overflow-tooltip />
<el-table-column label="操作" width="280" align="center" fixed="right">
<template #default="scope">
<el-button
v-if="hasAuth('professional_professionaltitlerelation_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_professionaltitlerelation_exam') && scope.row.canExam"
type="success"
link
icon="CircleCheck"
@click="changeState(scope.row, 1)">通过
</el-button>
<el-button
v-if="hasAuth('professional_professionaltitlerelation_exam') && scope.row.canDeptExam"
type="success"
link
icon="CircleCheck"
@click="changeState(scope.row, 1)">部门通过
</el-button>
<el-button
v-if="hasAuth('professional_professionaltitlerelation_exam') && scope.row.canBack"
type="danger"
link
icon="CircleClose"
@click="changeState(scope.row, -2)">驳回
</el-button>
<el-button
v-if="hasAuth('professional_professionaltitlerelation_exam') && scope.row.canDeptBack"
type="danger"
link
icon="CircleClose"
@click="changeState(scope.row, -2)">部门驳回
</el-button>
<el-button
v-if="hasAuth('professional_professionaltitlerelation_del')"
type="danger"
link
icon="delete"
style="margin-left: 12px"
@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="职称材料"
/>
<!-- 子组件 -->
<MultiDialog ref="multiDialogRef" @getList="getDataList" :page="state.pagination" :nowRow="null" />
<DataForm ref="dataFormRef" @refreshData="handleFilter" />
<ProfessionalBackResaon ref="backReasonRef" @refreshData="handleFilter" />
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, 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,
exportRelation
} from '/@/api/professional/professionaluser/professionaltitlerelation'
import { getProfessionalTitleList } from '/@/api/professional/rsbase/professionaltitlelevelconfig'
import { getMajorStationList } from '/@/api/professional/rsbase/professionalmajorstation'
import { defineAsyncComponent } from 'vue'
// 子组件
const TeacherNameNo = defineAsyncComponent(() => import('/@/components/TeacherNameNo/index.vue'))
const AuditState = defineAsyncComponent(() => import('/@/components/AuditState/index.vue'))
const MultiDialog = defineAsyncComponent(() => import('/@/views/professional/teacherbase/multiDialog.vue'))
const DataForm = defineAsyncComponent(() => import('./form.vue'))
const ProfessionalBackResaon = defineAsyncComponent(() => import('/@/views/professional/common/professional-back-resaon.vue'))
const previewFile = defineAsyncComponent(() => import('/@/components/tools/preview-file.vue'))
// 无权限即无节点:使用 useAuth + v-if
const { hasAuth } = useAuth()
// 消息提示 hooks
const message = useMessage()
const messageBox = useMessageBox()
// 字典数据
const { professional_state: professionalState } = useDict('professional_state')
// 表格引用
const tableRef = ref()
const searchFormRef = ref()
const multiDialogRef = ref()
const dataFormRef = ref()
const backReasonRef = ref()
const showSearch = ref(true)
// 搜索表单数据
const search = reactive({
state: '',
teacherNo: '',
realName: '',
professionalTitleConfigId: '',
majorStation: ''
})
// 材料预览
const imgUrl = ref<Array<{ title: string; url: string }>>([])
// 导出加载状态
const exportLoading = ref(false)
// 职称和专业技术职务列表
const professionalTitleList = ref<any[]>([])
const majorStationList = ref<any[]>([])
// 配置 useTable
const state: BasicTableProps = reactive<BasicTableProps>({
pageList: async (params: any) => {
const response = await fetchList(params)
const records = response.data.records || []
// 处理证明材料列表
records.forEach((v: any) => {
if (v.evidence != null) {
v.srcList = [v.evidence]
} else {
v.srcList = []
}
})
return {
data: {
records,
total: response.data.total || 0
}
}
},
queryForm: search
})
const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state)
// 预览材料
const handlePreview = (list: string[]) => {
imgUrl.value = []
nextTick(() => {
list.forEach(v => {
imgUrl.value.push({
title: '',
url: v
})
})
})
}
// 审核状态变更
const changeState = (row: any, val: number) => {
if (val === 1) {
const str = '通过'
messageBox.confirm(`是否确认${str}${row.realName}的申请`).then(async () => {
try {
await examObj({
id: row.id,
state: val
})
message.success('操作成功')
getDataList()
} catch (error: any) {
// 处理业务错误
}
}).catch(() => {})
} else if (val === -2) {
backReasonRef.value?.init(row, 'title')
}
}
// 查询
const handleFilter = () => {
getDataList()
}
// 重置查询
const resetQuery = () => {
Object.assign(search, {
state: '',
teacherNo: '',
realName: '',
professionalTitleConfigId: '',
majorStation: ''
})
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
try {
const response: any = await exportRelation(search)
const blob = new Blob([response as BlobPart])
const fileName = '职称信息.xls'
const elink = document.createElement('a')
elink.download = fileName
elink.style.display = 'none'
elink.href = URL.createObjectURL(blob)
document.body.appendChild(elink)
elink.click()
URL.revokeObjectURL(elink.href)
document.body.removeChild(elink)
message.success('导出成功')
} catch (error) {
message.error('导出失败')
} finally {
exportLoading.value = false
}
}
// 获取职称名称
const getProfessionalTitleName = (id: string | number) => {
const item = professionalTitleList.value.find((item: any) => item.id === id)
return item ? item.professionalTitle : '-'
}
// 获取专业技术职务名称
const getMajorStationName = (id: string | number) => {
const item = majorStationList.value.find((item: any) => item.id === id)
return item ? item.majorStationName : '-'
}
// 加载字典数据
const loadDictData = async () => {
try {
// 加载职称列表
const titleRes = await getProfessionalTitleList()
if (titleRes && titleRes.data) {
professionalTitleList.value = titleRes.data
}
// 加载专业技术职务列表
const majorRes = await getMajorStationList()
if (majorRes && majorRes.data) {
majorStationList.value = majorRes.data
}
} catch (error) {
// Failed to load dict data
}
}
// 初始化:仅加载下拉字典,表格数据由 useTable 在 createdIsNeed: true 时自动请求
onMounted(async () => {
await loadDictData()
})
</script>
<style lang="scss" scoped>
</style>