This commit is contained in:
guochunsi
2025-12-31 17:40:01 +08:00
parent 6d94e91b70
commit 74c06bb8a0
713 changed files with 115034 additions and 46 deletions

View File

@@ -0,0 +1,52 @@
<template>
<div>
<multi-upload
ref="commonUploadPicRef"
@pushListData="pushListData"
@delListData="delListData"
:params="params"
:fileList="fileList"
:limitNums="limitNums"
:accept-file="acceptFile"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
// @ts-ignore - Vue 3 script setup component
import multiUpload from './multiUpload.vue'
// Props
const props = defineProps<{
fileList: any[]
type?: string
params?: any
limitNums?: number
acceptFile?: string
}>()
// 组件引用
const commonUploadPicRef = ref()
// 推送列表数据
const pushListData = (data: { name: string; url: string }) => {
props.fileList.push(data)
}
// 删除列表数据
const delListData = (file: string) => {
const index = props.fileList.findIndex((item: any) => item.url === file)
if (index > -1) {
props.fileList.splice(index, 1)
}
}
// 暴露方法
defineExpose({
commonUploadPic: commonUploadPicRef
})
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,29 @@
<template>
<div>
<el-button type="success" icon="view" size="small" @click="handlePdfPreview">预览</el-button>
<el-dialog v-model="visible" width="90%" append-to-body destroy-on-close>
<auth-img :authSrc="url" style="height:1000px;" v-if="visible" />
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import authImg from '@/components/tools/auth-img.vue'
// Props
defineProps<{
url: string
}>()
// 对话框显示状态
const visible = ref(false)
// 预览PDF
const handlePdfPreview = () => {
visible.value = true
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,115 @@
<template>
<div>
<el-upload
class="upload-demo"
action="/professional/file/teacherAboutInfoUpload"
:data="params"
:headers="headers"
:before-upload="beforeUpload"
:on-remove="handleRemove"
:on-success="handleUploadSuccess"
:file-list="fileList"
:accept="realAcceptFile"
:limit="realLimitNums"
list-type="picture">
<el-button size="small" type="primary">点击上传</el-button>
<el-tag>仅支持{{realAcceptFile}}后缀的文件上传,且文件大小不能超过5M!最大上传数量限制为{{realLimitNums}}.</el-tag>
</el-upload>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useMessage } from '/@/hooks/message'
import { Session } from '/@/utils/storage'
// Props
const props = defineProps<{
params?: any
fileList?: any[]
limitNums?: number
acceptFile?: string
}>()
// Emits
const emit = defineEmits<{
delListData: [url: string]
pushListData: [obj: { name: string; url: string }]
}>()
// 消息提示 hooks
const message = useMessage()
// 响应式数据
const realLimitNums = ref(0)
const realAcceptFile = ref('.jpg,.jpeg,.png,.pdf')
const fileName = ref('')
// Computed
const headers = computed(() => {
return {
"Authorization": 'Bearer ' + Session.getToken()
}
})
// 生命周期
onMounted(() => {
if (props.limitNums) {
realLimitNums.value = props.limitNums
} else {
realLimitNums.value = 5
}
if (props.acceptFile) {
realAcceptFile.value = props.acceptFile
} else {
realAcceptFile.value = ".jpg,.jpeg,.png,.pdf"
}
})
// 方法
// const handleUploadLimit = () => {
// message.error('最多只能上传1张图片')
// }
const beforeUpload = (file: any) => {
let fileNameStr = file.name
const pos = fileNameStr.lastIndexOf(".")
fileNameStr = fileNameStr.substring(pos + 1, fileNameStr.length)
fileNameStr = fileNameStr.toLowerCase()
const extension = fileNameStr === 'jpg'
const extension2 = fileNameStr === 'png'
const extension3 = fileNameStr === 'jpeg'
const extension4 = fileNameStr === 'bmp'
const extension5 = fileNameStr === 'gif'
const extension6 = fileNameStr === 'pdf'
const isLt2M = file.size / 1024 / 1024 < 5
if (!extension && !extension2 && !extension3 && !extension4 && !extension5 && !extension6) {
message.warning('请检查文件上传格式!')
return false
}
if (!isLt2M) {
message.warning('上传图片大小不能超过 5MB!')
return false
}
fileName.value = file.name
return true // 返回false不会自动上传
}
const handleRemove = (file: any) => {
emit("delListData", file.url)
}
// const handleUploadError = () => {
// message.error('文件上传失败,请检查文件上传格式与大小')
// }
const handleUploadSuccess = (res: any) => {
message.success('文件上传成功')
const obj = { name: res.data.name, url: res.data.url }
emit("pushListData", obj)
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,161 @@
<template>
<el-dialog v-model="visible" title="驳回" width="600px" :close-on-click-modal="false" destroy-on-close>
<el-form label-width="150px">
<el-form-item label="姓名:">
<el-tag>{{ dataForm.name }}</el-tag>
</el-form-item>
<el-form-item label="工号:">
<el-tag>{{ dataForm.teacherNo }}</el-tag>
</el-form-item>
<el-form-item label="类型:">
<el-tag>{{ typeName }}</el-tag>
</el-form-item>
<el-form-item label="驳回理由">
<el-input
type="textarea"
v-model="dataForm.backReason"
:rows="4"
placeholder="请输入驳回理由"
maxlength="500"
show-word-limit
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button @click="saveData" :loading="loading" type="primary"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { useMessage, useMessageBox } from '/@/hooks/message'
import { putObj as editTitle } from '/@/api/professional/professionaluser/professionaltitlerelation'
import { putObj as editQua } from '/@/api/professional/professionaluser/professionalqualificationrelation'
import { putObj as editCer } from '/@/api/professional/professionaluser/professionalteachercertificaterelation'
import { putObj as editEducation } from '/@/api/professional/professionaluser/professionalteacheracademicrelation'
import { putObj as editHonor } from '/@/api/professional/professionaluser/professionalteacherhonor'
import { putObj as editPaper } from '/@/api/professional/professionalteacherpaper'
import { putObj as editMaterial } from '/@/api/professional/professionalteachingmaterial'
import { putObj as editTopic } from '/@/api/professional/professionaltopiclist'
// Emits
const emit = defineEmits<{
(e: 'refreshData'): void
}>()
// 消息提示
const message = useMessage()
const messageBox = useMessageBox()
// 对话框显示状态
const visible = ref(false)
// 加载状态
const loading = ref(false)
// 表单数据
const dataForm = reactive({
name: '',
id: null as string | number | null,
backReason: '',
teacherNo: ''
})
// 类型名称
const typeName = ref('')
// 类型
const type = ref('')
// 类型映射
const typeMap: Record<string, string> = {
title: '职称',
qua: '职业资格',
cer: '教师资格证',
education: '学历学位',
honor: '综合表彰',
paper: '教师论文',
material: '教材',
topic: '课题'
}
// API 方法映射
const apiMethodMap: Record<string, any> = {
title: editTitle,
qua: editQua,
cer: editCer,
education: editEducation,
honor: editHonor,
paper: editPaper,
material: editMaterial,
topic: editTopic
}
// 初始化
const init = (row: any, typeValue: string) => {
type.value = typeValue
typeName.value = typeMap[typeValue] || ''
dataForm.name = row.realName || ''
dataForm.teacherNo = row.teacherNo || ''
dataForm.id = row.id || null
dataForm.backReason = ''
visible.value = true
}
// 保存数据
const saveData = async () => {
if (!dataForm.backReason.trim()) {
message.warning('请填写驳回理由')
return
}
const apiMethod = apiMethodMap[type.value]
if (!apiMethod) {
message.error('未知的类型')
return
}
await saveDataMethod(apiMethod)
}
// 保存数据方法
const saveDataMethod = async (method: any) => {
const data = {
id: dataForm.id,
state: '-2',
backReason: dataForm.backReason
}
try {
await messageBox.confirm(`确认驳回, ${dataForm.name}${typeName.value}申请?`)
loading.value = true
await method(data)
message.success('操作成功')
visible.value = false
emit('refreshData')
} catch (error: any) {
// 用户取消或请求失败
if (error !== 'cancel') {
message.error(error?.msg || '操作失败')
}
} finally {
loading.value = false
}
}
// 暴露方法
defineExpose({
init
})
</script>
<style scoped lang="scss">
.dialog-footer {
text-align: right;
}
</style>

View File

@@ -0,0 +1,155 @@
<template>
<el-dialog append-to-body v-model="visible" width="90%" height="700px" :title="title">
<el-tabs v-model="activeName" type="border-card" tab-position="left">
<el-tab-pane label="获奖证书" name="awardImg" v-if="imageData.awardImg">
<auth-img v-for="src in imageData.awardImg" :authSrc="src.url" :key="src.url" style="height:1000px;"></auth-img>
</el-tab-pane>
<el-tab-pane label="知网截图" name="knowdgeImg" v-if="imageData.knowdgeImg">
<auth-img v-for="src in imageData.knowdgeImg" :authSrc="src.url" :key="src.url" style="height:1000px;"></auth-img>
</el-tab-pane>
<el-tab-pane label="刊物封面" name="pubCover" v-if="imageData.pubCover">
<auth-img v-for="src in imageData.pubCover" :authSrc="src.url" :key="src.url" style="height:1000px;"></auth-img>
</el-tab-pane>
<el-tab-pane label="目录页" name="cateImg" v-if="imageData.cateImg">
<auth-img v-for="src in imageData.cateImg" :authSrc="src.url" :key="src.url" style="height:1000px;"></auth-img>
</el-tab-pane>
<el-tab-pane label="内容页" name="contentImg" v-if="imageData.contentImg">
<auth-img v-for="src in imageData.contentImg" :authSrc="src.url" :key="src.url" style="height:1000px;"></auth-img>
</el-tab-pane>
<el-tab-pane label="教材封面" name="mateCover" v-if="imageData.mateCover">
<auth-img v-for="src in imageData.mateCover" :authSrc="src.url" :key="src.url" style="height:1000px;"></auth-img>
</el-tab-pane>
<el-tab-pane label="出版页" name="pubImg" v-if="imageData.pubImg">
<auth-img v-for="src in imageData.pubImg" :authSrc="src.url" :key="src.url" style="height:1000px;"></auth-img>
</el-tab-pane>
<el-tab-pane label="立项申报书" name="projectApp" v-if="imageData.projectApp">
<auth-img v-for="src in imageData.projectApp" :authSrc="src.url" :key="src.url" style="height:1000px;"></auth-img>
</el-tab-pane>
<el-tab-pane label="结题证书" name="conclusionBook" v-if="imageData.conclusionBook">
<auth-img v-for="src in imageData.conclusionBook" :authSrc="src.url" :key="src.url" style="height:1000px;"></auth-img>
</el-tab-pane>
<el-tab-pane label="结题报告" name="conclusionReport" v-if="imageData.conclusionReport">
<auth-img v-for="src in imageData.conclusionReport" :authSrc="src.url" :key="src.url" style="height:1000px;"></auth-img>
</el-tab-pane>
<el-tab-pane label="其他材料" name="otherImg" v-if="imageData.otherImg">
<auth-img v-for="src in imageData.otherImg" :authSrc="src.url" :key="src.url" style="height:1000px;"></auth-img>
</el-tab-pane>
</el-tabs>
</el-dialog>
</template>
<script setup lang="ts" name="showEvidence">
import { ref, reactive, nextTick } from 'vue';
import authImg from '/@/components/tools/auth-img.vue';
interface ImageItem {
url: string;
}
interface ImageData {
awardImg?: ImageItem[];
knowdgeImg?: ImageItem[];
pubCover?: ImageItem[];
cateImg?: ImageItem[];
contentImg?: ImageItem[];
mateCover?: ImageItem[];
pubImg?: ImageItem[];
projectApp?: ImageItem[];
conclusionBook?: ImageItem[];
conclusionReport?: ImageItem[];
otherImg?: ImageItem[];
}
const props = defineProps<{
row?: any;
title?: string;
}>();
const visible = ref(false);
const imageData = reactive<ImageData>({});
const activeName = ref('');
/**
* 安全解析JSON字符串
*/
const safeParseJson = (value: any): ImageItem[] | undefined => {
if (!value) return undefined;
if (typeof value === 'string') {
try {
return JSON.parse(value);
} catch {
return undefined;
}
}
if (Array.isArray(value)) {
return value;
}
return undefined;
};
/**
* 初始化数据
*/
const init = () => {
Object.assign(imageData, JSON.parse(JSON.stringify(props.row)));
if (imageData.awardImg) {
activeName.value = 'awardImg';
imageData.awardImg = safeParseJson(imageData.awardImg);
}
if (imageData.knowdgeImg) {
if (!activeName.value) activeName.value = 'knowdgeImg';
imageData.knowdgeImg = safeParseJson(imageData.knowdgeImg);
}
if (imageData.pubCover) {
imageData.pubCover = safeParseJson(imageData.pubCover);
}
if (imageData.cateImg) {
imageData.cateImg = safeParseJson(imageData.cateImg);
}
if (imageData.contentImg) {
imageData.contentImg = safeParseJson(imageData.contentImg);
}
if (imageData.mateCover) {
if (!activeName.value) activeName.value = 'mateCover';
imageData.mateCover = safeParseJson(imageData.mateCover);
}
if (imageData.pubImg) {
imageData.pubImg = safeParseJson(imageData.pubImg);
}
if (imageData.projectApp) {
if (!activeName.value) activeName.value = 'projectApp';
imageData.projectApp = safeParseJson(imageData.projectApp);
}
if (imageData.conclusionBook) {
imageData.conclusionBook = safeParseJson(imageData.conclusionBook);
}
if (imageData.conclusionReport) {
imageData.conclusionReport = safeParseJson(imageData.conclusionReport);
}
if (imageData.otherImg) {
imageData.otherImg = safeParseJson(imageData.otherImg);
}
nextTick(() => {
visible.value = true;
});
};
// 暴露方法供父组件调用
defineExpose({
init,
});
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,74 @@
<template>
<el-dialog v-model="visible" width="90%" append-to-body title="综合表彰材料">
<auth-img v-for="src in imgUrl" :key="src.title" :authSrc="src.url" style="height:1000px;"></auth-img>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, nextTick } from 'vue'
import authImg from '/@/components/tools/auth-img.vue'
interface ImageItem {
title: string
url: string
}
// 响应式数据
const visible = ref(false)
const row = ref<any>({})
const pdfSrc = ref('')
const imgUrl = ref<ImageItem[]>([])
// 方法(保留以备将来使用)
// const checkIsImg = (url: string) => {
// if ((url.indexOf('.jpg') == -1)
// && (url.indexOf('.jpeg') == -1)
// && (url.indexOf('.png') == -1)
// && (url.indexOf('.JPG') == -1)
// && (url.indexOf('.JPEG') == -1)
// && (url.indexOf('.PNG') == -1)) {
// return false
// }
// return true
// }
// const downLoadFileForLook = (fileUrl: string, teacherNo: string) => {
// const index = fileUrl.indexOf('fileName')
// const firstIndex = index + 'fileName'.length + 1
// const lastIndex = fileUrl.indexOf('&')
// const fileName = fileUrl.substr(firstIndex, lastIndex - firstIndex)
// pdfSrc.value = "/professional/file/showPdf?fileName=" + fileName + "&type=attachment&teacherNo=" + teacherNo
// }
const init = (rowData: any) => {
row.value = rowData
pdfSrc.value = ''
imgUrl.value = []
nextTick(() => {
const obj: ImageItem = {
title: "",
url: row.value.attachment
}
imgUrl.value.push(obj)
// if(checkIsImg(rowData.attachment)){
// const obj: ImageItem = {
// title: "",
// url: row.value.attachment
// }
// imgUrl.value.push(obj)
// }else{
// downLoadFileForLook(rowData.attachment, rowData.teacherNo)
// }
visible.value = true
})
}
// 暴露方法给父组件
defineExpose({
init
})
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,27 @@
<!--
- Copyright (c) 2018-2025, cyweb All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- Neither the name of the pig4cloud.com developer nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-->
<template>
<div></div>
</template>
<script setup lang="ts">
// 空组件,保留用于未来扩展
</script>
<style lang="scss" scoped>
</style>