Bläddra i källkod

feat(统计模块): 添加历史开课统计页面及学习行为分析功能

refactor(组织管理): 重构图片上传组件及表单逻辑
style(表格组件): 优化列渲染方式及样式
tanshanming 6 månader sedan
förälder
incheckning
fe69245e68

+ 0 - 0
src/api/statisticalAnalysis/analysisLearningBehaviors.js


+ 0 - 0
src/api/statisticalAnalysis/analysisTeachingActivities.js


+ 0 - 0
src/api/statisticalAnalysis/overviewLearningProgress.js


+ 0 - 0
src/api/statisticalAnalysis/platformStatusOverview.js


+ 7 - 0
src/api/statisticalAnalysis/statisticsHistoryCourseOfferings.js

@@ -0,0 +1,7 @@
+// 文件模块相关接口
+import { moduleRequest } from '@/utils/request'
+
+const request = moduleRequest(`/api/webapp/`)
+
+// 历史开课统计
+export const historyProgressPage = (params) => request('/disk/courseopen/historyProgressPage', params, 'get')

+ 0 - 0
src/api/statisticalAnalysis/videoAnalysis.js


+ 179 - 176
src/components/UpLoadImg/index.vue

@@ -1,7 +1,7 @@
 <template>
 	<div class="cover-upload-row">
 		<div v-if="form.coverUrl != ''" class="cover-upload-box">
-			<a-image  :src="form.coverUrl" class="cover-img"/>
+			<a-image :src="form.coverUrl" class="cover-img" />
 		</div>
 		<a-upload
 			:show-upload-list="false"
@@ -9,218 +9,221 @@
 			accept=".jpg,.png"
 			:action="action"
 			:headers="headers"
-			@change ="handleChange"
+			@change="handleChange"
 		>
-<!--			<div  class="cover-upload-box">-->
-<!--				<PictureOutlined  style="font-size: 32px; color: #bbb"/>-->
-<!--			</div>-->
-			<a-button v-if="form.coverUrl == ''" >上传图片</a-button>
-			<a-button v-if="form.coverUrl != ''" >更改图片</a-button>
+			<!--			<div  class="cover-upload-box">-->
+			<!--				<PictureOutlined  style="font-size: 32px; color: #bbb"/>-->
+			<!--			</div>-->
+			<a-button v-if="form.coverUrl == ''">上传图片</a-button>
+			<a-button v-if="form.coverUrl != ''">更改图片</a-button>
 		</a-upload>
 
-
 		<div class="cover-tip" style="margin-left: 10px">支持jpg、png等格式文件上传,文件大小不超过10MB</div>
 	</div>
-
 </template>
 
 <script setup>
-import {ref, reactive, watch, defineProps, defineEmits} from 'vue'
-import {message} from 'ant-design-vue'
-import {PictureOutlined, CloudUploadOutlined} from '@ant-design/icons-vue'
-import tool from "@/utils/tool";
-import sysConfig from '@/config/index'
-
-const action = ref(sysConfig.API_URL + `/api/webapp/dev/file/${props.urlType==1?'uploadMinioReturnId':'uploadMinioReturnUrl'}`)
-const headers = ref({
-	token: tool.data.get('TOKEN')
-})
-
-const props = defineProps({
-	count: Number,
-	default : () => 1,
-	modelValue: [String, Number, Boolean, Object, Array, null],
-	urlType: {
-		type: [String,Number],
-		default: 1
-	}
-})
+	import { ref, reactive, watch, defineProps, defineEmits } from 'vue'
+	import { message } from 'ant-design-vue'
+	import { PictureOutlined, CloudUploadOutlined } from '@ant-design/icons-vue'
+	import tool from '@/utils/tool'
+	import sysConfig from '@/config/index'
+	const props = defineProps({
+		count: Number,
+		modelValue: [String, Number, Boolean, Object, Array, null],
+		urlType: {
+			type: [String, Number],
+			default: 1
+		}
+	})
 
