This commit is contained in:
guochunsi
2026-02-25 10:39:44 +08:00
144 changed files with 76646 additions and 21701 deletions

View File

@@ -0,0 +1,121 @@
<template>
<el-dialog v-model="visible" :title="title" width="600" append-to-body>
<!-- <el-alert-->
<!-- type="warning"-->
<!-- :closable="false"-->
<!-- show-icon-->
<!-- style="margin-bottom: 20px;">-->
<!-- <template #title>-->
<!-- <span>下载模板</span>-->
<!-- </template>-->
<!-- </el-alert>-->
<div style="text-align: center; margin-bottom: 20px">
<el-button type="success" :icon="Download" @click="handleDownloadTemplate">下载模板</el-button>
</div>
<el-upload
ref="uploadRef"
class="upload-demo"
:action="uploadUrl"
:headers="headers"
:accept="'.xls,.xlsx'"
:on-success="handleUploadSuccess"
:on-error="handleAvatarError"
:limit="1"
drag
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip">只能上传 .xls .xlsx 格式的 Excel 文件</div>
</template>
</el-upload>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, computed, nextTick } from 'vue';
import { ElNotification } from 'element-plus';
import { Download, UploadFilled } from '@element-plus/icons-vue';
import { Session } from '/@/utils/storage';
import { downBlobFile } from '/@/utils/other';
const title = ref('');
// 响应式数据
const visible = ref(false);
// 计算属性
const headers = computed(() => {
return {
Authorization: 'Bearer ' + Session.getToken(),
TENANT_ID: Session.getTenant()
};
});
const uploadUrl = ref('')
const currentType = ref('')
const uploadRef = ref<{ clearFiles?: () => void }>()
const titleMap: Record<string, string> = {
titleRelation: '职称信息导入',
quaRelation: '职业资格信息导入',
cerRelation: '教师资格证信息导入',
eduDegree: '学历学位信息导入',
partyChange: '党组织变动信息导入',
honor: '综合表彰信息导入'
}
// 方法
const init = (type: any) => {
currentType.value = type
uploadUrl.value = '/professional/file/importTeacherOtherInfo?type=' + type
title.value = titleMap[type] || '信息导入'
visible.value = true
nextTick(() => {
uploadRef.value?.clearFiles()
})
}
// Emits
const emit = defineEmits<{
(e: 'refreshData'): void
}>()
const handleUploadSuccess = () => {
visible.value = false;
ElNotification({
title: '成功',
message: '导入成功',
type: 'success',
});
emit('refreshData')
};
const handleAvatarError = (err: any) => {
const result = JSON.parse(err.message);
if (result.code == '1') {
ElNotification.error({
title: '错误',
message: result.msg,
duration: 30000,
});
}
};
const handleDownloadTemplate = () => {
downBlobFile('/professional/file/exportTeacherInfoTemplate', { type: currentType.value || 'titleRelation' }, title.value+'模板.xlsx')
}
// 暴露方法给父组件
defineExpose({
init,
});
</script>
<style scoped lang="scss">
.upload-demo {
:deep(.el-upload-dragger) {
width: 100%;
}
}
</style>

View File