-const emit = defineEmits(['update:visible','update:modelValue', 'ok','handlerUpSelect','handlerNewSelect','handlerUpImage'])
+	const emit = defineEmits([
+		'update:visible',
+		'update:modelValue',
+		'ok',
+		'handlerUpSelect',
+		'handlerNewSelect',
+		'handlerUpImage',
+		'upload-done'
+	])
+	const action = ref(
+		sysConfig.API_URL + `/api/webapp/dev/file/${props.urlType === 1 ? 'uploadMinioReturnId' : 'uploadMinioReturnUrl'}`
+	)
+	const headers = ref({
+		token: tool.data.get('TOKEN')
+	})
 
-const modalVisible = ref(props.visible)
-watch(
-	() => props.visible,
-	(v) => {
-		modalVisible.value = v
-	}
-)
-watch(modalVisible, (v) => {
-	emit('update:visible', v)
-})
-
-const formRef = ref()
-const file = ref({
-	id : '',
-	name: '',
-})
-const form = reactive({
-
-	id : '',
-	title: '',
-	video: '',
-	coverUrl: '',
-	docUrl: '',
-	srtUrl: ''
-})
-
-
-const beforeUploadImg = (file)=> {
-	const isImg = file.type === 'image/jpeg' || file.type === 'image/png'
-	const isLt10M = file.size / 1024 / 1024 < 10
-	if (!isImg) {
-		message.error('只能上传jpg/png图片')
-		return false
+	const modalVisible = ref(props.visible)
+	watch(
+		() => props.visible,
+		(v) => {
+			modalVisible.value = v
+		}
+	)
+	watch(modalVisible, (v) => {
+		emit('update:visible', v)
+	})
+
+	const formRef = ref()
+	const file = ref({
+		id: '',
+		name: ''
+	})
+	const form = reactive({
+		id: '',
+		title: '',
+		video: '',
+		coverUrl: '',
+		docUrl: '',
+		srtUrl: ''
+	})
+
+	const beforeUploadImg = (file) => {
+		const isImg = file.type === 'image/jpeg' || file.type === 'image/png'
+		const isLt10M = file.size / 1024 / 1024 < 10
+		if (!isImg) {
+			message.error('只能上传jpg/png图片')
+			return false
+		}
+		if (!isLt10M) {
+			message.error('图片不能超过10MB')
+			return false
+		}
+		// mock上传
+		const reader = new FileReader()
+		reader.onload = (e) => {
+			form.coverUrl = e.target.result
+		}
+		reader.readAsDataURL(file)
+		return true
 	}
-	if (!isLt10M) {
-		message.error('图片不能超过10MB')
-		return false
+
+	//回显用显示图片
+	const setData = (data) => {
+		if (!data.url || data.url === '') {
+			form.coverUrl = ''
+		} else {
+			// 如果已经是完整URL,直接使用;否则拼接文件服务器地址
+			form.coverUrl = data.url.startsWith('http') ? data.url : sysConfig.FILE_URL + data.url
+		}
+
+		form.id = data.id
+		emit('handlerUpImage', form.id)
+		emit('update:modelValue', form.id)
 	}
-	// mock上传
-	const reader = new FileReader()
-	reader.onload = (e) => {
-		form.coverUrl = e.target.result
+
+	const handleChange = (res) => {
+		console.log('上传图片', res)
+		if (res.file && res.file.response && res.file.response.code == 200) {
+			message.success('上传成功')
+			form.id = res.file.response.data
+			emit('handlerUpImage', form.id)
+			emit('update:modelValue', form.id)
+			emit('upload-done', [{ id: form.id, url: form.coverUrl }])
+		} else if (res.file && res.file.status === 'error') {
+			message.error('上传失败')
+		}
 	}
-	reader.readAsDataURL(file)
-	return true
-}
-
-//回显用显示图片
-const setData = (data) => {
-	if(data.url == ''){
-		form.coverUrl = ''
-	}else{
-		form.coverUrl = sysConfig.FILE_URL+data.url
+	const setFile = (fileData) => {
+		console.log('设置了文件', fileData)
+		file.value.id = fileData.id
+		file.value.name = fileData.fileName
 	}
 
-	form.id = data.id
-	emit('handlerUpImage',form.id)
-	emit('update:modelValue', form.id)
-}
-
-
+	const handleOk = () => {
+		formRef.value.validate().then(() => {
+			emit('ok', { ...form })
+			modalVisible.value = false
+		})
+	}
 
-const handleChange = (res) => {
-	console.log('上传图片',res)
-	if (res.file && res.file.response && res.file.response.code == 200) {
-		message.success('上传成功')
-		form.id =	res.file.response.data
-		emit('handlerUpImage',form.id)
-		emit('update:modelValue', form.id)
-	} else {
-		// message.error('上传失败')
+	const getData = (callBack) => {
+		formRef.value.validate().then(() => {
+			callBack({ ...form })
+		})
 	}
-}
-const setFile = (fileData) => {
-	console.log("设置了文件",fileData)
-	file.value.id = fileData.id
-	file.value.name = fileData.fileName
-}
-
-const handleOk = () =>{
-	formRef.value.validate().then(() => {
-		emit('ok', {...form})
+
+	const handleCancel = () => {
 		modalVisible.value = false
-	})
-}
+	}
 
-const getData = (callBack) => {
-	formRef.value.validate().then(() => {
-		callBack({...form})
+	defineExpose({
+		getData,
+		setFile,
+		setData
 	})
-}
-
-const handleCancel = () => {
-	modalVisible.value = false
-}
-
-defineExpose({
-	getData,setFile,setData
-})
 </script>
 
-
 <style lang="less" scoped>
-.add-class-hours-modal {
-	.ant-modal-content {
-		border-radius: 10px;
-	}
+	.add-class-hours-modal {
+		.ant-modal-content {
+			border-radius: 10px;
+		}
 
-	.ant-modal-header {
-		border-radius: 10px 10px 0 0;
-	}
+		.ant-modal-header {
+			border-radius: 10px 10px 0 0;
+		}
 
-	.ant-form-item {
-		margin-bottom: 24px;
-	}
+		.ant-form-item {
+			margin-bottom: 24px;
+		}
 
-	.video-select-row {
-		display: flex;
-		align-items: center;
+		.video-select-row {
+			display: flex;
+			align-items: center;
+		}
 	}
 
-}
+	.cover-upload-row {
+		// display: flex;
+		// align-items: center;
 
-.cover-upload-row {
-	// display: flex;
-	// align-items: center;
-
-	.cover-upload-box {
-		width: 120px;
-		height: 120px;
-		background: #f7f8fa;
-		border-radius: 8px;
-		display: flex;
-		align-items: center;
-		justify-content: center;
-		margin-right: 24px;
-		border: 1px dashed #d9d9d9;
-		cursor: pointer;
-
-		.cover-img {
-			width: 100%;
-			height: 100%;
-			object-fit: cover;
+		.cover-upload-box {
+			width: 120px;
+			height: 120px;
+			background: #f7f8fa;
 			border-radius: 8px;
-		}
-
-		.cover-placeholder {
 			display: flex;
 			align-items: center;
 			justify-content: center;
-			width: 100%;
-			height: 100%;
-			color: #bbb;
-			font-size: 32px;
+			margin-right: 24px;
+			border: 1px dashed #d9d9d9;
+			cursor: pointer;
+
+			.cover-img {
+				width: 100%;
+				height: 100%;
+				object-fit: cover;
+				border-radius: 8px;
+			}
+
+			.cover-placeholder {
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				width: 100%;
+				height: 100%;
+				color: #bbb;
+				font-size: 32px;
+			}
+		}
+
+		.cover-tip {
+			color: #888;
+			font-size: 13px;
 		}
 	}
 
-	.cover-tip {
+	.upload-tip {
 		color: #888;
 		font-size: 13px;
+		margin-left: 12px;
+	}
+
+	.footer-btns {
+		display: flex;
+		justify-content: flex-end;
+		gap: 16px;
+		margin-top: 24px;
 	}
-}
-
-.upload-tip {
-	color: #888;
-	font-size: 13px;
-	margin-left: 12px;
-}
-
-.footer-btns {
-	display: flex;
-	justify-content: flex-end;
-	gap: 16px;
-	margin-top: 24px;
-}
 </style>

+ 16 - 6
src/views/statisticalAnalysis/analysisLearningBehaviors/index.vue

@@ -226,13 +226,19 @@
 			title: '平均完成率',
 			dataIndex: 'avgCompletionRate',
 			key: 'avgCompletionRate',
-			customRender: ({ text }) => `<span class="completion-rate">${text}%</span>`
+			render: ({ text }) => `<span class="completion-rate">${text}%</span>`
 		},
 		{
 			title: '作业提交率',
 			dataIndex: 'assignmentSubmissionRate',
 			key: 'assignmentSubmissionRate',
-			customRender: ({ text }) => `<span class="completion-rate">${text}%</span>`
+			render: ({ text }) => `<span class="completion-rate">${text}%</span>`
+		},
+		{
+			title: '退课率',
+			dataIndex: 'dropoutRate',
+			key: 'dropoutRate',
+			render: ({ text }) => `<span class="completion-rate">${text}%</span>`
 		}
 	]
 
@@ -244,7 +250,8 @@
 			courseCount: 45,
 			totalVisits: 5234,
 			avgCompletionRate: 78.5,
-			assignmentSubmissionRate: 85.2
+			assignmentSubmissionRate: 85.2,
+			dropoutRate: 10.2
 		},
 		{
 			id: 2,
@@ -252,7 +259,8 @@
 			courseCount: 38,
 			totalVisits: 4567,
 			avgCompletionRate: 72.3,
-			assignmentSubmissionRate: 79.8
+			assignmentSubmissionRate: 79.8,
+			dropoutRate: 12.5
 		},
 		{
 			id: 3,
@@ -260,7 +268,8 @@
 			courseCount: 28,
 			totalVisits: 3123,
 			avgCompletionRate: 68.9,
-			assignmentSubmissionRate: 76.4
+			assignmentSubmissionRate: 76.4,
+			dropoutRate: 15.2
 		},
 		{
 			id: 4,
@@ -268,7 +277,8 @@
 			courseCount: 35,
 			totalVisits: 3890,
 			avgCompletionRate: 75.2,
-			assignmentSubmissionRate: 82.1
+			assignmentSubmissionRate: 82.1,
+			dropoutRate: 13.8
 		}
 	])
 

+ 189 - 1
src/views/statisticalAnalysis/statisticsHistoryCourseOfferings/index.vue

@@ -1,3 +1,191 @@
 <template>
-	<div>统计历史课程 offerings</div>
+	<div class="p-6">
+		<div class="bg-white rounded-lg shadow-sm">
+			<!-- 页面标题 -->
+			<div class="p-4 border-b border-gray-200">
+				<h2 class="text-lg font-semibold text-gray-800">历史开课统计</h2>
+			</div>
+
+			<!-- 搜索区域 -->
+			<!-- <div class="p-4 bg-gray-50">
+				<a-form layout="inline" :model="searchForm" @finish="handleSearch">
+					<a-form-item label="课程名称">
+						<a-input
+							v-model:value="searchForm.courseName"
+							placeholder="请输入课程名称"
+							allow-clear
+							style="width: 200px"
+						/>
+					</a-form-item>
+					<a-form-item>
+						<a-button type="primary" html-type="submit" :loading="loading">
+							<template #icon><SearchOutlined /></template>
+							搜索
+						</a-button>
+						<a-button @click="handleReset" class="ml-2"> 重置 </a-button>
+					</a-form-item>
+				</a-form>
+			</div> -->
+
+			<!-- 数据表格 -->
+			<div class="p-4">
+				<a-table
+					:columns="columns"
+					:data-source="dataSource"
+					:loading="loading"
+					:pagination="pagination"
+					@change="handleTableChange"
+					row-key="courseId"
+					size="middle"
+				>
+					<template #bodyCell="{ column, record }">
+						<template v-if="column.key === 'courseName'">
+							<span class="text-blue-600 font-medium">{{ record.courseName }}</span>
+						</template>
+						<template v-if="column.key === 'courseOpenCount'">
+							<a-tag color="blue" class="text-center min-w-[60px]"> {{ record.courseOpenCount }}次 </a-tag>
+						</template>
+						<template v-if="column.key === 'videoCount'">
+							<a-tag color="green" class="text-center min-w-[60px]"> {{ record.videoCount }}个 </a-tag>
+						</template>
+						<template v-if="column.key === 'teachMaterialsCount'">
+							<a-tag color="orange" class="text-center min-w-[60px]"> {{ record.teachMaterialsCount }}份 </a-tag>
+						</template>
+					</template>
+				</a-table>
+			</div>
+		</div>
+	</div>
 </template>
+
+<script setup>
+	import { ref, reactive, onMounted } from 'vue'
+	import { message } from 'ant-design-vue'
+	import { SearchOutlined } from '@ant-design/icons-vue'
+	import { historyProgressPage } from '@/api/statisticalAnalysis/statisticsHistoryCourseOfferings'
+
+	// 响应式数据
+	const loading = ref(false)
+	const dataSource = ref([])
+
+	// 搜索表单
+	const searchForm = reactive({
+		courseName: ''
+	})
+
+	// 分页配置
+	const pagination = reactive({
+		current: 1,
+		pageSize: 10,
+		total: 0,
+		showSizeChanger: true,
+		showQuickJumper: true,
+		showTotal: (total, range) => `第 ${range[0]}-${range[1]} 条,共 ${total} 条数据`
+	})
+
+	// 表格列配置
+	const columns = [
+		{
+			title: '序号',
+			key: 'index',
+			width: 80,
+			align: 'center',
+			customRender: ({ index }) => pagination.current * pagination.pageSize - pagination.pageSize + index + 1
+		},
+		{
+			title: '课程名称',
+			dataIndex: 'courseName',
+			key: 'courseName',
+			ellipsis: true,
+			width: 200
+		},
+		{
+			title: '历史开课数',
+			dataIndex: 'courseOpenCount',
+			key: 'courseOpenCount',
+			align: 'center',
+			width: 120,
+			sorter: false
+		},
+		{
+			title: '视频数量',
+			dataIndex: 'videoCount',
+			key: 'videoCount',
+			align: 'center',
+			width: 120,
+			sorter: false
+		},
+		{
+			title: '讲义数量',
+			dataIndex: 'teachMaterialsCount',
+			key: 'teachMaterialsCount',
+			align: 'center',
+			width: 120,
+			sorter: false
+		}
+	]
+
+	// 获取数据
+	const fetchData = async () => {
+		try {
+			loading.value = true
+			const params = {
+				current: pagination.current,
+				size: pagination.pageSize
+				// ...searchForm
+			}
+
+			const response = await historyProgressPage(params)
+			if (response) {
+				dataSource.value = response.records || []
+				pagination.total = response.total || 0
+			} else {
+				message.error(response.message || '获取数据失败')
+			}
+		} catch (error) {
+			console.error('获取历史开课统计数据失败:', error)
+			message.error('获取数据失败,请稍后重试')
+		} finally {
+			loading.value = false
+		}
+	}
+
+	// 搜索处理
+	const handleSearch = () => {
+		pagination.current = 1
+		fetchData()
+	}
+
+	// 重置搜索
+	const handleReset = () => {
+		Object.keys(searchForm).forEach((key) => {
+			searchForm[key] = ''
+		})
+		pagination.current = 1
+		fetchData()
+	}
+
+	// 表格变化处理(分页、排序等)
+	const handleTableChange = (pag, filters, sorter) => {
+		pagination.current = pag.current
+		pagination.pageSize = pag.pageSize
+		fetchData()
+	}
+
+	// 组件挂载时获取数据
+	onMounted(() => {
+		fetchData()
+	})
+</script>
+
+<style scoped>
+	.ant-table-tbody > tr > td {
+		padding: 12px 16px;
+	}
+
+	.ant-tag {
+		margin: 0;
+		border-radius: 4px;
+		font-weight: 500;
+	}
+</style>

+ 67 - 31
src/views/sys/org/form.vue

@@ -32,18 +32,14 @@
 				<a-input v-model:value="formData.englishName" placeholder="请输入英文名称" allow-clear />
 			</a-form-item>
 			<a-form-item label="所在二级单位徽章:" name="badge">
-				<xn-upload upload-mode="defaults" :upload-number="1" @upload-done="handleBadgeUpload" />
-				<div v-if="formData.badge" class="mt-2">
-					<img :src="formData.badge" alt="单位徽章" style="max-width: 100px; max-height: 100px" />
-					<a-button type="link" @click="removeBadge">删除</a-button>
-				</div>
+				<UploadImg ref="badgeUploadRef" v-model="formData.badge" @handlerUpImage="handleBadgeUpload" />
 			</a-form-item>
-			<a-form-item label="单位宣传图:" name="propagandaImage">
-				<xn-upload upload-mode="defaults" :upload-number="1" @upload-done="handlePropagandaImageUpload" />
-				<div v-if="formData.propagandaImage" class="mt-2">
-					<img :src="formData.propagandaImage" alt="单位宣传图" style="max-width: 200px; max-height: 150px" />
-					<a-button type="link" @click="removePropagandaImage">删除</a-button>
-				</div>
+			<a-form-item label="单位宣传图:" name="propagandizePic">
+				<UploadImg
+					ref="propagandaUploadRef"
+					v-model="formData.propagandizePic"
+					@handlerUpImage="handlePropagandaImageUpload"
+				/>
 			</a-form-item>
 			<a-form-item label="组织分类:" name="category">
 				<a-select
@@ -63,15 +59,18 @@
 				}}</a-tag>
 				<a-input v-show="false" v-model:value="formData.directorId" />
 			</a-form-item>
-			<a-form-item label="二级管理员:" name="directorId">
-				<a-button type="link" style="padding-left: 0px" @click="openSelector(formData.secondaryAdministratorId)"
+			<a-form-item label="二级管理员:" name="secondaryAdministratorId">
+				<a-button
+					type="link"
+					style="padding-left: 0px"
+					@click="openSecondaryAdminSelector(formData.secondaryAdministratorId)"
 					>选择</a-button
 				>
 				<a-tag
 					v-if="formData.secondaryAdministratorId && formData.secondaryAdministratorName"
 					color="orange"
 					closable
-					@close="closeUserTag"
+					@close="closeSecondaryAdminTag"
 					>{{ formData.secondaryAdministratorName }}</a-tag
 				>
 				<a-input v-show="false" v-model:value="formData.secondaryAdministratorId" />
@@ -97,7 +96,7 @@
 	import orgApi from '@/api/sys/orgApi'
 	import userCenterApi from '@/api/sys/userCenterApi'
 	import UserSelectorPlus from '@/components/Selector/userSelectorPlus.vue'
-	import XnUpload from '@/components/XnUpload/index.vue'
+	import UploadImg from '@/components/UpLoadImg/index.vue'
 	import tool from '@/utils/tool'
 
 	// 定义emit事件
@@ -106,6 +105,8 @@
 	let visible = $ref(false)
 	let userSelectorPlusRef = ref()
 	const formRef = ref()
+	const badgeUploadRef = ref()
+	const propagandaUploadRef = ref()
 	// 表单数据,也就是默认给一些数据
 	const formData = ref({})
 	// 定义机构元素
@@ -127,6 +128,13 @@
 			}
 			orgApi.orgDetail(param).then((data) => {
 				formData.value = Object.assign({}, data)
+				// 设置上传组件的数据
+				if (data.badge && data.badgePath) {
+					badgeUploadRef.value?.setData({ id: data.badge, url: data.badgePath })
+				}
+				if (data.propagandizePic && data.propagandizePicPath) {
+					propagandaUploadRef.value?.setData({ id: data.propagandizePic, url: data.propagandizePicPath })
+				}
 			})
 		}
 		// 获取机构树并加入顶级
@@ -153,50 +161,78 @@
 	}
 	// 机构分类字典
 	const orgCategoryOptions = tool.dictList('ORG_CATEGORY')
+	// 当前选择的用户类型:director 或 secondaryAdmin
+	const currentUserType = ref('director')
+
 	// 打开人员选择器,选择主管
 	const openSelector = (id) => {
+		currentUserType.value = 'director'
+		let checkedUserIds = []
+		if (id) checkedUserIds.push(id)
+		userSelectorPlusRef.value.showUserPlusModal(checkedUserIds)
+	}
+
+	// 打开人员选择器,选择二级管理员
+	const openSecondaryAdminSelector = (id) => {
+		currentUserType.value = 'secondaryAdmin'
 		let checkedUserIds = []
-		checkedUserIds.push(id)
+		if (id) checkedUserIds.push(id)
 		userSelectorPlusRef.value.showUserPlusModal(checkedUserIds)
 	}
+
 	// 人员选择器回调
 	const userBack = (value) => {
-		if (value.length > 0) {
-			formData.value.directorId = value[0].id
-			formData.value.directorName = value[0].name
-		} else {
-			formData.value.directorId = ''
-			formData.value.directorName = ''
+		if (currentUserType.value === 'director') {
+			if (value.length > 0) {
+				formData.value.directorId = value[0].id
+				formData.value.directorName = value[0].name
+			} else {
+				formData.value.directorId = ''
+				formData.value.directorName = ''
+			}
+		} else if (currentUserType.value === 'secondaryAdmin') {
+			if (value.length > 0) {
+				formData.value.secondaryAdministratorId = value[0].id
+				formData.value.secondaryAdministratorName = value[0].name
+			} else {
+				formData.value.secondaryAdministratorId = ''
+				formData.value.secondaryAdministratorName = ''
+			}
 		}
 	}
+
 	// 通过小标签删除主管
 	const closeUserTag = () => {
 		formData.value.directorId = ''
 		formData.value.directorName = ''
 	}
 
+	// 通过小标签删除二级管理员
+	const closeSecondaryAdminTag = () => {
+		formData.value.secondaryAdministratorId = ''
+		formData.value.secondaryAdministratorName = ''
+	}
+
 	// 处理徽章上传
-	const handleBadgeUpload = (fileList) => {
-		if (fileList && fileList.length > 0) {
-			formData.value.badge = fileList[0].url
-		}
+	const handleBadgeUpload = (fileId) => {
+		formData.value.badge = fileId
 	}
 
 	// 删除徽章
 	const removeBadge = () => {
 		formData.value.badge = ''
+		formData.value.badgePath = ''
 	}
 
 	// 处理宣传图上传
-	const handlePropagandaImageUpload = (fileList) => {
-		if (fileList && fileList.length > 0) {
-			formData.value.propagandaImage = fileList[0].url
-		}
+	const handlePropagandaImageUpload = (fileId) => {
+		formData.value.propagandizePic = fileId
 	}
 
 	// 删除宣传图
 	const removePropagandaImage = () => {
-		formData.value.propagandaImage = ''
+		formData.value.propagandizePic = ''
+		formData.value.propagandizePicPath = ''
 	}
 	// 验证并提交数据
 	const onSubmit = () => {

+ 1 - 1
src/views/sys/org/index.vue

@@ -45,7 +45,7 @@
 					:row-key="(record) => record.id"
 					:row-selection="options.rowSelection"
 				>
-					<template #operator class="table-operator">
+					<template #operator>
 						<a-space>
 							<a-button type="primary" @click="formRef.onOpen(undefined, searchFormState.parentId)">
 								<template #icon><plus-outlined /></template>