@@ -37,6 +37,20 @@
</template>
</search-form>
<!-- 操作按钮 -->
<el-row>
<div class="mb15" style="width: 100%;">
<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"
@@ -77,6 +91,9 @@
@current-change="currentChangeHandle"
@size-change="sizeChangeHandle"
/>
<!-- 子组件 -->
<import-teacher-other-info ref="importTeacherOtherInfoRef" @refreshData="handleFilter" />
</div>
</div>
</template>
@@ -88,6 +105,7 @@ import { BasicTableProps, useTable } from '/@/hooks/table'
import { fetchList } from '/@/api/professional/professionaluser/professionalpartychange'
const TeacherNameNo = defineAsyncComponent(() => import('/@/components/TeacherNameNo/index.vue'))
const ImportTeacherOtherInfo = defineAsyncComponent(() => import('/@/views/professional/common/import-teacher-other-info.vue'))
// 表格引用
const tableRef = ref()
@@ -100,6 +118,10 @@ const search = reactive({
realName: ''
})
// 导入加载状态
const exportLoading = ref(false)
const importTeacherOtherInfoRef = ref()
// 配置 useTable
const state: BasicTableProps = reactive<BasicTableProps>({
pageList: async (params: any) => {
@@ -130,6 +152,11 @@ const resetQuery = () => {
getDataList()
}
// 打开导入弹窗
const handleImportDialog = () => {
importTeacherOtherInfoRef.value?.init('partyChange')
}
// 表格数据由 useTablecreatedIsNeed 默认 true在挂载时自动请求
</script>

View File

@@ -73,6 +73,14 @@
@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>
<div class="header-right">
<RightToolbar
@@ -207,6 +215,7 @@
<!-- 子组件 -->
<DataForm ref="dataFormRef" @refreshData="handleFilter" />
<ProfessionalBackResaon ref="backReasonRef" @refreshData="handleFilter" />
<import-teacher-other-info ref="importTeacherOtherInfoRef" @refreshData="handleFilter" />
</div>
</template>
@@ -220,11 +229,10 @@ import { useDict } from '/@/hooks/dict'
import {
fetchList,
examObj,
delObj,
exportExcel
} from '/@/api/professional/professionaluser/professionalqualificationrelation'
delObj} from '/@/api/professional/professionaluser/professionalqualificationrelation'
import { getLevelList } from '/@/api/professional/rsbase/professionalqualificationconfig'
import { getWorkTypeList } from '/@/api/professional/rsbase/professionalworktype'
import { makeExportTeacherInfoByTypeTask } from '/@/api/professional/professionalfile';
import { PROFESSIONAL_AUDIT_STATE_OPTIONS } from '/@/config/global'
import { defineAsyncComponent } from 'vue'
import { Medal } from '@element-plus/icons-vue'
@@ -234,6 +242,7 @@ const AuditState = defineAsyncComponent(() => import('/@/components/AuditState/i
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'))
const ImportTeacherOtherInfo = defineAsyncComponent(() => import('/@/views/professional/common/import-teacher-other-info.vue'))
// 审核状态选项
const auditStateOptions = PROFESSIONAL_AUDIT_STATE_OPTIONS
@@ -265,8 +274,9 @@ const search = reactive({
// 材料预览
const imgUrl = ref<Array<{ title: string; url: string }>>([])
// 导出加载状态
// 导出/导入加载状态
const exportLoading = ref(false)
const importTeacherOtherInfoRef = ref()
// 资格等级和工种列表
const qualificationLevelList = ref<any[]>([])
@@ -380,27 +390,15 @@ const handleDel = (row: any) => {
// 导出
const handleDownLoadWord = async () => {
exportLoading.value = true
try {
const response: any = await exportExcel(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
}
}
exportLoading.value = true;
let params = Object.assign(search, { type: 'P20003' });
makeExportTeacherInfoByTypeTask(params).then((res: any) => {
message.success('后台下载进行中,请稍后查看任务列表');
});
setTimeout(() => {
exportLoading.value = false;
}, 3000); // 5分钟后自动关闭
};
// 获取资格等级名称
const getQualificationLevelName = (id: string | number) => {
@@ -414,6 +412,11 @@ const getWorkTypeName = (id: string | number) => {
return item ? item.workName : '-'
}
// 打开导入弹窗
const handleImportDialog = () => {
importTeacherOtherInfoRef.value?.init('quaRelation')
}
// 加载字典数据
const loadDictData = async () => {
try {

View File

@@ -84,10 +84,10 @@
/>
</el-form-item>
<el-form-item label="证书编" prop="certificateNumber">
<el-form-item label="证书编" prop="certificateNumber">
<el-input
v-model="dataForm.certificateNumber"
placeholder="请输入证书编(仅支持英文和数字)"
placeholder="请输入证书编(仅支持英文和数字)"
clearable
show-word-limit
maxlength="100"
@@ -215,8 +215,8 @@ const formRules = computed(() => {
{ required: true, message: '请输入所学专业', trigger: 'blur' }
],
certificateNumber: [
{ required: true, message: '请输入证书编', trigger: 'blur' },
{ pattern: /^[A-Za-z0-9]+$/, message: '证书编只能包含英文和数字', trigger: 'blur' }
{ required: true, message: '请输入证书编', trigger: 'blur' },
{ pattern: /^[A-Za-z0-9]+$/, message: '证书编只能包含英文和数字', trigger: 'blur' }
]
}

View File

@@ -69,6 +69,14 @@
@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>
@@ -218,6 +226,7 @@
<!-- 子组件 -->
<DataForm ref="dataFormRef" @refreshData="handleFilter" />
<ProfessionalBackResaon ref="backReasonRef" @refreshData="handleFilter" />
<import-teacher-other-info ref="importTeacherOtherInfoRef" @refreshData="handleFilter" />
</div>
</div>
</template>
@@ -232,19 +241,19 @@ import { useDict } from '/@/hooks/dict'
import {
fetchList,
examObj,
delObj,
exportExcel
} from '/@/api/professional/professionaluser/professionalteacheracademicrelation'
delObj} from '/@/api/professional/professionaluser/professionalteacheracademicrelation'
import { getDegreeList } from '/@/api/professional/rsbase/professionalacademicdegreeconfig'
import { getQualificationList } from '/@/api/professional/rsbase/academicqualificationsconfig'
import { getAllTypeList } from '/@/api/professional/rsbase/professionalacademiceducationtypeconfig'
import { PROFESSIONAL_AUDIT_STATE_OPTIONS } 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 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'))
const ImportTeacherOtherInfo = defineAsyncComponent(() => import('/@/views/professional/common/import-teacher-other-info.vue'))
// 审核状态选项
const auditStateOptions = PROFESSIONAL_AUDIT_STATE_OPTIONS
@@ -279,6 +288,7 @@ const imgUrl = ref<Array<{ title: string; url: string }>>([])
// 导出加载状态
const exportLoading = ref(false)
const importTeacherOtherInfoRef = ref()
// 学位、学历和教育类型列表
const degreeList = ref<any[]>([])
@@ -401,26 +411,14 @@ const handleDel = (row: any) => {
// 导出
const handleDownLoadWord = async () => {
exportLoading.value = true
try {
const response: any = await exportExcel(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
}
exportLoading.value = true;
let params = Object.assign(search, { type: 'P20005' });
makeExportTeacherInfoByTypeTask(params).then((res: any) => {
message.success('后台下载进行中,请稍后查看任务列表');
});
setTimeout(() => {
exportLoading.value = false;
}, 3000); // 5分钟后自动关闭
}
// 获取学位名称
@@ -442,6 +440,11 @@ const getEducationTypeName = (id: string | number | undefined) => {
return item ? item.name : '-'
}
// 打开导入弹窗
const handleImportDialog = () => {
importTeacherOtherInfoRef.value?.init('eduDegree')
}
// 加载字典数据
const loadDictData = async () => {
try {

View File

@@ -69,6 +69,14 @@
@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>
@@ -188,6 +196,7 @@
<!-- 子组件 -->
<DataForm ref="dataFormRef" @refreshData="handleFilter" />
<ProfessionalBackResaon ref="backReasonRef" @refreshData="handleFilter" />
<import-teacher-other-info ref="importTeacherOtherInfoRef" @refreshData="handleFilter" />
</div>
</div>
</template>
@@ -203,9 +212,10 @@ import { getTeacherCertificateList } from '/@/api/professional/rsbase/profession
import {
fetchList,
examObj,
delObj,
exportExcel
delObj
} from '/@/api/professional/professionaluser/professionalteachercertificaterelation'
import { makeExportTeacherInfoByTypeTask } from '/@/api/professional/professionalfile';
import { PROFESSIONAL_AUDIT_STATE_OPTIONS } from '/@/config/global'
import { defineAsyncComponent } from 'vue'
const TeacherNameNo = defineAsyncComponent(() => import('/@/components/TeacherNameNo/index.vue'))
@@ -213,6 +223,7 @@ const AuditState = defineAsyncComponent(() => import('/@/components/AuditState/i
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'))
const ImportTeacherOtherInfo = defineAsyncComponent(() => import('/@/views/professional/common/import-teacher-other-info.vue'))
// 审核状态选项(独立定义,防止其他页面修改时被波及)
import type { StateOption } from '/@/components/AuditState/index.vue'
@@ -247,6 +258,7 @@ const imgUrl = ref<Array<{ title: string; url: string }>>([])
// 导出加载状态
const exportLoading = ref(false)
const importTeacherOtherInfoRef = ref()
// 证书列表
const certificateList = ref<any[]>([])
@@ -357,27 +369,15 @@ const handleDel = (row: any) => {
// 导出
const handleDownLoadWord = async () => {
exportLoading.value = true
try {
const response: any = await exportExcel(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
}
}
exportLoading.value = true;
let params = Object.assign(search, { type: 'P20004' });
makeExportTeacherInfoByTypeTask(params).then((res: any) => {
message.success('后台下载进行中,请稍后查看任务列表');
});
setTimeout(() => {
exportLoading.value = false;
}, 3000); // 5分钟后自动关闭
};
// 获取证书名称
const getCertificateName = (id: string | number) => {
@@ -385,6 +385,11 @@ const getCertificateName = (id: string | number) => {
return item ? item.cretificateName : '-'
}
// 打开导入弹窗
const handleImportDialog = () => {
importTeacherOtherInfoRef.value?.init('cerRelation')
}
// 加载字典数据
const loadDictData = async () => {
try {

View File

@@ -68,6 +68,14 @@
@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>
@@ -216,6 +224,7 @@
<!-- 子组件 -->
<ProfessionalBackResaon ref="backReasonRef" @refreshData="handleFilter" />
<DataForm ref="dataFormRef" @refreshData="handleFilter" />
<import-teacher-other-info ref="importTeacherOtherInfoRef" @refreshData="handleFilter" />
</div>
</div>
</template>
@@ -234,6 +243,7 @@ import {
} 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'))
@@ -241,6 +251,7 @@ const DetailPopover = defineAsyncComponent(() => import('/@/components/DetailPop
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()
@@ -281,6 +292,7 @@ const imgUrl = ref<Array<{ title: string; url: string }>>([])
// 导出加载状态
const exportLoading = ref(false)
const importTeacherOtherInfoRef = ref()
// 配置 useTable
const state: BasicTableProps = reactive<BasicTableProps>({
@@ -382,29 +394,19 @@ const handleDel = (row: any) => {
// 导出
const handleDownLoadWord = async () => {
exportLoading.value = true
try {
const response = await fetchList({
current: 1,
size: 999999,
...search
})
const data = response.data.records || []
const tHeader = ['工号', '姓名', '荣誉', '表彰单位', '年份']
const filterVal = ['teacherNo', 'teacherName', 'honor', 'honorCompany', 'year']
// 动态导入导出工具
const { export_json_to_excel } = await import('/@/excel/Export2Excel.js')
const exportData = data.map((v: any) => filterVal.map((j: string) => v[j]))
export_json_to_excel(tHeader, exportData, '综合表彰.xls')
message.success('导出成功')
} catch (error) {
message.error('导出失败')
} finally {
exportLoading.value = false
}
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在挂载时自动请求

View File

@@ -6,23 +6,25 @@
show-icon
style="margin-bottom: 20px;">
<template #title>
<span>导入前请先下载字典文件部分字段需严格按照字典值填写</span>
<span> 可先导出教职工信息,按照导出信息的模板填入职工数据,再执行导入</span>
</template>
</el-alert>
<div style="text-align: center; margin-bottom: 20px;">
<a href="excel/dictlist.xlsx" rel="external nofollow" download="职工信息字典下载" style="text-decoration: none;">
<el-button type="success" :icon="Download">下载字典文件</el-button>
</a>
</div>
<!-- <div style="text-align: center; margin-bottom: 20px;">-->
<!-- <a href="excel/dictlist.xlsx" rel="external nofollow" download="职工信息字典下载" style="text-decoration: none;">-->
<!-- <el-button type="success" :icon="Download">下载字典文件</el-button>-->
<!-- </a>-->
<!-- </div>-->
<el-upload
ref="uploadRef"
class="upload-demo"
action="/professional/file/makeImportTeacherInfoSimpleTask"
:headers="headers"
:accept="'.xls,.xlsx'"
:on-success="handleUploadSuccess"
:on-error="handleAvatarError"
:limit="1"
drag>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">
@@ -38,7 +40,7 @@
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { ref, computed, nextTick } from 'vue'
import { ElNotification } from 'element-plus'
import { Download, UploadFilled } from '@element-plus/icons-vue'
import { Session } from '/@/utils/storage'
@@ -53,9 +55,14 @@
}
})
const uploadRef = ref<{ clearFiles?: () => void }>()
// 方法
const init = () => {
visible.value = true
nextTick(() => {
uploadRef.value?.clearFiles?.()
})
}
const handleUploadSuccess = () => {

View File

@@ -1024,7 +1024,7 @@
</el-table-column>
<el-table-column prop="graduateSchool" label="毕业学校" min-width="180" align="center" show-overflow-tooltip />
<el-table-column prop="major" label="所学专业" min-width="150" align="center" show-overflow-tooltip />
<el-table-column prop="certificateNumber" label="证书编" min-width="120" align="center" />
<el-table-column prop="certificateNumber" label="证书编" min-width="120" align="center" />
<el-table-column label="学历证书" min-width="150" align="center">
<template #default="scope">
<el-button