Quellcode durchsuchen

Merge branch 'dev' of http://192.168.1.245:11111/shanming/onlineEducation-front into dev

zhangsq vor 7 Monaten
Ursprung
Commit
4f1e16d3fe
37 geänderte Dateien mit 2986 neuen und 614 gelöschten Zeilen
  1. 2 2
      .env.production
  2. 14 10
      src/api/forum/forumApi.js
  3. 27 0
      src/api/forum/forumPostInfoApi.js
  4. 27 0
      src/api/forum/forumPostTypeApi.js
  5. 27 0
      src/api/forum/forumReportInfoApi.js
  6. 27 0
      src/api/forum/forumSensitivityApi.js
  7. 27 0
      src/api/forum/forumSensitivityRecordApi.js
  8. 2 0
      src/api/hour/index.js
  9. 5 0
      src/components/UpLoadBreakPoint/index.vue
  10. 5 3
      src/components/UpLoadDoc/index.vue
  11. 21 25
      src/components/UpLoadImg/index.vue
  12. 5 3
      src/components/UpLoadSrt/index.vue
  13. 153 139
      src/components/XnWorkflow/nodes/addNode.vue
  14. 131 103
      src/views/courseAdd/components/courseProduction/addClassHours.vue
  15. 266 142
      src/views/courseAdd/components/courseProduction/addDialog.vue
  16. 337 0
      src/views/courseAdd/components/courseProduction/exList.vue
  17. 333 0
      src/views/courseAdd/components/courseProduction/exLists.vue
  18. 110 0
      src/views/courseAdd/components/courseProduction/fileName.vue
  19. 10 1
      src/views/courseAdd/components/courseProduction/index.vue
  20. 31 15
      src/views/courseAdd/components/courseProduction/resourceUpload.vue
  21. 6 6
      src/views/courseAdd/index.vue
  22. 160 143
      src/views/courseManagement/components/ListView.vue
  23. 47 15
      src/views/courseManagement/components/QueryView.vue
  24. 28 4
      src/views/forum/form.vue
  25. 1 1
      src/views/forum/index.vue
  26. 103 0
      src/views/forum/postinfo/form.vue
  27. 150 0
      src/views/forum/postinfo/index.vue
  28. 73 0
      src/views/forum/posttype/form.vue
  29. 110 0
      src/views/forum/posttype/index.vue
  30. 85 0
      src/views/forum/reportinfo/form.vue
  31. 126 0
      src/views/forum/reportinfo/index.vue
  32. 70 0
      src/views/forum/sensitivity/form.vue
  33. 122 0
      src/views/forum/sensitivity/index.vue
  34. 79 0
      src/views/forum/sensitivityrecord/form.vue
  35. 118 0
      src/views/forum/sensitivityrecord/index.vue
  36. 2 2
      src/views/myResources/personalResources/index.vue
  37. 146 0
      stats.html

+ 2 - 2
.env.production

@@ -5,8 +5,8 @@ NODE_ENV = production
 VITE_TITLE = Snowy
 
 # 接口地址
-VITE_API_BASEURL = http://localhost:9003
-
+VITE_API_BASEURL = http://192.168.1.245:19003
+VITE_FILEURL = http://192.168.1.245:10005/education/
 # 本地端口
 VITE_PORT = 9000
 

+ 14 - 10
src/api/forum/forumApi.js

@@ -1,6 +1,6 @@
 import { baseRequest } from '@/utils/request'
 
-const request = (url, ...arg) => baseRequest(`/api/webapp/forum/${url}`, ...arg)
+const request = (url, ...arg) => baseRequest(`/api/webapp/${url}`, ...arg)
 /**
  * 菜单
  *
@@ -10,38 +10,42 @@ const request = (url, ...arg) => baseRequest(`/api/webapp/forum/${url}`, ...arg)
 export default {
 	// 查询帖子列表接口
 	forumList(data) {
-		return request('postinfo/page', data, 'get')
+		return request('forum/postinfo/page', data, 'get')
 	},
 	// 查询分类列表接口
 	forumTypeList(data) {
-		return request('posttype/list', data, 'get')
+		return request('forum/posttype/list', data, 'get')
 	},
 	// 发帖接口 // 编辑帖子接口
 	submitForm(data, edit = false) {
-		return request(`postinfo/${edit ? 'edit' : 'add'}`, data)
+		return request(`forum/postinfo/${edit ? 'edit' : 'add'}`, data)
 	},
 	// 帖子详情接口
 	forumTypeDetail(data) {
-		return request('postinfo/detail', data, 'get')
+		return request('forum/postinfo/detail', data, 'get')
 	},
 	// 点赞接口 // 取消点赞接口
 	postlikeSubmit(data, isLike = 0) {
-		return request(`postlike/${isLike == 0 ? 'add' : 'cancel'}`, data)
+		return request(`forum/postlike/${isLike == 0 ? 'add' : 'cancel'}`, data)
 	},
 	// 回复帖子接口 //编辑回复
 	submitPostreply(data, edit = false) {
-		return request(`postreply/${edit ? 'edit' : 'add'}`, data)
+		return request(`forum/postreply/${edit ? 'edit' : 'add'}`, data)
 	},
 	// 举报帖子接口
 	reportinfoAdd(data) {
-		return request('reportinfo/add', data)
+		return request('forum/reportinfo/add', data)
 	},
 	// 删除自己回复接口
 	postreplyDel(data) {
-		return request('postreply/delete', data)
+		return request('forum/postreply/delete', data)
 	},
 	// 扩展帖子列表,1.查询我发布的 2.查询我回复的 3.查询关于我的 4.查询我点赞的
 	moreList(data) {
-		return request('postinfo/moreList', data, 'get')
+		return request('forum/postinfo/moreList', data, 'get')
+	},
+	// 扩展帖子列表,1.查询我发布的 2.查询我回复的 3.查询关于我的 4.查询我点赞的
+	allUserList(data) {
+		return request('sys/user/allList', data, 'get')
 	}
 }

+ 27 - 0
src/api/forum/forumPostInfoApi.js

@@ -0,0 +1,27 @@
+import { baseRequest } from '@/utils/request'
+
+const request = (url, ...arg) => baseRequest(`/api/forumapp/forum/postinfo/` + url, ...arg)
+
+/**
+ * 帖子信息表Api接口管理器
+ *
+ * @author 金吉龙
+ * @date  2025/07/16 08:46
+ **/
+export default {
+	// 获取帖子信息表分页
+	forumPostInfoPage(data) {
+		return request('page', data, 'get')
+	},
+	// 提交帖子信息表表单 edit为true时为编辑,默认为新增
+	forumPostInfoSubmitForm(data, edit = false) {
+		return request(edit ? 'edit' : 'add', data)
+	},
+	// 删除帖子信息表
+	forumPostInfoDelete(data) {
+		return request('delete', data)
+	},
+	// 获取帖子信息表详情
+	forumPostInfoDetail(data) {
+		return request('detail', data, 'get')
+	}}

+ 27 - 0
src/api/forum/forumPostTypeApi.js

@@ -0,0 +1,27 @@
+import { baseRequest } from '@/utils/request'
+
+const request = (url, ...arg) => baseRequest(`/api/forumapp/forum/posttype/` + url, ...arg)
+
+/**
+ * 帖子分类表Api接口管理器
+ *
+ * @author 金吉龙
+ * @date  2025/07/16 08:43
+ **/
+export default {
+	// 获取帖子分类表分页
+	forumPostTypePage(data) {
+		return request('page', data, 'get')
+	},
+	// 提交帖子分类表表单 edit为true时为编辑,默认为新增
+	forumPostTypeSubmitForm(data, edit = false) {
+		return request(edit ? 'edit' : 'add', data)
+	},
+	// 删除帖子分类表
+	forumPostTypeDelete(data) {
+		return request('delete', data)
+	},
+	// 获取帖子分类表详情
+	forumPostTypeDetail(data) {
+		return request('detail', data, 'get')
+	}}

+ 27 - 0
src/api/forum/forumReportInfoApi.js

@@ -0,0 +1,27 @@
+import { baseRequest } from '@/utils/request'
+
+const request = (url, ...arg) => baseRequest(`/api/forumapp/forum/reportinfo/` + url, ...arg)
+
+/**
+ * 论坛-帖子举报信息表Api接口管理器
+ *
+ * @author 金吉龙
+ * @date  2025/07/17 16:39
+ **/
+export default {
+	// 获取论坛-帖子举报信息表分页
+	forumReportInfoPage(data) {
+		return request('page', data, 'get')
+	},
+	// 提交论坛-帖子举报信息表表单 edit为true时为编辑,默认为新增
+	forumReportInfoSubmitForm(data, edit = false) {
+		return request(edit ? 'edit' : 'add', data)
+	},
+	// 删除论坛-帖子举报信息表
+	forumReportInfoDelete(data) {
+		return request('delete', data)
+	},
+	// 获取论坛-帖子举报信息表详情
+	forumReportInfoDetail(data) {
+		return request('detail', data, 'get')
+	}}

+ 27 - 0
src/api/forum/forumSensitivityApi.js

@@ -0,0 +1,27 @@
+import { baseRequest } from '@/utils/request'
+
+const request = (url, ...arg) => baseRequest(`/api/forumapp/forum/sensitivity/` + url, ...arg)
+
+/**
+ * 论坛-敏感词Api接口管理器
+ *
+ * @author 金吉龙
+ * @date  2025/07/15 11:07
+ **/
+export default {
+	// 获取论坛-敏感词分页
+	forumSensitivityPage(data) {
+		return request('page', data, 'get')
+	},
+	// 提交论坛-敏感词表单 edit为true时为编辑,默认为新增
+	forumSensitivitySubmitForm(data, edit = false) {
+		return request(edit ? 'edit' : 'add', data)
+	},
+	// 删除论坛-敏感词
+	forumSensitivityDelete(data) {
+		return request('delete', data)
+	},
+	// 获取论坛-敏感词详情
+	forumSensitivityDetail(data) {
+		return request('detail', data, 'get')
+	}}

+ 27 - 0
src/api/forum/forumSensitivityRecordApi.js

@@ -0,0 +1,27 @@
+import { baseRequest } from '@/utils/request'
+
+const request = (url, ...arg) => baseRequest(`/api/forumapp/forum/sensitivityrecord/` + url, ...arg)
+
+/**
+ * 敏感词过滤记录Api接口管理器
+ *
+ * @author 金吉龙
+ * @date  2025/07/16 08:41
+ **/
+export default {
+	// 获取敏感词过滤记录分页
+	forumSensitivityRecordPage(data) {
+		return request('page', data, 'get')
+	},
+	// 提交敏感词过滤记录表单 edit为true时为编辑,默认为新增
+	forumSensitivityRecordSubmitForm(data, edit = false) {
+		return request(edit ? 'edit' : 'add', data)
+	},
+	// 删除敏感词过滤记录
+	forumSensitivityRecordDelete(data) {
+		return request('delete', data)
+	},
+	// 获取敏感词过滤记录详情
+	forumSensitivityRecordDetail(data) {
+		return request('detail', data, 'get')
+	}}

+ 2 - 0
src/api/hour/index.js

@@ -9,4 +9,6 @@ const request = moduleRequest(`/api/webapp/`)
 // 获取文件列表(区分文件路径)
 //收藏增加
 export const add = (p) => request('/disk/hour/add', p, 'post')
+export const edit = (p) => request('/disk/hour/edit', p, 'post')
 export const detail = (p) => request('/disk/hour/detail', p, 'get')
+export const del = (p) => request('/disk/hour/delete', p, 'post')

+ 5 - 0
src/components/UpLoadBreakPoint/index.vue

@@ -793,10 +793,15 @@
 		uploadFileList.value = []
 		fileList.value = []
 	}
+	const open = () => {
+		clearFileList()
+	}
 
 	onMounted(() => {
 		// getList()
 	})
+
+	defineExpose({open})
 </script>
 
 <style scoped></style>

+ 5 - 3
src/components/UpLoadDoc/index.vue

@@ -84,8 +84,10 @@ const handleChange = (res) => {
 }
 
 //回显用显示图片
-const setDoc = (src) => {
-	form.coverUrl = src
+const setData = (item) => {
+	form.id = item.id
+	form.name = item.name
+	emit('handlerUpDoc',form.id)
 }
 const beforeUploadDoc = (file) =>{
 	const isDoc = /\.(ppt|pptx|doc|docx|pdf)$/i.test(file.name)
@@ -125,7 +127,7 @@ const handleCancel = () => {
 }
 
 defineExpose({
-	getData,setFile,setDoc
+	getData,setFile,setData
 })
 </script>
 

+ 21 - 25
src/components/UpLoadImg/index.vue

@@ -9,7 +9,7 @@
 			@change ="handleChange"
 		>
 			<div class="cover-upload-box">
-				<img v-if="form.coverUrl" :src="form.coverUrl" class="cover-img"/>
+				<img v-if="form.coverUrl != ''" :src="form.coverUrl" class="cover-img"/>
 				<div v-else class="cover-placeholder">
 					<PictureOutlined style="font-size: 32px; color: #bbb"/>
 				</div>
@@ -25,19 +25,22 @@ 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/uploadMinioReturnId')
 const headers = ref({
 	token: tool.data.get('TOKEN')
 })
+import sysConfig from '@/config/index'
 //课程类型
 
 
 const props = defineProps({
 	count: Number,
-	default : () => 1
+	default : () => 1,
+	modelValue: [String, Number, Boolean, Object, Array, null]
 })
-const emit = defineEmits(['update:visible', 'ok','handlerUpSelect','handlerNewSelect','handlerUpImage'])
+
+const emit = defineEmits(['update:visible','update:modelValue', 'ok','handlerUpSelect','handlerNewSelect','handlerUpImage'])
 
 const modalVisible = ref(props.visible)
 watch(
@@ -65,17 +68,6 @@ const form = reactive({
 	srtUrl: ''
 })
 
-const rules = {
-	title: [{required: true, message: '请输入课时名称'}],
-	video: [{required: true, message: '请选择或上传视频'}],
-	coverUrl: [{required: true, message: '请上传封面'}]
-}
-
-// mock视频资源
-const videoList = ref([
-	{id: 'v1', name: '示例视频1.mp4'},
-	{id: 'v2', name: '示例视频2.mp4'}
-])
 
 const beforeUploadImg = (file)=> {
 	const isImg = file.type === 'image/jpeg' || file.type === 'image/png'
@@ -98,23 +90,27 @@ const beforeUploadImg = (file)=> {
 }
 
 //回显用显示图片
-const setImage = (src) => {
-	form.coverUrl = src
+const setData = (data) => {
+	if(data.url == ''){
+		form.coverUrl = ''
+	}else{
+		form.coverUrl = sysConfig.FILE_URL+data.url
+	}
+
+	form.id = data.id
+	emit('handlerUpImage',form.id)
+	emit('update:modelValue', form.id)
 }
 
 
-const dummyRequest = ({ file, onSuccess, onError }) => {
-	console.log('走了吗',onSuccess)
-	setTimeout(() => {
-		onSuccess('ok')
-	}, 500)
-}
+
 const handleChange = (res) => {
 	console.log('上传图片',res)
-	if (res.file && res.file.response &&res.file.response.code == 200) {
+	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('上传失败')
 	}
@@ -143,7 +139,7 @@ const handleCancel = () => {
 }
 
 defineExpose({
-	getData,setFile,setImage
+	getData,setFile,setData
 })
 </script>
 

+ 5 - 3
src/components/UpLoadSrt/index.vue

@@ -91,8 +91,10 @@ const beforeUploadSrt = (file) => {
 	return true
 }
 //回显用显示图片
-const setImage = (src) => {
-	form.coverUrl = src
+const setData = (data) => {
+	form.id = data.id
+	form.name = data.name
+	emit('handlerUpSrt',form.id)
 }
 
 
@@ -132,7 +134,7 @@ const handleCancel = () => {
 }
 
 defineExpose({
-	getData,setFile,setImage
+	getData,setFile,setData
 })
 </script>
 

+ 153 - 139
src/components/XnWorkflow/nodes/addNode.vue

@@ -1,152 +1,166 @@
-	<div class="add-node-btn-<template>
-box">
-		<div class="add-node-btn">
-			<a-popover v-model:visible="visible" placement="rightTop" trigger="click" :width="270">
-				<template #content>
-					<div class="add-node-popover-body">
-						<ul style="height: 80px">
-							<li>
-								<a-button shape="circle" size="large" @click="addType('userTask')">
-									<template #icon>
-										<user-outlined style="color: #ff943e; font-size: 18px" />
-									</template>
-								</a-button>
-								<p>审批节点</p>
-							</li>
-							<li>
-								<a-button shape="circle" size="large" @click="addType('serviceTask')">
-									<template #icon>
-										<send-outlined style="color: #3296fa; font-size: 18px" />
-									</template>
-								</a-button>
-								<p>抄送节点</p>
-							</li>
-							<li v-if="addExclusiveGateway">
-								<a-button shape="circle" size="large" @click="addType('exclusiveGateway')">
-									<template #icon>
-										<share-alt-outlined style="color: #15bc83; font-size: 18px" />
-									</template>
-								</a-button>
-								<p>条件分支</p>
-							</li>
-							<li v-if="addParallelGateway">
-								<a-button shape="circle" size="large" @click="addType('parallelGateway')">
-									<template #icon>
-										<partition-outlined :rotate="180" style="color: #ac28f5; font-size: 18px" />
-									</template>
-								</a-button>
-								<p>并行分支</p>
-							</li>
-						</ul>
-					</div>
-				</template>
-				<a-button type="primary" shape="circle">
-					<!--  @click="addNodeButton" -->
-					<template #icon><plus-outlined /></template>
-				</a-button>
-			</a-popover>
-		</div>
-	</div>
+<template>
+<div class="add-node-btn-box">
+
+<div class="add-node-btn">
+	<a-popover v-model:visible="visible" placement="rightTop" trigger="click" :width="270">
+		<template #content>
+			<div class="add-node-popover-body">
+				<ul style="height: 80px">
+					<li>
+						<a-button shape="circle" size="large" @click="addType('userTask')">
+							<template #icon>
+								<user-outlined style="color: #ff943e; font-size: 18px"/>
+							</template>
+						</a-button>
+						<p>审批节点</p>
+					</li>
+					<li>
+						<a-button shape="circle" size="large" @click="addType('serviceTask')">
+							<template #icon>
+								<send-outlined style="color: #3296fa; font-size: 18px"/>
+							</template>
+						</a-button>
+						<p>抄送节点</p>
+					</li>
+					<li v-if="addExclusiveGateway">
+						<a-button shape="circle" size="large" @click="addType('exclusiveGateway')">
+							<template #icon>
+								<share-alt-outlined style="color: #15bc83; font-size: 18px"/>
+							</template>
+						</a-button>
+						<p>条件分支</p>
+					</li>
+					<li v-if="addParallelGateway">
+						<a-button shape="circle" size="large" @click="addType('parallelGateway')">
+							<template #icon>
+								<partition-outlined :rotate="180" style="color: #ac28f5; font-size: 18px"/>
+							</template>
+						</a-button>
+						<p>并行分支</p>
+					</li>
+				</ul>
+			</div>
+		</template>
+		<a-button type="primary" shape="circle">
+			<!--  @click="addNodeButton" -->
+			<template #icon>
+				<plus-outlined/>
+			</template>
+		</a-button>
+	</a-popover>
+</div>
+</div>
 </template>
 
 <script>
-	import { cloneDeep } from 'lodash-es'
-	import config from '@/components/XnWorkflow/nodes/config/config'
-	const NodeTitleMap = {
-		userTask: '审核人',
-		serviceTask: '抄送人',
-		exclusiveGateway: '条件路由',
-		parallelGateway: '并行路由'
-	}
-	export default {
-		props: {
-			modelValue: { type: Object, default: () => {} },
-			parentData: { type: Object, default: () => {} },
-			nodeItem: { type: Object, default: () => {} }
+import {cloneDeep} from 'lodash-es'
+import config from '@/components/XnWorkflow/nodes/config/config'
+
+const NodeTitleMap = {
+	userTask: '审核人',
+	serviceTask: '抄送人',
+	exclusiveGateway: '条件路由',
+	parallelGateway: '并行路由'
+}
+export default {
+	props: {
+		modelValue: {
+			type: Object, default: () => {
+			}
 		},
-		emits: ['update:modelValue'],
-		data() {
-			return {
-				visible: false,
-				addExclusiveGateway: true,
-				addParallelGateway: true
+		parentData: {
+			type: Object, default: () => {
 			}
 		},
-		mounted() {},
-		methods: {
-			addNodeButton() {
-				// 他的上级是条件分支或并行分支,将其不在添加 // 控制节点下面
-				if (!this.parentData) {
-					this.disabledChildren()
-				} else {
-					if (this.parentData.type === 'exclusiveGateway' || this.parentData.type === 'parallelGateway') {
-						this.addExclusiveGateway = false
-						this.addParallelGateway = false
-					}
-				}
-			},
-			disabledChildren() {
-				// 如果下级是条件分支或并行分支,将其不在添加 // 控制节点上面
-				if (this.modelValue && this.modelValue.type) {
-					if (this.modelValue.type === 'exclusiveGateway' || this.modelValue.type === 'parallelGateway') {
-						this.addExclusiveGateway = false
-						this.addParallelGateway = false
-					}
-				}
-				// 不管其他的,如果是条件分支的项,那么他的下面无法添加条件
-				if (this.nodeItem) {
+		nodeItem: {
+			type: Object, default: () => {
+			}
+		}
+	},
+	emits: ['update:modelValue'],
+	data() {
+		return {
+			visible: false,
+			addExclusiveGateway: true,
+			addParallelGateway: true
+		}
+	},
+	mounted() {
+	},
+	methods: {
+		addNodeButton() {
+			// 他的上级是条件分支或并行分支,将其不在添加 // 控制节点下面
+			if (!this.parentData) {
+				this.disabledChildren()
+			} else {
+				if (this.parentData.type === 'exclusiveGateway' || this.parentData.type === 'parallelGateway') {
 					this.addExclusiveGateway = false
+					this.addParallelGateway = false
 				}
-			},
-			getBaseCondition(type, title) {
-				const condition = cloneDeep(config.nodeModel.node)
-				condition.id = this.$TOOL.snowyUuid()
-				condition.type = type
-				condition.title = title
-				return condition
-			},
-			addType(type) {
-				const nodeModel = this.getBaseCondition(type, NodeTitleMap[type]) || {}
-				nodeModel.childNode = this.modelValue
-				if (type === 'userTask') {
-					// 创建 configInfo
-					const configInfo = cloneDeep(config.nodeConfigInfo.userTaskConfigInfo)
-					nodeModel.properties.configInfo = configInfo
-				} else if (type === 'exclusiveGateway') {
-					nodeModel.dataLegal = true
-					// 创建分支节点1
-					const condition1 = this.getBaseCondition('sequenceFlow', '条件1')
-					// 创建分支节点1 configInfo
-					const condition1ConfigInfo1 = cloneDeep(config.nodeConfigInfo.conditionConfigInfo)
-					condition1ConfigInfo1.priorityLevel = 1
-					condition1.properties.configInfo = condition1ConfigInfo1
-					// 创建分支节点2
-					const condition2 = this.getBaseCondition('sequenceFlow', '条件2')
-					// 创建分支节点2 configInfo
-					const condition1ConfigInfo2 = cloneDeep(config.nodeConfigInfo.conditionConfigInfo)
-					condition1ConfigInfo2.priorityLevel = 2
-					condition2.properties.configInfo = condition1ConfigInfo2
-					// 装进去
-					nodeModel.conditionNodeList.push(condition1)
-					nodeModel.conditionNodeList.push(condition2)
-				} else if (type === 'parallelGateway') {
-					// 创建主节点
-					nodeModel.dataLegal = true
-					// 创建分支节点1
-					const condition1 = this.getBaseCondition('userTask', '审批人1')
-					condition1.properties.configInfo = cloneDeep(config.nodeConfigInfo.userTaskConfigInfo)
-					condition1.dataLegal = true
-					// 创建分支节点2
-					const condition2 = this.getBaseCondition('userTask', '审批人2')
-					condition2.properties.configInfo = cloneDeep(config.nodeConfigInfo.userTaskConfigInfo)
-					condition2.dataLegal = true
-					// 装进去
-					nodeModel.conditionNodeList.push(condition1)
-					nodeModel.conditionNodeList.push(condition2)
+			}
+		},
+		disabledChildren() {
+			// 如果下级是条件分支或并行分支,将其不在添加 // 控制节点上面
+			if (this.modelValue && this.modelValue.type) {
+				if (this.modelValue.type === 'exclusiveGateway' || this.modelValue.type === 'parallelGateway') {
+					this.addExclusiveGateway = false
+					this.addParallelGateway = false
 				}
-				this.visible = false
-				this.$emit('update:modelValue', nodeModel)
 			}
+			// 不管其他的,如果是条件分支的项,那么他的下面无法添加条件
+			if (this.nodeItem) {
+				this.addExclusiveGateway = false
+			}
+		},
+		getBaseCondition(type, title) {
+			const condition = cloneDeep(config.nodeModel.node)
+			condition.id = this.$TOOL.snowyUuid()
+			condition.type = type
+			condition.title = title
+			return condition
+		},
+		addType(type) {
+			const nodeModel = this.getBaseCondition(type, NodeTitleMap[type]) || {}
+			nodeModel.childNode = this.modelValue
+			if (type === 'userTask') {
+				// 创建 configInfo
+				const configInfo = cloneDeep(config.nodeConfigInfo.userTaskConfigInfo)
+				nodeModel.properties.configInfo = configInfo
+			} else if (type === 'exclusiveGateway') {
+				nodeModel.dataLegal = true
+				// 创建分支节点1
+				const condition1 = this.getBaseCondition('sequenceFlow', '条件1')
+				// 创建分支节点1 configInfo
+				const condition1ConfigInfo1 = cloneDeep(config.nodeConfigInfo.conditionConfigInfo)
+				condition1ConfigInfo1.priorityLevel = 1
+				condition1.properties.configInfo = condition1ConfigInfo1
+				// 创建分支节点2
+				const condition2 = this.getBaseCondition('sequenceFlow', '条件2')
+				// 创建分支节点2 configInfo
+				const condition1ConfigInfo2 = cloneDeep(config.nodeConfigInfo.conditionConfigInfo)
+				condition1ConfigInfo2.priorityLevel = 2
+				condition2.properties.configInfo = condition1ConfigInfo2
+				// 装进去
+				nodeModel.conditionNodeList.push(condition1)
+				nodeModel.conditionNodeList.push(condition2)
+			} else if (type === 'parallelGateway') {
+				// 创建主节点
+				nodeModel.dataLegal = true
+				// 创建分支节点1
+				const condition1 = this.getBaseCondition('userTask', '审批人1')
+				condition1.properties.configInfo = cloneDeep(config.nodeConfigInfo.userTaskConfigInfo)
+				condition1.dataLegal = true
+				// 创建分支节点2
+				const condition2 = this.getBaseCondition('userTask', '审批人2')
+				condition2.properties.configInfo = cloneDeep(config.nodeConfigInfo.userTaskConfigInfo)
+				condition2.dataLegal = true
+				// 装进去
+				nodeModel.conditionNodeList.push(condition1)
+				nodeModel.conditionNodeList.push(condition2)
+			}
+			this.visible = false
+			this.$emit('update:modelValue', nodeModel)
 		}
 	}
+}
 </script>

+ 131 - 103
src/views/courseAdd/components/courseProduction/addClassHours.vue

@@ -3,23 +3,25 @@
 		<a-form-item label="课时名称:" name="title" required>
 			<a-input v-model:value="form.title" placeholder="输入内容"/>
 		</a-form-item>
-		<a-form-item label="选择视频:" required>
+		<a-form-item label="选择视频:" required name="video">
 			<div class="video-select-row">
 				<a-button type="primary" @click="()=>{emit('handlerSelect')}">选择资源</a-button>
 				<a-button type="primary" @click="()=>{emit('handlerUpSelect')}">新上传资源</a-button>
 			</div>
-			<div style="margin-top: 12px;  margin-bottom: 12px">{{ file.name }}</div>
+			<!--			<span  style="margin-top: 12px;  margin-bottom: 12px">{{ file.name }}</span>-->
+			<FileName ref="fileNameRef" v-model:value="form.video"></FileName>
 		</a-form-item>
-		<a-form-item label="上传封面:" required>
+		<a-form-item label="上传封面:" required name="coverUrl">
 			<div class="cover-upload-row">
-				<UpLoadImg ref="upLoadImgRef" @handlerUpImage="handlerUpImage"></UpLoadImg>
+				<UpLoadImg v-model:value="form.coverUrl" ref="upLoadImgRef"
+						   @handlerUpImage="handlerUpImage"></UpLoadImg>
 			</div>
 		</a-form-item>
-		<a-form-item label="上传讲义:">
-			<UpLoadDoc ref="upLoadDocRef" @handlerUpDoc="handlerUpDoc"></UpLoadDoc>
+		<a-form-item label="上传讲义:" name="docUrl">
+			<UpLoadDoc v-model:value="form.docUrl" ref="upLoadDocRef" @handlerUpDoc="handlerUpDoc"></UpLoadDoc>
 		</a-form-item>
-		<a-form-item label="上传字幕:">
-			<UpLoadSrt ref="UpLoadSrtRef" @handlerUpSrt="handlerUpSrt"></UpLoadSrt>
+		<a-form-item label="上传字幕:" name="srtUrl">
+			<UpLoadSrt v-model:value="form.srtUrl" ref="UpLoadSrtRef" @handlerUpSrt="handlerUpSrt"></UpLoadSrt>
 		</a-form-item>
 
 	</a-form>
@@ -32,25 +34,18 @@ import {PictureOutlined, CloudUploadOutlined} from '@ant-design/icons-vue'
 import UpLoadImg from '@/components/UpLoadImg/index.vue'
 import UpLoadDoc from '@/components/UpLoadDoc/index.vue'
 import UpLoadSrt from '@/components/UpLoadSrt/index.vue'
+import FileName from './fileName.vue'
 import {add, detail} from '@/api/hour/index'
 
 const props = defineProps({
 	visible: Boolean
 })
-const emit = defineEmits(['update:visible', 'ok', 'handlerUpSelect', 'handlerNewSelect'])
-
-const modalVisible = ref(props.visible)
-watch(
-	() => props.visible,
-	(v) => {
-		modalVisible.value = v
-	}
-)
-watch(modalVisible, (v) => {
-	emit('update:visible', v)
-})
+const emit = defineEmits(['update:visible', 'ok', 'handlerUpSelect', 'handlerNewSelect','handlerEx','handlerExs'])
 
+const fileNameRef = ref(null)
 const upLoadImgRef = ref(null)
+const upLoadDocRef = ref(null)
+const UpLoadSrtRef = ref(null)
 const modeTag = ref('add')
 const formRef = ref()
 const file = ref({
@@ -68,15 +63,11 @@ const form = reactive({
 const rules = {
 	title: [{required: true, message: '请输入课时名称'}],
 	video: [{required: true, message: '请选择或上传视频'}],
-	coverUrl: [{required: true, message: '请上传封面'}]
+	coverUrl: [{required: true, message: '请上传封面'}],
+	docUrl: [{required: true, message: '请上传讲义'}],
+	srtUrl: [{required: true, message: '请上传字幕'}],
 }
 
-// mock视频资源
-const videoList = ref([
-	{id: 'v1', name: '示例视频1.mp4'},
-	{id: 'v2', name: '示例视频2.mp4'}
-])
-
 
 const handlerUpImage = (id) => {
 	form.coverUrl = id
@@ -94,13 +85,10 @@ const setFile = (fileData) => {
 	file.value.name = fileData.fileName
 
 	form.video = fileData.id
-}
-
-const handleOk = () => {
-	formRef.value.validate().then(() => {
-		emit('ok', {...form})
-		modalVisible.value = false
+	nextTick(() => {
+		fileNameRef.value.setData({id : fileData.id, name :fileData.fileName })
 	})
+
 }
 
 const getData = (callBack) => {
@@ -109,7 +97,8 @@ const getData = (callBack) => {
 
 		data.name = form.title
 		data.courseRelates = []
-		//todo :1视频资源,2讲义,3字幕,4作业,5测验
+		//todo :0封面 1视频资源,2讲义,3字幕,4作业,5测验
+		data.courseRelates.push({funcType: 0, relateId: form.coverUrl})
 		data.courseRelates.push({funcType: 1, relateId: form.video})
 		data.courseRelates.push({funcType: 2, relateId: form.docUrl})
 		data.courseRelates.push({funcType: 3, relateId: form.srtUrl})
@@ -135,73 +124,121 @@ const getData = (callBack) => {
 	})
 }
 
-const handleCancel = () => {
-	modalVisible.value = false
+const open = () => {
+	modeTag.value = 'add'
+	// reset()
+}
+const reset = () => {
+	console.log("关掉吗")
+	formRef.value.resetFields();
+	nextTick(() => {
+		form.title = ''
+		form.video = ''
+		form.coverUrl = ''
+		form.docUrl = ''
+		form.srtUrl = ''
+
+		upLoadImgRef.value.setData({id: "", url: ""})
+		fileNameRef.value.setData({id: "", name: ""})
+		upLoadDocRef.value.setData({id: "", name: ""})
+		UpLoadSrtRef.value.setData({id: "", name: ""})
+	})
 }
+
+
 const edit = (item) => {
 	// form.value.id = data.id
 	modeTag.value = 'edit'
-
-	detail({id : item.id}).then((res)=>{
-		console.log('需要展示修改了123123', res)
-		if(res.code == 200){
-		     let data =	res.data
-			form.title = 	data.name
-			for (let i = 0; i <data.courseRelates.length ; i++) {
-			  let itemi =data.courseRelates[i]
-				switch (itemi.funcType) {
-					case '1':
-						form.video = itemi.relateId
-						break;
-					case '2':
-						form.docUrl = itemi.relateId
-						break;
-					case '3':
-						form.srtUrl = itemi.relateId
-						break;
+	// let data = {}
+	//
+	// data.name = form.title
+	// data.courseRelates = []
+	// //todo :1视频资源,2讲义,3字幕,4作业,5测验
+	// data.courseRelates.push({funcType : 1,relateId:form.video})
+	// data.courseRelates.push({funcType : 2,relateId:form.docUrl})
+	// data.courseRelates.push({funcType : 3,relateId:form.srtUrl})
+	console.log('修改什么', item)
+	if(item){
+		detail({id: item.id}).then((res) => {
+			if (res.code == 200) {
+				let data = res.data
+				form.title = data.name
+				for (let i = 0; i < data.courseRelates.length; i++) {
+					let itemi = data.courseRelates[i]
+					console.log('每条数据', itemi)
+					switch (itemi.funcType) {
+						case '0':
+							upLoadImgRef.value.setData({id: itemi.relateId, url: itemi.url})
+							break;
+						case '1':
+							// form.video = itemi.relateId
+							// file.value.name = itemi.name
+							form.video = itemi.relateId
+							fileNameRef.value.setData({id: itemi.relateId, name: itemi.name})
+							break;
+						case '2':
+							// form.docUrl = itemi.relateId
+							upLoadDocRef.value.setData({id: itemi.relateId, name: itemi.name})
+							break;
+						case '3':
+							// form.srtUrl = itemi.relateId
+							UpLoadSrtRef.value.setData({id: itemi.relateId, name: itemi.name})
+							break;
+						case '4':
+							// form.srtUrl = itemi.relateId
+							// UpLoadSrtRef.value.setData({id: itemi.relateId, name: itemi.name})
+							emit('handlerEx', [itemi])
+							break;
+						case '5':
+							// form.srtUrl = itemi.relateId
+							// UpLoadSrtRef.value.setData({id: itemi.relateId, name: itemi.name})
+							emit('handlerExs', [itemi])
+							break;
+					}
 				}
 			}
-		}
 
-		// {
-		// 	"code": 200,
-		// 	"msg": "操作成功",
-		// 	"data": {
-		// 	"chapterId": "1945821914740051969",
-		// 		"courseRelates": [
-		// 		{
-		// 			"relateId": "1941793498696044545",
-		// 			"chapterHourType": "1",
-		// 			"name": "1940384168492494848",
-		// 			"mainId": "1946118995975536642",
-		// 			"url": "upload/20250705/3705fd26117d7e6653b13d60e8e0399d.mp4",
-		// 			"funcType": "1"
-		// 		},
-		// 		{
-		// 			"relateId": "1946118974324539394",
-		// 			"chapterHourType": "1",
-		// 			"name": "",
-		// 			"mainId": "1946118995975536642",
-		// 			"url": "",
-		// 			"funcType": "2"
-		// 		},
-		// 		{
-		// 			"relateId": "1946118987192664066",
-		// 			"chapterHourType": "1",
-		// 			"name": "",
-		// 			"mainId": "1946118995975536642",
-		// 			"url": "",
-		// 			"funcType": "3"
-		// 		}
-		// 	],
-		// 		"name": "110",
-		// 		"remark": "",
-		// 		"id": "1946118995975536642"
-		// }
-		// }
-	}).catch((err) => {
+			// {
+			// 	"code": 200,
+			// 	"msg": "操作成功",
+			// 	"data": {
+			// 	"chapterId": "1945821914740051969",
+			// 		"courseRelates": [
+			// 		{
+			// 			"relateId": "1941793498696044545",
+			// 			"chapterHourType": "1",
+			// 			"name": "1940384168492494848",
+			// 			"mainId": "1946118995975536642",
+			// 			"url": "upload/20250705/3705fd26117d7e6653b13d60e8e0399d.mp4",
+			// 			"funcType": "1"
+			// 		},
+			// 		{
+			// 			"relateId": "1946118974324539394",
+			// 			"chapterHourType": "1",
+			// 			"name": "",
+			// 			"mainId": "1946118995975536642",
+			// 			"url": "",
+			// 			"funcType": "2"
+			// 		},
+			// 		{
+			// 			"relateId": "1946118987192664066",
+			// 			"chapterHourType": "1",
+			// 			"name": "",
+			// 			"mainId": "1946118995975536642",
+			// 			"url": "",
+			// 			"funcType": "3"
+			// 		}
+			// 	],
+			// 		"name": "110",
+			// 		"remark": "",
+			// 		"id": "1946118995975536642"
+			// }
+			// }
+		}).catch((err) => {
+
+		})
+	}
 
-	})
 
 	// chapterId:"1945821914740051969"
 	// createTime:"2025-07-18 16:05:00.444"
@@ -209,20 +246,11 @@ const edit = (item) => {
 	// id:"1946118995975536642"
 	// name:"110"
 	// remark:""
-	// let data = {}
-	//
-	// data.name = form.title
-	// data.courseRelates = []
-	// //todo :1视频资源,2讲义,3字幕,4作业,5测验
-	// data.courseRelates.push({funcType : 1,relateId:form.video})
-	// data.courseRelates.push({funcType : 2,relateId:form.docUrl})
-	// data.courseRelates.push({funcType : 3,relateId:form.srtUrl})
-
 
 
 }
 defineExpose({
-	getData, setFile, edit
+	getData, setFile, edit, open, reset
 })
 
 </script>

+ 266 - 142
src/views/courseAdd/components/courseProduction/addDialog.vue

@@ -2,20 +2,22 @@
 	<div>
 		<a-modal
 			v-model:visible="modalVisible"
-			:title="modeTag.value == 'add'?'添加课时':'修改课时'"
+			:title="modeTag == 'add'?'添加课时':'修改课时'"
 			:footer="null"
-			width="700px"
+			width="900px"
 			class="add-class-hours-modal"
 		>
 			<a-tabs v-model:activeKey="activeKey" type="card" @change="handleChange">
 				<a-tab-pane key="1" tab="课时">
-					<addClassHours ref="addClassHoursRef" @handlerSelect="handlerSelect" @handlerUpSelect="handlerUpSelect"></addClassHours>
+					<addClassHours ref="addClassHoursRef" @handlerSelect="handlerSelect"
+								   @handlerUpSelect="handlerUpSelect" @handlerEx="handlerEx"
+								   @handlerExs="handlerExs"></addClassHours>
 				</a-tab-pane>
-				<a-tab-pane key="2" tab="作业" >
-					<div>这里是作业的内容</div>
+				<a-tab-pane key="2" tab="作业">
+					<exList v-if="activeKey == '2'" ref="exListRef" @handlerEx="handlerEx"></exList>
 				</a-tab-pane>
-				<a-tab-pane key="3" tab="考试" >
-					<div>这里是考试的内容</div>
+				<a-tab-pane key="3" tab="考试">
+					<exLists v-if="activeKey == '3'" ref="exListsRef" @handlerExs="handlerExs"></exLists>
 				</a-tab-pane>
 			</a-tabs>
 			<div class="footer-btns">
@@ -25,177 +27,299 @@
 		</a-modal>
 
 		<resListDialog ref="resListDialogRef" @handleSelectFile="handleSelectFile"></resListDialog>
-		<resourceUpload ref="resourceUploadRef"></resourceUpload>
+		<resourceUpload ref="resourceUploadRef" @onSub="onSub"></resourceUpload>
 	</div>
 
 </template>
 
 <script setup>
-	import { ref, reactive, watch, defineProps, defineEmits } from 'vue'
-	import { message } from 'ant-design-vue'
-	import addClassHours from './addClassHours.vue'
-	import resListDialog from './resListDialog.vue'
-	import resourceUpload from './resourceUpload.vue'
-	import { add } from '@/api/hour/index'
-
-	const addClassHoursRef = ref(null)
-	const resListDialogRef = ref(null)
-	const resourceUploadRef = ref(null)
-	const activeKey = ref('1')
-	const modeTag = ref('add')
-
-	const emit = defineEmits(['update:visible', 'ok','onAddChapter'])
-
-	const props = defineProps({
-		//课程id
-		courseInfoId: {
-			type: Number,
-			required: true,
-			default: null
-		},
-		// visible: Boolean
+import {ref, reactive, watch, defineProps, defineEmits} from 'vue'
+import {message} from 'ant-design-vue'
+import addClassHours from './addClassHours.vue'
+import resListDialog from './resListDialog.vue'
+import resourceUpload from './resourceUpload.vue'
+import exList from './exList.vue'
+import exLists from './exLists.vue'
+import {add, edit as editApi} from '@/api/hour/index'
+
+const addClassHoursRef = ref(null)
+const resListDialogRef = ref(null)
+const resourceUploadRef = ref(null)
+const exListRef = ref(null)
+const exListsRef = ref(null)
+const exListRefData = ref(null)
+const exListsRefData = ref(null)
+const exListRefCount = ref(0)
+const exListsRefCount = ref(0)
+const activeKey = ref('1')
+const modeTag = ref('add')
+
+const emit = defineEmits(['update:visible', 'ok', 'onAddChapter'])
+
+const props = defineProps({
+	//课程id
+	courseInfoId: {
+		type: Number,
+		required: true,
+		default: null
+	},
+	// visible: Boolean
+})
+
+const form = ref({
+	id: '',
+	courseId: ''
+})
+
+
+const modalVisible = ref(false)
+// watch(
+// 	() => props.visible,
+// 	(v) => {
+// 		modalVisible.value = v
+// 	}
+// )
+// watch(modalVisible, (v) => {
+// 	emit('update:visible', v)
+// })
+
+const open = () => {
+	exListRefCount.value = 0
+	exListsRefCount.value = 0
+	modalVisible.value = true
+	activeKey.value = '1'
+	modeTag.value = 'add'
+	nextTick(() => {
+		addClassHoursRef.value.reset()
+		addClassHoursRef.value.open()
 	})
+}
+const handleChange = (activeKey) => {
+	nextTick(() => {
+		if (activeKey == '1' && addClassHoursRef.value) {
+			if (modeTag.value == 'add') {
+				addClassHoursRef.value.reset()
+				addClassHoursRef.value.open()
+			}
+			if (modeTag.value == 'edit') {
+				addClassHoursRef.value.edit()
+			}
+		}
+		if (activeKey == '2' && exListRef.value) {
+				if (modeTag.value == 'add') {
+					exListRef.value.open()
+				}
+				if (modeTag.value == 'edit') {
+					console.log('走没走1', exListRefData.value)
+					if(exListRefData.value && exListRefData.value[0]&& exListRefData.value[0].relateId){
+						exListRef.value.edit(exListRefData.value[0].relateId)
+					}else if(exListRefData.value && exListRefData.value[0]&& exListRefData.value[0].id){
+						exListRef.value.edit(exListRefData.value[0].id)
+					}
+					console.log('走没走2', exListRefData.value)
+					if(exListRefData.value  == null){
+						exListRef.value.open()
+					}
+				}
 
-	const form = ref({
-		id : ''
+		}
+		if (activeKey == '3' && exListsRef.value) {
+				if (modeTag.value == 'add') {
+					exListsRef.value.open()
+				}
+				if (modeTag.value == 'edit') {
+					console.log('走没走3', exListsRefData.value)
+					if(exListsRefData.value && exListsRefData.value[0]&& exListsRefData.value[0].relateId){
+						exListsRef.value.edit(exListsRefData.value[0].relateId)
+					}else if(exListRefData.value && exListRefData.value[0]&& exListRefData.value[0].id){
+						exListRef.value.edit(exListRefData.value[0].id)
+					}
+					console.log('走没走4', exListsRefData.value)
+					if(exListsRefData.value  == null){
+						exListsRef.value.open()
+					}
+				}
+		}
 	})
 
+}
+const setData = (data) => {
+	console.log('进来的章节信息添加的时候', data)
+	form.value.id = data.id
+}
+const edit = (item) => {
+	exListRefCount.value = 0
+	exListsRefCount.value = 0
+	activeKey.value = '1'
+	modalVisible.value = true
+	console.log('修改进来的', item)
+	form.value.id = item.id
+	form.value.chapterId = item.courseId
 
-	const modalVisible = ref(false)
-	// watch(
-	// 	() => props.visible,
-	// 	(v) => {
-	// 		modalVisible.value = v
-	// 	}
-	// )
-	// watch(modalVisible, (v) => {
-	// 	emit('update:visible', v)
-	// })
+	modeTag.value = 'edit'
+	console.log('有没有', addClassHoursRef.value)
+	nextTick(() => {
+		addClassHoursRef.value.edit(item)
+	})
 
-	const open = () =>{
-		activeKey.value = '1'
-		modalVisible.value = true
-		modeTag.value = 'add'
-	}
-	const handleChange = (activeKey) =>{
+}
+const handlerEx = (item) => {
+	console.log('有没有1  ', item)
+	exListRefData.value = item
+	// exListRef.value.setData(item)
+}
+const handlerExs = (item) => {
+	exListsRefData.value = item
+	// exListsRef.value.setData(item)
+}
+const handlerSelect = () => {
+	resListDialogRef.value.open()
+}
+const handlerUpSelect = () => {
+	resourceUploadRef.value.open()
+}
+const handleSelectFile = (item) => {
+	resListDialogRef.value.close()
+	addClassHoursRef.value.setFile(item)
+}
+const onSub = (list) => {
 
-	}
-	const setData = (data) => {
-		console.log('进来的章节信息',data)
-		form.value.id = data.id
-	}
-	const edit = (item) => {
-		activeKey.value = '1'
-		console.log('进来的章节信息',item)
-		// form.value.id = data.id
-		modalVisible.value = true
-		modeTag.value = 'edit'
-		console.log('有没有',addClassHoursRef.value)
-		nextTick(()=>{
-			addClassHoursRef.value.edit(item)
-		})
+	console.log('上传的id', list)
+	// resListDialogRef.value.close()
+	addClassHoursRef.value.setFile(list)
 
-	}
+	// addClassHoursRef.value.close()
+}
+const handleOk = () => {
 
-	const handlerSelect = () =>{
-		resListDialogRef.value.open()
-	}
-	const handlerUpSelect = () =>{
-		resourceUploadRef.value.open()
-	}
-	const handleSelectFile = (item) =>{
-		resListDialogRef.value.close()
-		addClassHoursRef.value.setFile(item)
-	}
-	const  handleOk = () =>{
 
-		console.log('有没有',addClassHoursRef)
-		addClassHoursRef.value.getData((data)=>{
-			//设置章节id
-			data.chapterId = form.value.id
-			// props.courseInfoId
-			console.log('提交的参数',data)
+	addClassHoursRef.value.getData((data) => {
 
-			add(data).then((res)=>{
+		let exlist = exListRefData.value
+		if (exlist && exlist.length == 1) {
+			if(exlist[0].id){
+				data.courseRelates.push({funcType: 4, relateId: exlist[0].id})
+			}else if(exlist[0].relateId){
+				data.courseRelates.push({funcType: 4, relateId: exlist[0].relateId})
+			}
+		}
+		let exlists = exListsRefData.value
+		if (exlists &&exlists.length == 1) {
+			if(exlists[0].id){
+				data.courseRelates.push({funcType: 5, relateId: exlists[0].id})
+			}else if(exlist[0].relateId){
+				data.courseRelates.push({funcType: 5, relateId: exlists[0].relateId})
+			}
+		}
+
+		//设置章节id
+		data.chapterId = form.value.id
+		// props.courseInfoId
+		console.log('提交的参数', data)
+		if (modeTag.value == 'add') {
+			add(data).then((res) => {
 				modalVisible.value = false
 				emit('onAddChapter')
+				resourceUploadRef.value.close()
 			}).catch((err) => {
 
 			})
+		}
+		if (modeTag.value == 'edit') {
+			data.id = form.value.id
+			data.chapterId = form.value.chapterId
+			editApi(data).then((res) => {
+				modalVisible.value = false
+				emit('onAddChapter')
+			}).catch((err) => {
 
-		})
-		// formRef.value.validate().then(() => {
-		// 	emit('ok', { ...form })
-		// 	modalVisible.value = false
-		// })
-	}
-	function handleCancel() {
-		modalVisible.value = false
-	}
+			})
+		}
 
-	defineExpose({ open ,setData,edit})
+	})
+	// formRef.value.validate().then(() => {
+	// 	emit('ok', { ...form })
+	// 	modalVisible.value = false
+	// })
+}
+
+function handleCancel() {
+	modalVisible.value = false
+}
+
+defineExpose({open, setData, edit})
 </script>
 
 <style lang="less" scoped>
-	.add-class-hours-modal {
-		.ant-modal-content {
-			border-radius: 10px;
-		}
-		.ant-modal-header {
-			border-radius: 10px 10px 0 0;
-		}
-		.ant-form-item {
-			margin-bottom: 24px;
-		}
-		.video-select-row {
-			display: flex;
-			align-items: center;
-		}
-		.cover-upload-row {
+.add-class-hours-modal {
+	.ant-modal-content {
+		border-radius: 10px;
+	}
+
+	.ant-modal-header {
+		border-radius: 10px 10px 0 0;
+	}
+
+	.ant-form-item {
+		margin-bottom: 24px;
+	}
+
+	.video-select-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;
-			.cover-upload-box {
-				width: 120px;
-				height: 120px;
-				background: #f7f8fa;
+			justify-content: center;
+			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;
-				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;
+				width: 100%;
+				height: 100%;
+				color: #bbb;
+				font-size: 32px;
 			}
 		}
-		.upload-tip {
+
+		.cover-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>

+ 337 - 0
src/views/courseAdd/components/courseProduction/exList.vue

@@ -0,0 +1,337 @@
+<template>
+	<div class="app-container">
+
+		<div v-if="modeTag == 'list'">
+			<!-- 查询表单 -->
+			<a-form :model="queryParam" layout="inline" class="search-form">
+				<a-form-item label="题目ID:">
+					<a-input v-model:value="queryParam.id" placeholder="请输入题目ID" allow-clear />
+				</a-form-item>
+
+				<!-- <a-form-item label="年级:">
+					<a-select
+						v-model:value="queryParam.level"
+						placeholder="请选择年级"
+						@change="levelChange"
+						allow-clear
+						style="width: 120px"
+					>
+						<a-select-option v-for="item in levelEnum" :key="item.key" :value="item.key">
+							{{ item.value }}
+						</a-select-option>
+					</a-select>
+				</a-form-item>
+				<a-form-item label="学科:">
+					<a-select v-model:value="queryParam.subjectId" placeholder="请选择学科" allow-clear style="width: 200px">
+						<a-select-option v-for="item in subjectFilter" :key="item.id" :value="item.id">
+							{{ item.name }} ( {{ item.levelName }} )
+						</a-select-option>
+					</a-select>
+				</a-form-item> -->
+				<a-form-item>
+					<a-button type="primary" @click="submitForm">查询</a-button>
+					<!--				<a-button type="primary" @click="openDrawer('add')" style="margin-left: 8px">添加</a-button>-->
+				</a-form-item>
+			</a-form>
+
+			<!-- 数据表格 -->
+			<a-table
+				:loading="listLoading"
+				:data-source="tableData"
+				:columns="columns"
+				:pagination="false"
+				row-key="id"
+				class="data-table"
+				:locale="{ emptyText: '暂无数据' }"
+			>
+				<template #bodyCell="{ column, record }">
+					<template v-if="column.key === 'action'">
+						<a-button style="margin-right: 10px" type="primary" @click="handleOk(record)">选择
+						</a-button>
+						<!--					<a-button size="small" @click="openDrawer('edit', record.id)">编辑</a-button>-->
+						<!--					<a-button size="small" type="primary" danger @click="deletePaper(record)" style="margin-left: 8px">-->
+						<!--						删除-->
+						<!--					</a-button>-->
+					</template>
+				</template>
+			</a-table>
+
+			<!-- 分页 -->
+			<a-pagination
+				v-if="total > 0"
+				:current="queryParam.pageIndex"
+				:page-size="queryParam.pageSize"
+				:total="total"
+				:show-size-changer="true"
+				:show-quick-jumper="true"
+				:show-total="(total, range) => `第 ${range[0]}-${range[1]} 条/共 ${total} 条`"
+				@change="handlePageChange"
+				@show-size-change="handlePageSizeChange"
+				class="pagination"
+			/>
+
+			<!-- 编辑/添加 抽屉 -->
+			<a-drawer
+				:visible="drawerVisible"
+				:title="drawerTitle"
+				placement="right"
+				width="900"
+				@close="closeDrawer"
+				destroyOnClose
+			>
+				<!--			<FormEdit v-if="drawerVisible" :id="editId" @success="onEditSuccess" />-->
+			</a-drawer>
+		</div>
+
+		<div v-if="modeTag == 'item'">
+			<a-table
+				:loading="listLoading"
+				:data-source="itemDatas"
+				:columns="columns"
+				:pagination="false"
+				row-key="id"
+				class="data-table"
+				:locale="{ emptyText: '暂无数据' }"
+			>
+				<template #bodyCell="{ column, record }">
+					<template v-if="column.key === 'action'">
+						<a-button style="margin-right: 10px" type="primary" @click="handleReset(record)">重新选择
+						</a-button>
+						<!--					<a-button size="small" @click="openDrawer('edit', record.id)">编辑</a-button>-->
+						<!--					<a-button size="small" type="primary" danger @click="deletePaper(record)" style="margin-left: 8px">-->
+						<!--						删除-->
+						<!--					</a-button>-->
+					</template>
+				</template>
+			</a-table>
+		</div>
+
+	</div>
+</template>
+
+<script setup>
+	import { ref, reactive, computed, onMounted } from 'vue'
+	import { message, Modal } from 'ant-design-vue'
+	import { useExamStore } from '@/store/exam'
+	import examPaperApi from '@/api/exam/paper/examPaperApi'
+	// import FormEdit from './form.vue'
+	import { parseTime } from '@/utils/exam'
+	const emit = defineEmits(['handlerEx'])
+	const examStore = useExamStore()
+
+	const modeTag = ref('list')
+	const itemDatas = ref([])
+	// 响应式数据
+	const queryParam = reactive({
+		id: null,
+		// level: null,
+		// subjectId: null,
+		current: 1,
+		size: 10
+	})
+
+	const subjectFilter = ref([])
+	const listLoading = ref(false)
+	const tableData = ref([])
+	const total = ref(0)
+
+	// Drawer 控制
+	const drawerVisible = ref(false)
+	const drawerTitle = ref('')
+	const editId = ref(null)
+
+	function openDrawer(type, id = null) {
+		if (type === 'add') {
+			drawerTitle.value = '添加试卷'
+			editId.value = null
+		} else {
+			drawerTitle.value = '编辑试卷'
+			editId.value = id
+		}
+		drawerVisible.value = true
+	}
+	function closeDrawer() {
+		drawerVisible.value = false
+	}
+	function onEditSuccess() {
+		closeDrawer()
+		search()
+	}
+	const open=() =>{
+		examStore.initSubject(search)
+	}
+	// 表格列配置
+	const columns = [
+		{
+			title: 'Id',
+			dataIndex: 'id',
+			key: 'id',
+			width: 90
+		},
+		{
+			title: '学科',
+			dataIndex: 'subjectId',
+			key: 'subjectId',
+			width: 200,
+			customRender: ({ text }) => examStore.subjectEnumFormat(text)
+		},
+		{
+			title: '名称',
+			dataIndex: 'name',
+			key: 'name'
+		},
+		{
+			title: '创建时间',
+			dataIndex: 'createTime',
+			key: 'createTime',
+			width: 200,
+			customRender: ({ text }) => parseTime(text, '{y}-{m}-{d} {h}:{i}:{s}')
+		},
+		{
+			title: '操作',
+			key: 'action',
+			width: 160,
+			align: 'center'
+		}
+	]
+	// 计算属性
+	const levelEnum = computed(() => examStore.getLevelEnum)
+
+	// 方法
+	const submitForm = () => {
+		queryParam.pageIndex = 1
+		search()
+	}
+
+	const search = async () => {
+		listLoading.value = true
+		try {
+			const response = await examPaperApi.pageList(queryParam)
+			if (response) {
+				const data = response
+				tableData.value = data.records || []
+				total.value = data.total || 0
+				queryParam.pageIndex = data.current || 1
+				listLoading.value = false
+			} else {
+				message.error(response.message || '获取数据失败')
+			}
+		} catch (error) {
+			console.error('获取试卷列表失败:', error)
+			message.error('获取数据失败')
+		} finally {
+			listLoading.value = false
+		}
+	}
+
+	const deletePaper = async (row) => {
+		try {
+			// 显示确认对话框
+			const confirmed = await new Promise((resolve) => {
+				Modal.confirm({
+					title: '确认删除',
+					content: `确定要删除试卷"${row.name}"吗?`,
+					okText: '确定',
+					cancelText: '取消',
+					onOk: () => resolve(true),
+					onCancel: () => resolve(false)
+				})
+			})
+
+			if (!confirmed) return
+
+			await examPaperApi.deletePaper(row.id)
+			search()
+		} catch (error) {
+			console.error('删除试卷失败:', error)
+			message.error('删除失败')
+		}
+	}
+
+	const levelChange = () => {
+		queryParam.subjectId = null
+		subjectFilter.value = examStore.subjects.filter((data) => data.level === queryParam.level)
+	}
+
+	const handlePageChange = (page, pageSize) => {
+		queryParam.pageIndex = page
+		queryParam.pageSize = pageSize
+		search()
+	}
+
+	const handlePageSizeChange = (current, size) => {
+		queryParam.pageIndex = 1
+		queryParam.pageSize = size
+		search()
+	}
+	const handleOk = (item) => {
+		console.log("选取了",item)
+		modeTag.value = 'item'
+		itemDatas.value = []
+		itemDatas.value.push(item)
+
+		emit('handlerEx', [item])
+
+	}
+	const handleReset = (item) => {
+		console.log("选取了",item)
+		modeTag.value = 'list'
+		itemDatas.value = []
+		queryParam.pageIndex = 1
+		search()
+
+		emit('handlerEx', null)
+	}
+	const getItemData = () => {
+	  return 	itemDatas.value
+	}
+	const edit = async (id) => {
+		modeTag.value = 'item'
+		listLoading.value = true
+		try {
+			const response = await examPaperApi.pageList({id :id,current : 1 ,size:10 })
+			if (response) {
+				const data = response
+				itemDatas.value = data.records || []
+				listLoading.value = false
+			} else {
+				message.error(response.message || '获取数据失败')
+			}
+		} catch (error) {
+			console.error('获取试卷列表失败:', error)
+			message.error('获取数据失败')
+		} finally {
+			listLoading.value = false
+		}
+
+	}
+
+	// 生命周期
+	onMounted(async () => {
+
+	})
+
+	defineExpose({getItemData,edit,open})
+</script>
+
+<style lang="less" scoped>
+	.app-container {
+		padding: 24px;
+		background: #fff;
+
+		.search-form {
+			margin-bottom: 24px;
+			padding: 24px;
+			background: #fafafa;
+			border-radius: 6px;
+		}
+
+		.data-table {
+			margin-bottom: 24px;
+		}
+
+		.pagination {
+			text-align: right;
+		}
+	}
+</style>

+ 333 - 0
src/views/courseAdd/components/courseProduction/exLists.vue

@@ -0,0 +1,333 @@
+<template>
+	<div class="app-container">
+
+		<div v-if="modeTag == 'list'">
+			<!-- 查询表单 -->
+			<a-form :model="queryParam" layout="inline" class="search-form">
+				<a-form-item label="题目ID:">
+					<a-input v-model:value="queryParam.id" placeholder="请输入题目ID" allow-clear />
+				</a-form-item>
+
+				<!-- <a-form-item label="年级:">
+					<a-select
+						v-model:value="queryParam.level"
+						placeholder="请选择年级"
+						@change="levelChange"
+						allow-clear
+						style="width: 120px"
+					>
+						<a-select-option v-for="item in levelEnum" :key="item.key" :value="item.key">
+							{{ item.value }}
+						</a-select-option>
+					</a-select>
+				</a-form-item>
+				<a-form-item label="学科:">
+					<a-select v-model:value="queryParam.subjectId" placeholder="请选择学科" allow-clear style="width: 200px">
+						<a-select-option v-for="item in subjectFilter" :key="item.id" :value="item.id">
+							{{ item.name }} ( {{ item.levelName }} )
+						</a-select-option>
+					</a-select>
+				</a-form-item> -->
+				<a-form-item>
+					<a-button type="primary" @click="submitForm">查询</a-button>
+					<!--				<a-button type="primary" @click="openDrawer('add')" style="margin-left: 8px">添加</a-button>-->
+				</a-form-item>
+			</a-form>
+
+			<!-- 数据表格 -->
+			<a-table
+				:loading="listLoading"
+				:data-source="tableData"
+				:columns="columns"
+				:pagination="false"
+				row-key="id"
+				class="data-table"
+				:locale="{ emptyText: '暂无数据' }"
+			>
+				<template #bodyCell="{ column, record }">
+					<template v-if="column.key === 'action'">
+						<a-button style="margin-right: 10px" type="primary" @click="handleOk(record)">选择
+						</a-button>
+						<!--					<a-button size="small" @click="openDrawer('edit', record.id)">编辑</a-button>-->
+						<!--					<a-button size="small" type="primary" danger @click="deletePaper(record)" style="margin-left: 8px">-->
+						<!--						删除-->
+						<!--					</a-button>-->
+					</template>
+				</template>
+			</a-table>
+
+			<!-- 分页 -->
+			<a-pagination
+				v-if="total > 0"
+				:current="queryParam.pageIndex"
+				:page-size="queryParam.pageSize"
+				:total="total"
+				:show-size-changer="true"
+				:show-quick-jumper="true"
+				:show-total="(total, range) => `第 ${range[0]}-${range[1]} 条/共 ${total} 条`"
+				@change="handlePageChange"
+				@show-size-change="handlePageSizeChange"
+				class="pagination"
+			/>
+
+			<!-- 编辑/添加 抽屉 -->
+			<a-drawer
+				:visible="drawerVisible"
+				:title="drawerTitle"
+				placement="right"
+				width="900"
+				@close="closeDrawer"
+				destroyOnClose
+			>
+				<!--			<FormEdit v-if="drawerVisible" :id="editId" @success="onEditSuccess" />-->
+			</a-drawer>
+		</div>
+
+		<div v-if="modeTag == 'item'">
+			<a-table
+				:loading="listLoading"
+				:data-source="itemDatas"
+				:columns="columns"
+				:pagination="false"
+				row-key="id"
+				class="data-table"
+				:locale="{ emptyText: '暂无数据' }"
+			>
+				<template #bodyCell="{ column, record }">
+					<template v-if="column.key === 'action'">
+						<a-button style="margin-right: 10px" type="primary" @click="handleReset(record)">重新选择
+						</a-button>
+						<!--					<a-button size="small" @click="openDrawer('edit', record.id)">编辑</a-button>-->
+						<!--					<a-button size="small" type="primary" danger @click="deletePaper(record)" style="margin-left: 8px">-->
+						<!--						删除-->
+						<!--					</a-button>-->
+					</template>
+				</template>
+			</a-table>
+		</div>
+
+	</div>
+</template>
+
+<script setup>
+	import { ref, reactive, computed, onMounted } from 'vue'
+	import { message, Modal } from 'ant-design-vue'
+	import { useExamStore } from '@/store/exam'
+	import examPaperApi from '@/api/exam/paper/examPaperApi'
+	// import FormEdit from './form.vue'
+	import { parseTime } from '@/utils/exam'
+	const emit = defineEmits(['handlerExs'])
+	const examStore = useExamStore()
+
+	const modeTag = ref('list')
+	const itemDatas = ref([])
+	// 响应式数据
+	const queryParam = reactive({
+		id: null,
+		// level: null,
+		// subjectId: null,
+		current: 1,
+		size: 10
+	})
+
+	const subjectFilter = ref([])
+	const listLoading = ref(false)
+	const tableData = ref([])
+	const total = ref(0)
+
+	// Drawer 控制
+	const drawerVisible = ref(false)
+	const drawerTitle = ref('')
+	const editId = ref(null)
+
+	function openDrawer(type, id = null) {
+		if (type === 'add') {
+			drawerTitle.value = '添加试卷'
+			editId.value = null
+		} else {
+			drawerTitle.value = '编辑试卷'
+			editId.value = id
+		}
+		drawerVisible.value = true
+	}
+	function closeDrawer() {
+		drawerVisible.value = false
+	}
+	function onEditSuccess() {
+		closeDrawer()
+		search()
+	}
+
+	// 表格列配置
+	const columns = [
+		{
+			title: 'Id',
+			dataIndex: 'id',
+			key: 'id',
+			width: 90
+		},
+		{
+			title: '学科',
+			dataIndex: 'subjectId',
+			key: 'subjectId',
+			width: 200,
+			customRender: ({ text }) => examStore.subjectEnumFormat(text)
+		},
+		{
+			title: '名称',
+			dataIndex: 'name',
+			key: 'name'
+		},
+		{
+			title: '创建时间',
+			dataIndex: 'createTime',
+			key: 'createTime',
+			width: 200,
+			customRender: ({ text }) => parseTime(text, '{y}-{m}-{d} {h}:{i}:{s}')
+		},
+		{
+			title: '操作',
+			key: 'action',
+			width: 160,
+			align: 'center'
+		}
+	]
+	// 计算属性
+	const levelEnum = computed(() => examStore.getLevelEnum)
+
+	// 方法
+	const submitForm = () => {
+		queryParam.pageIndex = 1
+		search()
+	}
+
+	const search = async () => {
+		listLoading.value = true
+		try {
+			const response = await examPaperApi.pageList(queryParam)
+			if (response) {
+				const data = response
+				tableData.value = data.records || []
+				total.value = data.total || 0
+				queryParam.pageIndex = data.current || 1
+				listLoading.value = false
+			} else {
+				message.error(response.message || '获取数据失败')
+			}
+		} catch (error) {
+			console.error('获取试卷列表失败:', error)
+			message.error('获取数据失败')
+		} finally {
+			listLoading.value = false
+		}
+	}
+
+	const deletePaper = async (row) => {
+		try {
+			// 显示确认对话框
+			const confirmed = await new Promise((resolve) => {
+				Modal.confirm({
+					title: '确认删除',
+					content: `确定要删除试卷"${row.name}"吗?`,
+					okText: '确定',
+					cancelText: '取消',
+					onOk: () => resolve(true),
+					onCancel: () => resolve(false)
+				})
+			})
+
+			if (!confirmed) return
+
+			await examPaperApi.deletePaper(row.id)
+			search()
+		} catch (error) {
+			console.error('删除试卷失败:', error)
+			message.error('删除失败')
+		}
+	}
+
+	const levelChange = () => {
+		queryParam.subjectId = null
+		subjectFilter.value = examStore.subjects.filter((data) => data.level === queryParam.level)
+	}
+
+	const handlePageChange = (page, pageSize) => {
+		queryParam.pageIndex = page
+		queryParam.pageSize = pageSize
+		search()
+	}
+
+	const handlePageSizeChange = (current, size) => {
+		queryParam.pageIndex = 1
+		queryParam.pageSize = size
+		search()
+	}
+	const handleOk = (item) => {
+		console.log("选取了",item)
+		modeTag.value = 'item'
+		itemDatas.value = []
+		itemDatas.value.push(item)
+
+		emit('handlerExs', [item])
+	}
+	const handleReset = (item) => {
+		console.log("选取了",item)
+		modeTag.value = 'list'
+		itemDatas.value = []
+		queryParam.pageIndex = 1
+		search()
+		emit('handlerExs',null)
+	}
+	const getItemData = () => {
+		return 	itemDatas.value
+	}
+	const edit = async (id) => {
+		modeTag.value = 'item'
+		listLoading.value = true
+		try {
+			const response = await examPaperApi.pageList({id :id,current : 1 ,size:10 })
+			if (response) {
+				const data = response
+				itemDatas.value = data.records || []
+				listLoading.value = false
+			} else {
+				message.error(response.message || '获取数据失败')
+			}
+		} catch (error) {
+			console.error('获取试卷列表失败:', error)
+			message.error('获取数据失败')
+		} finally {
+			listLoading.value = false
+		}
+	}
+	// 生命周期
+	onMounted(async () => {
+		// examStore.initSubject(search)
+	})
+	const open=() =>{
+		examStore.initSubject(search)
+	}
+	defineExpose({getItemData,edit,open})
+</script>
+
+<style lang="less" scoped>
+	.app-container {
+		padding: 24px;
+		background: #fff;
+
+		.search-form {
+			margin-bottom: 24px;
+			padding: 24px;
+			background: #fafafa;
+			border-radius: 6px;
+		}
+
+		.data-table {
+			margin-bottom: 24px;
+		}
+
+		.pagination {
+			text-align: right;
+		}
+	}
+</style>

+ 110 - 0
src/views/courseAdd/components/courseProduction/fileName.vue

@@ -0,0 +1,110 @@
+<template>
+	<span  style="margin-top: 12px;  margin-bottom: 12px">{{ formRef.name }}</span>
+</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 UpLoadImg from '@/components/UpLoadImg/index.vue'
+import UpLoadDoc from '@/components/UpLoadDoc/index.vue'
+import UpLoadSrt from '@/components/UpLoadSrt/index.vue'
+import {add, detail} from '@/api/hour/index'
+
+const props = defineProps({
+	modelValue: [String, Number, Boolean, Object, Array, null]
+})
+const emit = defineEmits(['update:visible','update:modelValue', 'ok', 'handlerUpSelect', 'handlerNewSelect'])
+
+const formRef = ref({
+	id : '',
+	name : ''
+})
+const setData = (data) => {
+	console.log("最里面的参数",data)
+	formRef.value.id = data.id
+	formRef.value.name = data.name
+	console.log("最里面的参数更新外面",formRef.value)
+	emit('update:modelValue',data.id)
+}
+
+
+defineExpose({
+	setData
+})
+
+</script>
+
+<style lang="less" scoped>
+.add-class-hours-modal {
+	.ant-modal-content {
+		border-radius: 10px;
+	}
+
+	.ant-modal-header {
+		border-radius: 10px 10px 0 0;
+	}
+
+	.ant-form-item {
+		margin-bottom: 24px;
+	}
+
+	.video-select-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;
+				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;
+		}
+	}
+
+	.upload-tip {
+		color: #888;
+		font-size: 13px;
+		margin-left: 12px;
+	}
+
+	.footer-btns {
+		display: flex;
+		justify-content: flex-end;
+		gap: 16px;
+		margin-top: 24px;
+	}
+}
+</style>

+ 10 - 1
src/views/courseAdd/components/courseProduction/index.vue

@@ -37,7 +37,7 @@
 				<div style="display: flex; flex-direction: column; justify-content: space-between; height: 100%  ">
 					<div>{{ lesson.name }}</div>
 					<div>
-						<span>视频大小:{{ lesson.size }}MB</span>
+<!--						<span>视频大小:{{ lesson.size }}MB</span>-->
 						<span>发布时间:{{ lesson.createTime }}</span>
 						<span>发布人:{{ lesson.createUserName }}</span>
 					</div>
@@ -68,6 +68,8 @@ import {ref, reactive, onMounted} from 'vue'
 import addDialog from './addDialog.vue'
 import courseProductionApi from '@/api/courseCenter/courseProduction.js'
 import {useRoute, useRouter} from 'vue-router'
+import { del ,  edit as editApi } from '@/api/hour/index'
+
 
 const router = useRouter()
 const route = useRoute()
@@ -92,6 +94,13 @@ const closePopover = () => {
 const handleEdit = (item) => {
 	addDialogRef.value.edit(item)
 }
+const handleDel = (item) => {
+	console.log('删除',item)
+
+	del([{id : item.id}]).then(()=>{
+		getList()
+	})
+}
 
 // 模态框显示状态
 const modalVisible = ref(false)

+ 31 - 15
src/views/courseAdd/components/courseProduction/resourceUpload.vue

@@ -128,7 +128,7 @@
 		<template v-if="isState == 0">
 			<!-- 资源上传 -->
 			<!-- <UploadModal @success="uploadSuccess"></UploadModal> -->
-			<UpLoadBreakPoint uploadCount="1" @onSuccess="onSuccess"></UpLoadBreakPoint>
+			<UpLoadBreakPoint ref="UpLoadBreakPointRef" uploadCount="1" @onSuccess="onSuccess"></UpLoadBreakPoint>
 		</template>
 	</a-modal>
 </template>
@@ -142,7 +142,7 @@ import UploadModal from './UploadModal.vue'
 import coverUpload from './coverUpload/index.vue'
 import UpLoadBreakPoint from '@/components/UpLoadBreakPoint/index.vue'
 import {useMyResourceStore} from '@/store/myResource'
-
+import { message } from 'ant-design-vue'
 const myResourceStore = useMyResourceStore()
 const {proxy} = getCurrentInstance()
 const props = defineProps({
@@ -162,7 +162,7 @@ const props = defineProps({
 		default: null
 	}
 })
-const emit = defineEmits(['close', 'getList'])
+const emit = defineEmits(['close', 'getList','onSub'])
 import tool from '@/utils/tool'
 
 const headers = ref({
@@ -191,6 +191,8 @@ const formState = reactive({
 	isRecommend: 0, // 资源是否推荐
 	isHot: 0 // 资源是否热门
 })
+
+
 const coverImagePath = ref() // 预览回显
 const formRef = ref() // 添加表单引用
 const collegeMajorOptions = ref([]) //院系
@@ -205,7 +207,8 @@ const userRelateIdss = ref([])
 const uploadModalVisible = ref(false)
 // 用户选择模态框
 const userReleaseVisible = ref(false)
-
+const UpLoadBreakPointRef = ref(false)
+const files = ref({})
 // 文件列表
 const fileList = ref([])
 
@@ -217,6 +220,10 @@ const handleUploadCancel = () => {
 
 const open = () => {
 	uploadModalVisible.value = true
+
+	nextTick(() => {
+		UpLoadBreakPointRef.value.open()
+	})
 }
 const close = () => {
 	uploadModalVisible.value = false
@@ -244,10 +251,12 @@ const onSuccess = (uploadFileList) => {
 	for (let i = 0; i < uploadFileList.length; i++) {
 		if (uploadFileList[i].userFileId) {
 			list.push(uploadFileList[i].userFileId)
+			files.value = {id : uploadFileList[i].userFileId , fileName : uploadFileList[i].name}
 		}
 	}
 	console.log('formState.userfileIds是:', list)
 	formState.userfileIds = list.join(',')
+
 }
 
 // 自定义校验函数示例
@@ -431,7 +440,7 @@ const handleUploadOk = async () => {
 		await formRef.value.validate()
 		console.log('formState.userfileIds是:提交了', formState.userfileIds)
 		if (!formState.userfileIds) {
-			Modal.error({content: '请先上传文件!'})
+			message.error({content: '请先上传文件!'})
 			return
 		}
 		if (props.isState == 1) {
@@ -457,10 +466,10 @@ const handleUploadOk = async () => {
 				.edit(formData)
 				.then((res) => {
 					emit('getList')
-					Modal.success({content: '资源编辑成功'})
+					message.success({content: '资源编辑成功'})
 				})
 				.catch((err) => {
-					Modal.success({content: '资源编辑失败'})
+					message.success({content: '资源编辑失败'})
 					console.log(err)
 				})
 		} else {
@@ -486,20 +495,27 @@ const handleUploadOk = async () => {
 				.add(formData)
 				.then((res) => {
 					emit('getList')
-					Modal.success({content: '资源上传成功'})
+
+					files.value = {
+						fileName: files.value.fileName,
+						id: res.data.addIdListStr
+					}
+					emit("onSub",files.value)
+					message.success({content: '资源上传成功'})
+					uploadModalVisible.value = false
 				})
 				.catch((err) => {
-					Modal.success({content: '资源上传失败'})
+					message.success({content: '资源上传失败'})
 					console.log(err)
 				})
 		}
 	} catch (error) {
 		if (error.errorFields) {
 			// 表单验证错误
-			Modal.error({content: '请检查表单填写是否正确'})
+			message.error({content: '请检查表单填写是否正确'})
 		} else {
 			// API错误
-			Modal.error({content: '资源上传失败'})
+			message.error({content: '资源上传失败'})
 			console.error(error)
 		}
 	}
@@ -551,7 +567,7 @@ const getformState = () => {
 const beforeUpload = (file) => {
 	const isLt2G = file.size / 1024 / 1024 / 1024 < 2
 	if (!isLt2G) {
-		Modal.error({content: '文件大小不能超过 2GB!'})
+		message.error({content: '文件大小不能超过 2GB!'})
 	}
 	return isLt2G
 }
@@ -572,7 +588,7 @@ const handleRemove = (file) => {
 const handleChange = ({file, fileList: newFileList}) => {
 	if (newFileList.length > 1) {
 		fileList.value = [newFileList[0]] // 只保留最新上传的文件
-		Modal.error({content: '只能上传一个文件!'})
+		message.error({content: '只能上传一个文件!'})
 		return
 	}
 
@@ -590,9 +606,9 @@ const handleChange = ({file, fileList: newFileList}) => {
 	}
 	if (file.response?.code == 200) {
 		file.percent = 100
-		Modal.success({content: '文件上传成功'})
+		message.success({content: '文件上传成功'})
 	} else if (file.response?.code == 500) {
-		Modal.error({content: '文件上传失败'})
+		message.error({content: '文件上传失败'})
 		file.percent = 0
 	}
 }

+ 6 - 6
src/views/courseAdd/index.vue

@@ -14,12 +14,12 @@
 					<a-tab-pane key="3" tab="学员管理" :disabled="courseInfoId==null">
 						<StudentDetails :courseInfoId="courseInfoId"></StudentDetails>
 					</a-tab-pane>
-					<a-tab-pane key="4" tab="作业布置" :disabled="courseInfoId==null">
-						<div>这里是作业布置的内容</div>
-					</a-tab-pane>
-					<a-tab-pane key="5" tab="测试布置" :disabled="courseInfoId==null">
-						<div>这里是测试布置的内容</div>
-					</a-tab-pane>
+<!--					<a-tab-pane key="4" tab="作业布置" :disabled="courseInfoId==null">-->
+<!--						<div>这里是作业布置的内容</div>-->
+<!--					</a-tab-pane>-->
+<!--					<a-tab-pane key="5" tab="测试布置" :disabled="courseInfoId==null">-->
+<!--						<div>这里是测试布置的内容</div>-->
+<!--					</a-tab-pane>-->
 				</a-tabs>
 			</div>
 		</a-layout>

+ 160 - 143
src/views/courseManagement/components/ListView.vue

@@ -2,8 +2,8 @@
 	<a-table
 		ref="table"
 		:columns="columns"
-		:data-source="dataSource"
-		:row-key="(record) => record.collegeId"
+		:data-source="dataSources"
+		:row-key="(record) => record.courseId"
 		bordered
 		:expand-row-by-click="true"
 		:pagination="false"
@@ -23,7 +23,7 @@
 		<a-pagination
 			v-model:current="pagination.current"
 			v-model:pageSize="pagination.size"
-			:total="pagination.total"
+			:total="total"
 			show-less-items
 			@change="handlerChange"
 		/>
@@ -31,160 +31,177 @@
 </template>
 
 <script setup>
-	import tool from '@/utils/tool'
-	import { ref, onMounted } from 'vue'
-	import { EyeOutlined, EditOutlined, SnippetsOutlined, DeleteOutlined } from '@ant-design/icons-vue'
-	import { list } from '@/api/courseinfo'
-	import { useRouter } from 'vue-router'
-	import collegeApi from '@/api/college'
+import tool from '@/utils/tool'
+import {ref, onMounted} from 'vue'
+import {EyeOutlined, EditOutlined, SnippetsOutlined, DeleteOutlined} from '@ant-design/icons-vue'
+import {list} from '@/api/courseinfo'
+import {useRouter} from 'vue-router'
+import collegeApi from '@/api/college'
 
-	const router = useRouter()
+const router = useRouter()
 
-	const emit = defineEmits(['handleEdit'])
-	//发布按钮状态
-	const releaseVisible = ref(false)
-	const loading = ref(false) // 列表loading
-	const dataSource = ref([])
-	const formState = ref({
-		name: '',
-		loacl: ''
-	}) // 列表loading
-	const columns = [
-		{
-			title: '课程名称',
-			dataIndex: 'courseName',
-			sorter: true,
-			width: '20%'
-		},
-		{
-			title: '状态',
-			dataIndex: 'courseType',
-			sorter: true,
-			width: '10%'
-		},
-		{
-			title: '课程类型',
-			dataIndex: 'isCreaterName',
-			sorter: true,
-			width: '20%'
-		},
-		{
-			title: '课时数量',
-			dataIndex: 'majorId',
-			sorter: true,
-			width: '10%'
-		},
-		{
-			title: '更新时间',
-			dataIndex: 'publishTime',
-			sorter: true,
-			width: '20%'
-		},
-		{
-			title: '操作',
-			dataIndex: 'action',
-			sorter: true,
-			width: '20%'
-		}
-	]
-	// tool.formatTimestamp()
-
-	const formatTimestamp = (time) => {
-		return tool.formatTimestamp(time)
+const emit = defineEmits(['handleEdit'])
+//发布按钮状态
+const releaseVisible = ref(false)
+const loading = ref(false) // 列表loading
+const dataSources = ref([])
+const formState = ref({
+	name: '',
+	loacl: ''
+}) // 列表loading
+const columns = [
+	{
+		title: '课程名称',
+		dataIndex: 'courseName',
+		sorter: true,
+		width: '15%'
+	},
+	{
+		title: '状态',
+		dataIndex: 'putawayStatusName',
+		sorter: true,
+		width: '10%'
+	},
+	{
+		title: '院系类型',
+		dataIndex: 'collegeAllIdName',
+		sorter: true,
+		width: '25%'
+	},
+	{
+		title: '课程类型',
+		dataIndex: 'courseTypeName',
+		sorter: true,
+		width: '8%'
+	},
+	{
+		title: '课时数量',
+		dataIndex: 'hourCount',
+		sorter: true,
+		width: '7%'
+	},
+	{
+		title: '发布时间',
+		dataIndex: 'publishTime',
+		sorter: true,
+		width: '12%'
+	},
+	{
+		title: '操作',
+		dataIndex: 'action',
+		sorter: true,
+		width: '20%'
 	}
-	const pagination = reactive({
-		size: 10,
-		current: 1,
-		total: 0
+]
+// tool.formatTimestamp()
+
+const formatTimestamp = (time) => {
+	return tool.formatTimestamp(time)
+}
+const total = ref(0)
+const pagination = ref({
+	size: 10,
+	current: 1,
+})
+const onChangeCurrent = (current) => {
+	router.push({
+		path: '/' + current
 	})
-	const onChangeCurrent = (current) => {
-		router.push({
-			path: '/' + current
-		})
-	}
-	const handlerChange = (page, pageSize) => {
-		pagination.size = pageSize
-		pagination.current = page
+}
+const handlerChange = (page, pageSize) => {
+	console.log('分页参数', page, pageSize)
+	// pagination.value.size = pageSize
+	// pagination.value.current = page
 
-		getList()
-	}
-	const publishedData = ref()
-	//发布确定
+	getList()
+}
+const handleDetail = (record) => {
+	console.log('查看详情', record)
+	router.push({
+		path: '/portal/courseDetails',
+		query: {
+			id: record.courseId
+		}
+	})
+	// 在这里添加查看详情的逻辑
+}
 
-	// 上传资源模态框
-	const uploadModalVisible = ref(false)
-	// 详情按钮点击事件
-	const handleDetail = (record) => {
-		console.log('查看详情', record)
-		router.push({
-			path: '/portal/courseDetails',
-			query: {
-				id: record.courseId
-			}
-		})
-		// 在这里添加查看详情的逻辑
-	}
+// 编辑按钮点击事件
+const handleEdit = (record) => {
+	console.log('编辑记录', record)
+	// 在这里添加编辑记录的逻辑
 
-	// 编辑按钮点击事件
-	const handleEdit = (record) => {
-		console.log('编辑记录', record)
-		// 在这里添加编辑记录的逻辑
+	emit('handleEdit', record)
+}
 
-		emit('handleEdit', record)
-	}
+// 上架按钮点击事件
+const handleShelf = (record) => {
+	console.log('上架记录', record)
+	// 在这里添加上架记录的逻辑
+}
 
-	// 上架按钮点击事件
-	const handleShelf = (record) => {
-		console.log('上架记录', record)
-		// 在这里添加上架记录的逻辑
-	}
+// 删除按钮点击事件
+const handleDelete = (record) => {
+	console.log('删除记录', record)
+	// 在这里添加删除记录的逻辑
+}
+const getList = () => {
+	console.log('获取列表 getList')
 
-	// 删除按钮点击事件
-	const handleDelete = (record) => {
-		console.log('删除记录', record)
-		// 在这里添加删除记录的逻辑
-	}
-	const getList = () => {
-		console.log('获取列表', list)
+	list({...pagination.value}).then((data) => {
+		if (data.code == 200) {
+			console.log('获取列表 新数组', data.data.records)
+			dataSources.value = []
+			dataSources.value = data.data.records
+			console.log('获取列表 最新数组', dataSources.value)
+			pagination.value.current = data.data.current
+			pagination.value.size = data.data.size
+			total.value = data.data.total
+		}
+		// data.records
+	})
+}
+const setList = (search) => {
+	console.log('获取列表 setList',search)
+	// courseName: '',
+	// 	collegeId: '',
+	// 	majorId: '',
+	// 	courseType: '',
+	// 	loacl: []
+	formState.value = search
+	pagination.value.current = 1
+	list({...pagination.value, ...formState.value}).then((data) => {
+		if (data.code == 200) {
+			dataSources.value = data.data.records
+			pagination.value.current = data.data.current
+			pagination.value.size = data.data.size
+			total.value = data.data.total
+		}
+		// data.records
+	})
+}
 
-		list({ ...pagination }).then((data) => {
-			if (data.code == 200) {
-				dataSource.value = data.data.records
-				pagination.current = data.data.current
-				pagination.size = data.data.size
-				pagination.total = data.data.total
-			}
-			// data.records
-		})
-	}
-	const setList = (search) => {
-		console.log('获取列表', list)
-		formState.value = search
-		pagination.current = 1
-		list({ ...pagination, ...search }).then((data) => {
-			if (data.code == 200) {
-				dataSource.value = data.data.records
-				pagination.current = data.data.current
-				pagination.size = data.data.size
-				pagination.total = data.data.total
-			}
-			// data.records
-		})
-	}
+// 重置按钮点击事件
+onMounted(() => {
+	// getListData()
+	getList()
+})
 
-	// 重置按钮点击事件
-	onMounted(() => {
-		// getListData()
-		getList()
-	})
 
-	defineExpose({
-		setList
-	})
+// watch(
+// 	() => dataSources.value,
+// 	(newVal, oldVal) => {
+// 		console.log('数据源变化了 ', ' 新的 ',newVal, '  旧的 ',oldVal)
+// 	},
+// 	{ deep: true, immediate: true }
+// )
+defineExpose({
+	setList
+})
 </script>
 
 <style scoped>
-	.desc p {
-		margin-bottom: 1em;
-	}
+.desc p {
+	margin-bottom: 1em;
+}
 </style>

+ 47 - 15
src/views/courseManagement/components/QueryView.vue

@@ -16,10 +16,18 @@
 					/>
 				</a-form-item>
 				<a-form-item label="" style="width: 200px">
-					<a-input v-model:value="formState.type" placeholder="选择课程类型" allowClear />
+<!--					<a-input v-model:value="formState.type" placeholder="选择课程类型" allowClear />-->
+					<a-select
+						ref="select"
+						placeholder="选择课程类型"
+						v-model:value="formState.courseType"
+						:fieldNames="{ label: 'dictLabel', value: 'dictValue' }"
+						:options="COURSE_TYPE"
+						allowClear
+					></a-select>
 				</a-form-item>
 				<a-form-item label="" style="width: 300px">
-					<a-range-picker allowClear />
+					<a-range-picker 	v-model:value="formState.time" allowClear />
 				</a-form-item>
 			</a-form>
 		</div>
@@ -39,21 +47,21 @@
 <script setup>
 	import { ref, onMounted } from 'vue'
 	import { SearchOutlined, ReloadOutlined } from '@ant-design/icons-vue'
-	import tool from '@/utils/tool'
 	import { useRouter } from 'vue-router'
 	import collegeApi from '@/api/college'
-
+	import tool from '@/utils/tool'
 	const emit = defineEmits([])
 	const router = useRouter()
 	//发布按钮状态
 	const releaseVisible = ref(false)
 	const loading = ref(false) // 列表loading
-
+	const COURSE_TYPE = tool.dictTypeList('COURSE_TYPE')
 	const formState = ref({
-		courseName: '',
-		collegeId: '',
-		majorId: '',
-		courseType: '',
+		courseName: undefined,
+		collegeId: undefined,
+		majorId: undefined,
+		courseType: undefined,
+		time : [],
 		loacl: []
 	}) // 列表loading
 
@@ -115,19 +123,43 @@
 		})
 	}
 	const handleSearch = () => {
-		console.log('执行查询操作', formState.value)
+		console.log('执行查询操作', formState.value,COURSE_TYPE)
 		// 在这里添加查询逻辑
 
-		emit('handlerSearch', formState.value)
+		let newJson =  JSON.parse(JSON.stringify(formState.value))
+		console.log('执行查询操作123   ', newJson.loacl.length)
+		for (let i = 0; i < newJson.loacl.length; i++) {
+			let item = newJson.loacl[i]
+			if(i == 0){
+				newJson.collegeId = item
+			}
+			if(i == 1){
+				newJson.collegeTwoId = item
+			}
+			if(i == 2){
+				newJson.collegeThreeId = item
+			}
+		}
+		newJson.loacl = undefined
+		if(newJson.time.length == 2){
+		 let beginTime =	tool.formatTimesYearMonthDay(newJson.time[0])
+		let endTime=	tool.formatTimesYearMonthDay(newJson.time[1])
+			newJson.beginTime = beginTime
+			newJson.endTime = endTime
+			newJson.time= undefined
+		}
+
+		emit('handlerSearch', newJson)
 	}
 
 	// 重置按钮点击事件
 	const handleReset = () => {
 		formState.value = {
-			courseName: '',
-			collegeId: '',
-			majorId: '',
-			courseType: '',
+			courseName: undefined,
+			collegeId: undefined,
+			majorId: undefined,
+			courseType: undefined,
+			time : [],
 			loacl: []
 			// 其他需要重置的字段
 		}

+ 28 - 4
src/views/forum/form.vue

@@ -21,7 +21,7 @@
 			<a-row :gutter="16">
 				<a-col :span="12">
 					<a-form-item label="标题:" name="postTitle">
-						<a-input v-model:value="formData.postTitle" placeholder="请输入显示名称" allow-clear />
+						<a-input v-model:value="formData.postTitle" placeholder="请输入标题" allow-clear />
 					</a-form-item>
 				</a-col>
 				<a-col :span="12">
@@ -29,16 +29,29 @@
 						<a-select
 							v-model:value="formData.typeId"
 							show-search
-							placeholder="所有分类"
+							placeholder="请选择分类"
 							style="width: 200px"
 							:options="typeOptions"
 							:filter-option="filterOption"
 						></a-select>
 					</a-form-item>
 				</a-col>
+				<a-col :span="12">
+					<a-form-item label="指向:" name="appointUserArr">
+						<a-select
+							v-model:value="formData.appointUserArr"
+							mode="multiple"
+							show-search
+							placeholder="请选择"
+							style="width: 200px"
+							:options="usertypeOptions"
+							:filter-option="filterOption"
+						></a-select>
+					</a-form-item>
+				</a-col>
 				<a-col :span="24">
 					<a-form-item label="内容:" name="postContent">
-						<xn-editor v-model="formData.postContent" placeholder="请输入" :height="400"></xn-editor>
+						<xn-editor v-model="formData.postContent" placeholder="请输入内容" :height="400"></xn-editor>
 					</a-form-item>
 				</a-col>
 			</a-row>
@@ -72,7 +85,7 @@
 	const submitLoading = ref(false)
 	// 模块ID
 	const moduleId = ref('')
-
+	const usertypeOptions = ref([])
 	const filterOption = (input, option) => {
 		return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
 	}
@@ -90,7 +103,17 @@
 				}
 			})
 		})
+		forumApi.allUserList().then((data)=>{
+			usertypeOptions.value = data.map(r=>{
+				return{
+					label:r.name,
+					value:r.id,
+					...r
+				}
+			})
+		})
 		if (module) {
+			record.appointUserArr = record.appointUser.split(',')
 			formData.value = Object.assign(formData.value, record)
 		}
 	}
@@ -115,6 +138,7 @@
 			.validate()
 			.then(() => {
 				submitLoading.value = true
+				formData.value.appointUser = formData.value.appointUserArr.join(',')
 				forumApi.submitForm(formData.value, formData.value.postId).then(() => {
 					onClose()
 					emit('successful')

+ 1 - 1
src/views/forum/index.vue

@@ -163,7 +163,7 @@
 	}
 	getTypeList()
 
-	const typeValueEx = ref('')
+	const typeValueEx = ref('请选择')
 	const exType = ref(false)
 	const typeOptionsEx = ref([
 		{

+ 103 - 0
src/views/forum/postinfo/form.vue

@@ -0,0 +1,103 @@
+<template>
+    <xn-form-container
+        :title="formData.postId ? '编辑帖子信息表' : '增加帖子信息表'"
+        :width="700"
+        :visible="visible"
+        :destroy-on-close="true"
+        @close="onClose"
+    >
+        <a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
+            <a-form-item label="分类id:" name="typeId">
+                <a-input v-model:value="formData.typeId" placeholder="请输入分类id" allow-clear />
+            </a-form-item>
+            <a-form-item label="用户id:" name="userId">
+                <a-input v-model:value="formData.userId" placeholder="请输入用户id" allow-clear />
+            </a-form-item>
+            <a-form-item label="帖子标题:" name="postTitle">
+                <a-input v-model:value="formData.postTitle" placeholder="请输入帖子标题" allow-clear />
+            </a-form-item>
+            <a-form-item label="帖子内容:" name="postContent">
+                <a-input v-model:value="formData.postContent" placeholder="请输入帖子内容" allow-clear />
+            </a-form-item>
+            <a-form-item label="是否置顶 0普通 1置顶:" name="isTop">
+                <a-input v-model:value="formData.isTop" placeholder="请输入是否置顶 0普通 1置顶" allow-clear />
+            </a-form-item>
+            <a-form-item label="浏览次数:" name="viewCount">
+                <a-input v-model:value="formData.viewCount" placeholder="请输入浏览次数" allow-clear />
+            </a-form-item>
+            <a-form-item label="回复次数:" name="replyCount">
+                <a-input v-model:value="formData.replyCount" placeholder="请输入回复次数" allow-clear />
+            </a-form-item>
+            <a-form-item label="点赞次数:" name="likeCount">
+                <a-input v-model:value="formData.likeCount" placeholder="请输入点赞次数" allow-clear />
+            </a-form-item>
+            <a-form-item label="最后回复用户id:" name="lastReplyUserId">
+                <a-input v-model:value="formData.lastReplyUserId" placeholder="请输入最后回复用户id" allow-clear />
+            </a-form-item>
+            <a-form-item label="最后回复时间:" name="lastReplyTime">
+                <a-date-picker v-model:value="formData.lastReplyTime" value-format="YYYY-MM-DD HH:mm:ss" show-time placeholder="请选择最后回复时间" style="width: 100%" />
+            </a-form-item>
+            <a-form-item label="帖子类型 0普通帖子 1技术支持 2内容纠错:" name="postType">
+                <a-input v-model:value="formData.postType" placeholder="请输入帖子类型 0普通帖子 1技术支持 2内容纠错" allow-clear />
+            </a-form-item>
+            <a-form-item label="定向用户:" name="appointUser">
+                <a-input v-model:value="formData.appointUser" placeholder="请输入定向用户" allow-clear />
+            </a-form-item>
+        </a-form>
+        <template #footer>
+            <a-button style="margin-right: 8px" @click="onClose">关闭</a-button>
+            <a-button type="primary" @click="onSubmit" :loading="submitLoading">保存</a-button>
+        </template>
+    </xn-form-container>
+</template>
+
+<script setup name="forumPostInfoForm">
+    import { cloneDeep } from 'lodash-es'
+    import { required } from '@/utils/formRules'
+    import forumPostInfoApi from '@/api/forum/forumPostInfoApi'
+    // 抽屉状态
+    const visible = ref(false)
+    const emit = defineEmits({ successful: null })
+    const formRef = ref()
+    // 表单数据
+    const formData = ref({})
+    const submitLoading = ref(false)
+
+    // 打开抽屉
+    const onOpen = (record) => {
+        visible.value = true
+        if (record) {
+            let recordData = cloneDeep(record)
+            formData.value = Object.assign({}, recordData)
+        }
+    }
+    // 关闭抽屉
+    const onClose = () => {
+        formRef.value.resetFields()
+        formData.value = {}
+        visible.value = false
+    }
+    // 默认要校验的
+    const formRules = {
+    }
+    // 验证并提交数据
+    const onSubmit = () => {
+        formRef.value.validate().then(() => {
+            submitLoading.value = true
+            const formDataParam = cloneDeep(formData.value)
+            forumPostInfoApi
+                .forumPostInfoSubmitForm(formDataParam, formDataParam.postId)
+                .then(() => {
+                    onClose()
+                    emit('successful')
+                })
+                .finally(() => {
+                    submitLoading.value = false
+                })
+        })
+    }
+    // 抛出函数
+    defineExpose({
+        onOpen
+    })
+</script>

+ 150 - 0
src/views/forum/postinfo/index.vue

@@ -0,0 +1,150 @@
+<template>
+    <a-card :bordered="false">
+        <s-table
+            ref="table"
+            :columns="columns"
+            :data="loadData"
+            :alert="options.alert.show"
+            bordered
+            :row-key="(record) => record.postId"
+            :tool-config="toolConfig"
+            :row-selection="options.rowSelection"
+        >
+            <template #operator class="table-operator">
+                <a-space>
+                    <a-button type="primary" @click="formRef.onOpen()" v-if="hasPerm('forumPostInfoAdd')">
+                        <template #icon><plus-outlined /></template>
+                        新增
+                    </a-button>
+                    <xn-batch-delete
+                        v-if="hasPerm('forumPostInfoBatchDelete')"
+                        :selectedRowKeys="selectedRowKeys"
+                        @batchDelete="deleteBatchForumPostInfo"
+                    />
+                </a-space>
+            </template>
+            <template #bodyCell="{ column, record }">
+                <template v-if="column.dataIndex === 'action'">
+                    <a-space>
+                        <a @click="formRef.onOpen(record)" v-if="hasPerm('forumPostInfoEdit')">编辑</a>
+                        <a-divider type="vertical" v-if="hasPerm(['forumPostInfoEdit', 'forumPostInfoDelete'], 'and')" />
+                        <a-popconfirm title="确定要删除吗?" @confirm="deleteForumPostInfo(record)">
+                            <a-button type="link" danger size="small" v-if="hasPerm('forumPostInfoDelete')">删除</a-button>
+                        </a-popconfirm>
+                    </a-space>
+                </template>
+            </template>
+        </s-table>
+    </a-card>
+    <Form ref="formRef" @successful="table.refresh(true)" />
+</template>
+
+<script setup name="postinfo">
+    import Form from './form.vue'
+    import forumPostInfoApi from '@/api/forum/forumPostInfoApi'
+    const table = ref()
+    const formRef = ref()
+    const toolConfig = { refresh: true, height: true, columnSetting: true, striped: false }
+    const columns = [
+        {
+            title: '分类id',
+            dataIndex: 'typeId'
+        },
+        {
+            title: '用户id',
+            dataIndex: 'userId'
+        },
+        {
+            title: '帖子标题',
+            dataIndex: 'postTitle'
+        },
+        {
+            title: '帖子内容',
+            dataIndex: 'postContent'
+        },
+        {
+            title: '是否置顶 0普通 1置顶',
+            dataIndex: 'isTop'
+        },
+        {
+            title: '浏览次数',
+            dataIndex: 'viewCount'
+        },
+        {
+            title: '回复次数',
+            dataIndex: 'replyCount'
+        },
+        {
+            title: '点赞次数',
+            dataIndex: 'likeCount'
+        },
+        {
+            title: '最后回复用户id',
+            dataIndex: 'lastReplyUserId'
+        },
+        {
+            title: '最后回复时间',
+            dataIndex: 'lastReplyTime'
+        },
+        {
+            title: '帖子类型 0普通帖子 1技术支持 2内容纠错',
+            dataIndex: 'postType'
+        },
+        {
+            title: '定向用户',
+            dataIndex: 'appointUser'
+        },
+    ]
+    // 操作栏通过权限判断是否显示
+    if (hasPerm(['forumPostInfoEdit', 'forumPostInfoDelete'])) {
+        columns.push({
+            title: '操作',
+            dataIndex: 'action',
+            align: 'center',
+            width: '150px'
+        })
+    }
+    const selectedRowKeys = ref([])
+    // 列表选择配置
+    const options = {
+        // columns数字类型字段加入 needTotal: true 可以勾选自动算账
+        alert: {
+            show: true,
+            clear: () => {
+                selectedRowKeys.value = ref([])
+            }
+        },
+        rowSelection: {
+            onChange: (selectedRowKey, selectedRows) => {
+                selectedRowKeys.value = selectedRowKey
+            }
+        }
+    }
+    const loadData = (parameter) => {
+        return forumPostInfoApi.forumPostInfoPage(parameter).then((data) => {
+            return data
+        })
+    }
+    // 重置
+    const reset = () => {
+        searchFormRef.value.resetFields()
+        table.value.refresh(true)
+    }
+    // 删除
+    const deleteForumPostInfo = (record) => {
+        let params = [
+            {
+                postId: record.postId
+            }
+        ]
+        forumPostInfoApi.forumPostInfoDelete(params).then(() => {
+            table.value.refresh(true)
+        })
+    }
+    // 批量删除
+    const deleteBatchForumPostInfo = (params) => {
+        forumPostInfoApi.forumPostInfoDelete(params).then(() => {
+            table.value.clearRefreshSelected()
+        })
+    }
+</script>

+ 73 - 0
src/views/forum/posttype/form.vue

@@ -0,0 +1,73 @@
+<template>
+    <xn-form-container
+        :title="formData.typeId ? '编辑帖子分类表' : '增加帖子分类表'"
+        :width="700"
+        :visible="visible"
+        :destroy-on-close="true"
+        @close="onClose"
+    >
+        <a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
+            <a-form-item label="分类名称:" name="typeName">
+                <a-input v-model:value="formData.typeName" placeholder="请输入分类名称" allow-clear />
+            </a-form-item>
+            <a-form-item label="分类描述:" name="typeDesc">
+                <a-input v-model:value="formData.typeDesc" placeholder="请输入分类描述" allow-clear />
+            </a-form-item>
+        </a-form>
+        <template #footer>
+            <a-button style="margin-right: 8px" @click="onClose">关闭</a-button>
+            <a-button type="primary" @click="onSubmit" :loading="submitLoading">保存</a-button>
+        </template>
+    </xn-form-container>
+</template>
+
+<script setup name="forumPostTypeForm">
+    import { cloneDeep } from 'lodash-es'
+    import { required } from '@/utils/formRules'
+    import forumPostTypeApi from '@/api/forum/forumPostTypeApi'
+    // 抽屉状态
+    const visible = ref(false)
+    const emit = defineEmits({ successful: null })
+    const formRef = ref()
+    // 表单数据
+    const formData = ref({})
+    const submitLoading = ref(false)
+
+    // 打开抽屉
+    const onOpen = (record) => {
+        visible.value = true
+        if (record) {
+            let recordData = cloneDeep(record)
+            formData.value = Object.assign({}, recordData)
+        }
+    }
+    // 关闭抽屉
+    const onClose = () => {
+        formRef.value.resetFields()
+        formData.value = {}
+        visible.value = false
+    }
+    // 默认要校验的
+    const formRules = {
+    }
+    // 验证并提交数据
+    const onSubmit = () => {
+        formRef.value.validate().then(() => {
+            submitLoading.value = true
+            const formDataParam = cloneDeep(formData.value)
+            forumPostTypeApi
+                .forumPostTypeSubmitForm(formDataParam, formDataParam.typeId)
+                .then(() => {
+                    onClose()
+                    emit('successful')
+                })
+                .finally(() => {
+                    submitLoading.value = false
+                })
+        })
+    }
+    // 抛出函数
+    defineExpose({
+        onOpen
+    })
+</script>

+ 110 - 0
src/views/forum/posttype/index.vue

@@ -0,0 +1,110 @@
+<template>
+    <a-card :bordered="false">
+        <s-table
+            ref="table"
+            :columns="columns"
+            :data="loadData"
+            :alert="options.alert.show"
+            bordered
+            :row-key="(record) => record.typeId"
+            :tool-config="toolConfig"
+            :row-selection="options.rowSelection"
+        >
+            <template #operator class="table-operator">
+                <a-space>
+                    <a-button type="primary" @click="formRef.onOpen()" v-if="hasPerm('forumPostTypeAdd')">
+                        <template #icon><plus-outlined /></template>
+                        新增
+                    </a-button>
+                    <xn-batch-delete
+                        v-if="hasPerm('forumPostTypeBatchDelete')"
+                        :selectedRowKeys="selectedRowKeys"
+                        @batchDelete="deleteBatchForumPostType"
+                    />
+                </a-space>
+            </template>
+            <template #bodyCell="{ column, record }">
+                <template v-if="column.dataIndex === 'action'">
+                    <a-space>
+                        <a @click="formRef.onOpen(record)" v-if="hasPerm('forumPostTypeEdit')">编辑</a>
+                        <a-divider type="vertical" v-if="hasPerm(['forumPostTypeEdit', 'forumPostTypeDelete'], 'and')" />
+                        <a-popconfirm title="确定要删除吗?" @confirm="deleteForumPostType(record)">
+                            <a-button type="link" danger size="small" v-if="hasPerm('forumPostTypeDelete')">删除</a-button>
+                        </a-popconfirm>
+                    </a-space>
+                </template>
+            </template>
+        </s-table>
+    </a-card>
+    <Form ref="formRef" @successful="table.refresh(true)" />
+</template>
+
+<script setup name="posttype">
+    import Form from './form.vue'
+    import forumPostTypeApi from '@/api/forum/forumPostTypeApi'
+    const table = ref()
+    const formRef = ref()
+    const toolConfig = { refresh: true, height: true, columnSetting: true, striped: false }
+    const columns = [
+        {
+            title: '分类名称',
+            dataIndex: 'typeName'
+        },
+        {
+            title: '分类描述',
+            dataIndex: 'typeDesc'
+        },
+    ]
+    // 操作栏通过权限判断是否显示
+    if (hasPerm(['forumPostTypeEdit', 'forumPostTypeDelete'])) {
+        columns.push({
+            title: '操作',
+            dataIndex: 'action',
+            align: 'center',
+            width: '150px'
+        })
+    }
+    const selectedRowKeys = ref([])
+    // 列表选择配置
+    const options = {
+        // columns数字类型字段加入 needTotal: true 可以勾选自动算账
+        alert: {
+            show: true,
+            clear: () => {
+                selectedRowKeys.value = ref([])
+            }
+        },
+        rowSelection: {
+            onChange: (selectedRowKey, selectedRows) => {
+                selectedRowKeys.value = selectedRowKey
+            }
+        }
+    }
+    const loadData = (parameter) => {
+        return forumPostTypeApi.forumPostTypePage(parameter).then((data) => {
+            return data
+        })
+    }
+    // 重置
+    const reset = () => {
+        searchFormRef.value.resetFields()
+        table.value.refresh(true)
+    }
+    // 删除
+    const deleteForumPostType = (record) => {
+        let params = [
+            {
+                typeId: record.typeId
+            }
+        ]
+        forumPostTypeApi.forumPostTypeDelete(params).then(() => {
+            table.value.refresh(true)
+        })
+    }
+    // 批量删除
+    const deleteBatchForumPostType = (params) => {
+        forumPostTypeApi.forumPostTypeDelete(params).then(() => {
+            table.value.clearRefreshSelected()
+        })
+    }
+</script>

+ 85 - 0
src/views/forum/reportinfo/form.vue

@@ -0,0 +1,85 @@
+<template>
+    <xn-form-container
+        :title="formData.reportId ? '编辑论坛-帖子举报信息表' : '增加论坛-帖子举报信息表'"
+        :width="700"
+        :visible="visible"
+        :destroy-on-close="true"
+        @close="onClose"
+    >
+        <a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
+            <a-form-item label="帖子主键id:" name="postId">
+                <a-input v-model:value="formData.postId" placeholder="请输入帖子主键id" allow-clear />
+            </a-form-item>
+            <a-form-item label="举报人用户id:" name="userId">
+                <a-input v-model:value="formData.userId" placeholder="请输入举报人用户id" allow-clear />
+            </a-form-item>
+            <a-form-item label="举报原因类型 0垃圾广告 1色情内容 2人身攻击 3政治敏感 4其他:" name="reportReasonType">
+                <a-input v-model:value="formData.reportReasonType" placeholder="请输入举报原因类型 0垃圾广告 1色情内容 2人身攻击 3政治敏感 4其他" allow-clear />
+            </a-form-item>
+            <a-form-item label="举报信息描述:" name="reportDetail">
+                <a-input v-model:value="formData.reportDetail" placeholder="请输入举报信息描述" allow-clear />
+            </a-form-item>
+            <a-form-item label="证据图片:" name="evidenceScreenshot">
+                <a-input v-model:value="formData.evidenceScreenshot" placeholder="请输入证据图片" allow-clear />
+            </a-form-item>
+            <a-form-item label="举报处理状态 0待处理 1已关闭帖子 2已驳回:" name="reportStatus">
+                <a-input v-model:value="formData.reportStatus" placeholder="请输入举报处理状态 0待处理 1已关闭帖子 2已驳回" allow-clear />
+            </a-form-item>
+        </a-form>
+        <template #footer>
+            <a-button style="margin-right: 8px" @click="onClose">关闭</a-button>
+            <a-button type="primary" @click="onSubmit" :loading="submitLoading">保存</a-button>
+        </template>
+    </xn-form-container>
+</template>
+
+<script setup name="forumReportInfoForm">
+    import { cloneDeep } from 'lodash-es'
+    import { required } from '@/utils/formRules'
+    import forumReportInfoApi from '@/api/forum/forumReportInfoApi'
+    // 抽屉状态
+    const visible = ref(false)
+    const emit = defineEmits({ successful: null })
+    const formRef = ref()
+    // 表单数据
+    const formData = ref({})
+    const submitLoading = ref(false)
+
+    // 打开抽屉
+    const onOpen = (record) => {
+        visible.value = true
+        if (record) {
+            let recordData = cloneDeep(record)
+            formData.value = Object.assign({}, recordData)
+        }
+    }
+    // 关闭抽屉
+    const onClose = () => {
+        formRef.value.resetFields()
+        formData.value = {}
+        visible.value = false
+    }
+    // 默认要校验的
+    const formRules = {
+    }
+    // 验证并提交数据
+    const onSubmit = () => {
+        formRef.value.validate().then(() => {
+            submitLoading.value = true
+            const formDataParam = cloneDeep(formData.value)
+            forumReportInfoApi
+                .forumReportInfoSubmitForm(formDataParam, formDataParam.reportId)
+                .then(() => {
+                    onClose()
+                    emit('successful')
+                })
+                .finally(() => {
+                    submitLoading.value = false
+                })
+        })
+    }
+    // 抛出函数
+    defineExpose({
+        onOpen
+    })
+</script>

+ 126 - 0
src/views/forum/reportinfo/index.vue

@@ -0,0 +1,126 @@
+<template>
+    <a-card :bordered="false">
+        <s-table
+            ref="table"
+            :columns="columns"
+            :data="loadData"
+            :alert="options.alert.show"
+            bordered
+            :row-key="(record) => record.reportId"
+            :tool-config="toolConfig"
+            :row-selection="options.rowSelection"
+        >
+            <template #operator class="table-operator">
+                <a-space>
+                    <a-button type="primary" @click="formRef.onOpen()" v-if="hasPerm('forumReportInfoAdd')">
+                        <template #icon><plus-outlined /></template>
+                        新增
+                    </a-button>
+                    <xn-batch-delete
+                        v-if="hasPerm('forumReportInfoBatchDelete')"
+                        :selectedRowKeys="selectedRowKeys"
+                        @batchDelete="deleteBatchForumReportInfo"
+                    />
+                </a-space>
+            </template>
+            <template #bodyCell="{ column, record }">
+                <template v-if="column.dataIndex === 'action'">
+                    <a-space>
+                        <a @click="formRef.onOpen(record)" v-if="hasPerm('forumReportInfoEdit')">编辑</a>
+                        <a-divider type="vertical" v-if="hasPerm(['forumReportInfoEdit', 'forumReportInfoDelete'], 'and')" />
+                        <a-popconfirm title="确定要删除吗?" @confirm="deleteForumReportInfo(record)">
+                            <a-button type="link" danger size="small" v-if="hasPerm('forumReportInfoDelete')">删除</a-button>
+                        </a-popconfirm>
+                    </a-space>
+                </template>
+            </template>
+        </s-table>
+    </a-card>
+    <Form ref="formRef" @successful="table.refresh(true)" />
+</template>
+
+<script setup name="reportinfo">
+    import Form from './form.vue'
+    import forumReportInfoApi from '@/api/forum/forumReportInfoApi'
+    const table = ref()
+    const formRef = ref()
+    const toolConfig = { refresh: true, height: true, columnSetting: true, striped: false }
+    const columns = [
+        {
+            title: '帖子主键id',
+            dataIndex: 'postId'
+        },
+        {
+            title: '举报人用户id',
+            dataIndex: 'userId'
+        },
+        {
+            title: '举报原因类型 0垃圾广告 1色情内容 2人身攻击 3政治敏感 4其他',
+            dataIndex: 'reportReasonType'
+        },
+        {
+            title: '举报信息描述',
+            dataIndex: 'reportDetail'
+        },
+        {
+            title: '证据图片',
+            dataIndex: 'evidenceScreenshot'
+        },
+        {
+            title: '举报处理状态 0待处理 1已关闭帖子 2已驳回',
+            dataIndex: 'reportStatus'
+        },
+    ]
+    // 操作栏通过权限判断是否显示
+    if (hasPerm(['forumReportInfoEdit', 'forumReportInfoDelete'])) {
+        columns.push({
+            title: '操作',
+            dataIndex: 'action',
+            align: 'center',
+            width: '150px'
+        })
+    }
+    const selectedRowKeys = ref([])
+    // 列表选择配置
+    const options = {
+        // columns数字类型字段加入 needTotal: true 可以勾选自动算账
+        alert: {
+            show: true,
+            clear: () => {
+                selectedRowKeys.value = ref([])
+            }
+        },
+        rowSelection: {
+            onChange: (selectedRowKey, selectedRows) => {
+                selectedRowKeys.value = selectedRowKey
+            }
+        }
+    }
+    const loadData = (parameter) => {
+        return forumReportInfoApi.forumReportInfoPage(parameter).then((data) => {
+            return data
+        })
+    }
+    // 重置
+    const reset = () => {
+        searchFormRef.value.resetFields()
+        table.value.refresh(true)
+    }
+    // 删除
+    const deleteForumReportInfo = (record) => {
+        let params = [
+            {
+                reportId: record.reportId
+            }
+        ]
+        forumReportInfoApi.forumReportInfoDelete(params).then(() => {
+            table.value.refresh(true)
+        })
+    }
+    // 批量删除
+    const deleteBatchForumReportInfo = (params) => {
+        forumReportInfoApi.forumReportInfoDelete(params).then(() => {
+            table.value.clearRefreshSelected()
+        })
+    }
+</script>

+ 70 - 0
src/views/forum/sensitivity/form.vue

@@ -0,0 +1,70 @@
+<template>
+    <xn-form-container
+        :title="formData.sensitivityId ? '编辑论坛-敏感词' : '增加论坛-敏感词'"
+        :width="700"
+        :visible="visible"
+        :destroy-on-close="true"
+        @close="onClose"
+    >
+        <a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
+            <a-form-item label="敏感词:" name="sensitivityWord">
+                <a-input v-model:value="formData.sensitivityWord" placeholder="请输入敏感词" allow-clear />
+            </a-form-item>
+        </a-form>
+        <template #footer>
+            <a-button style="margin-right: 8px" @click="onClose">关闭</a-button>
+            <a-button type="primary" @click="onSubmit" :loading="submitLoading">保存</a-button>
+        </template>
+    </xn-form-container>
+</template>
+
+<script setup name="forumSensitivityForm">
+    import { cloneDeep } from 'lodash-es'
+    import { required } from '@/utils/formRules'
+    import forumSensitivityApi from '@/api/forum/forumSensitivityApi'
+    // 抽屉状态
+    const visible = ref(false)
+    const emit = defineEmits({ successful: null })
+    const formRef = ref()
+    // 表单数据
+    const formData = ref({})
+    const submitLoading = ref(false)
+
+    // 打开抽屉
+    const onOpen = (record) => {
+        visible.value = true
+        if (record) {
+            let recordData = cloneDeep(record)
+            formData.value = Object.assign({}, recordData)
+        }
+    }
+    // 关闭抽屉
+    const onClose = () => {
+        formRef.value.resetFields()
+        formData.value = {}
+        visible.value = false
+    }
+    // 默认要校验的
+    const formRules = {
+    }
+    // 验证并提交数据
+    const onSubmit = () => {
+        formRef.value.validate().then(() => {
+            submitLoading.value = true
+            const formDataParam = cloneDeep(formData.value)
+            forumSensitivityApi
+                .forumSensitivitySubmitForm(formDataParam, formDataParam.sensitivityId)
+                .then(() => {
+                    onClose()
+                    emit('successful')
+                })
+                .finally(() => {
+                    submitLoading.value = false
+                })
+        })
+    }
+    // 抛出函数
+    defineExpose({
+        onOpen
+    })
+</script>

+ 122 - 0
src/views/forum/sensitivity/index.vue

@@ -0,0 +1,122 @@
+<template>
+    <a-card :bordered="false">
+        <a-form ref="searchFormRef" name="advanced_search" :model="searchFormState" class="ant-advanced-search-form">
+            <a-row :gutter="24">
+                <a-col :span="6">
+                    <a-form-item label="敏感词" name="sensitivityWord">
+                        <a-input v-model:value="searchFormState.sensitivityWord" placeholder="请输入敏感词" />
+                    </a-form-item>
+                </a-col>
+                <a-col :span="6">
+                    <a-button type="primary" @click="table.refresh(true)">查询</a-button>
+                    <a-button style="margin: 0 8px" @click="reset">重置</a-button>
+                </a-col>
+            </a-row>
+        </a-form>
+        <s-table
+            ref="table"
+            :columns="columns"
+            :data="loadData"
+            :alert="options.alert.show"
+            bordered
+            :row-key="(record) => record.sensitivityId"
+            :tool-config="toolConfig"
+            :row-selection="options.rowSelection"
+        >
+            <template #operator class="table-operator">
+                <a-space>
+                    <a-button type="primary" @click="formRef.onOpen()" v-if="hasPerm('forumSensitivityAdd')">
+                        <template #icon><plus-outlined /></template>
+                        新增
+                    </a-button>
+                    <xn-batch-delete
+                        v-if="hasPerm('forumSensitivityBatchDelete')"
+                        :selectedRowKeys="selectedRowKeys"
+                        @batchDelete="deleteBatchForumSensitivity"
+                    />
+                </a-space>
+            </template>
+            <template #bodyCell="{ column, record }">
+                <template v-if="column.dataIndex === 'action'">
+                    <a-space>
+                        <a @click="formRef.onOpen(record)" v-if="hasPerm('forumSensitivityEdit')">编辑</a>
+                        <a-divider type="vertical" v-if="hasPerm(['forumSensitivityEdit', 'forumSensitivityDelete'], 'and')" />
+                        <a-popconfirm title="确定要删除吗?" @confirm="deleteForumSensitivity(record)">
+                            <a-button type="link" danger size="small" v-if="hasPerm('forumSensitivityDelete')">删除</a-button>
+                        </a-popconfirm>
+                    </a-space>
+                </template>
+            </template>
+        </s-table>
+    </a-card>
+    <Form ref="formRef" @successful="table.refresh(true)" />
+</template>
+
+<script setup name="sensitivity">
+    import Form from './form.vue'
+    import forumSensitivityApi from '@/api/forum/forumSensitivityApi'
+    let searchFormState = reactive({})
+    const searchFormRef = ref()
+    const table = ref()
+    const formRef = ref()
+    const toolConfig = { refresh: true, height: true, columnSetting: true, striped: false }
+    const columns = [
+        {
+            title: '敏感词',
+            dataIndex: 'sensitivityWord'
+        },
+    ]
+    // 操作栏通过权限判断是否显示
+    if (hasPerm(['forumSensitivityEdit', 'forumSensitivityDelete'])) {
+        columns.push({
+            title: '操作',
+            dataIndex: 'action',
+            align: 'center',
+            width: '150px'
+        })
+    }
+    const selectedRowKeys = ref([])
+    // 列表选择配置
+    const options = {
+        // columns数字类型字段加入 needTotal: true 可以勾选自动算账
+        alert: {
+            show: true,
+            clear: () => {
+                selectedRowKeys.value = ref([])
+            }
+        },
+        rowSelection: {
+            onChange: (selectedRowKey, selectedRows) => {
+                selectedRowKeys.value = selectedRowKey
+            }
+        }
+    }
+    const loadData = (parameter) => {
+        const searchFormParam = JSON.parse(JSON.stringify(searchFormState))
+        return forumSensitivityApi.forumSensitivityPage(Object.assign(parameter, searchFormParam)).then((data) => {
+            return data
+        })
+    }
+    // 重置
+    const reset = () => {
+        searchFormRef.value.resetFields()
+        table.value.refresh(true)
+    }
+    // 删除
+    const deleteForumSensitivity = (record) => {
+        let params = [
+            {
+                sensitivityId: record.sensitivityId
+            }
+        ]
+        forumSensitivityApi.forumSensitivityDelete(params).then(() => {
+            table.value.refresh(true)
+        })
+    }
+    // 批量删除
+    const deleteBatchForumSensitivity = (params) => {
+        forumSensitivityApi.forumSensitivityDelete(params).then(() => {
+            table.value.clearRefreshSelected()
+        })
+    }
+</script>

+ 79 - 0
src/views/forum/sensitivityrecord/form.vue

@@ -0,0 +1,79 @@
+<template>
+    <xn-form-container
+        :title="formData.recordId ? '编辑敏感词过滤记录' : '增加敏感词过滤记录'"
+        :width="700"
+        :visible="visible"
+        :destroy-on-close="true"
+        @close="onClose"
+    >
+        <a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
+            <a-form-item label="过滤词语:" name="sensitivityWord">
+                <a-input v-model:value="formData.sensitivityWord" placeholder="请输入过滤词语" allow-clear />
+            </a-form-item>
+            <a-form-item label="帖子id:" name="postId">
+                <a-input v-model:value="formData.postId" placeholder="请输入帖子id" allow-clear />
+            </a-form-item>
+            <a-form-item label="过滤类型 0发帖 1回复:" name="recordType">
+                <a-input v-model:value="formData.recordType" placeholder="请输入过滤类型 0发帖 1回复" allow-clear />
+            </a-form-item>
+            <a-form-item label="用户id:" name="userId">
+                <a-input v-model:value="formData.userId" placeholder="请输入用户id" allow-clear />
+            </a-form-item>
+        </a-form>
+        <template #footer>
+            <a-button style="margin-right: 8px" @click="onClose">关闭</a-button>
+            <a-button type="primary" @click="onSubmit" :loading="submitLoading">保存</a-button>
+        </template>
+    </xn-form-container>
+</template>
+
+<script setup name="forumSensitivityRecordForm">
+    import { cloneDeep } from 'lodash-es'
+    import { required } from '@/utils/formRules'
+    import forumSensitivityRecordApi from '@/api/forum/forumSensitivityRecordApi'
+    // 抽屉状态
+    const visible = ref(false)
+    const emit = defineEmits({ successful: null })
+    const formRef = ref()
+    // 表单数据
+    const formData = ref({})
+    const submitLoading = ref(false)
+
+    // 打开抽屉
+    const onOpen = (record) => {
+        visible.value = true
+        if (record) {
+            let recordData = cloneDeep(record)
+            formData.value = Object.assign({}, recordData)
+        }
+    }
+    // 关闭抽屉
+    const onClose = () => {
+        formRef.value.resetFields()
+        formData.value = {}
+        visible.value = false
+    }
+    // 默认要校验的
+    const formRules = {
+    }
+    // 验证并提交数据
+    const onSubmit = () => {
+        formRef.value.validate().then(() => {
+            submitLoading.value = true
+            const formDataParam = cloneDeep(formData.value)
+            forumSensitivityRecordApi
+                .forumSensitivityRecordSubmitForm(formDataParam, formDataParam.recordId)
+                .then(() => {
+                    onClose()
+                    emit('successful')
+                })
+                .finally(() => {
+                    submitLoading.value = false
+                })
+        })
+    }
+    // 抛出函数
+    defineExpose({
+        onOpen
+    })
+</script>

+ 118 - 0
src/views/forum/sensitivityrecord/index.vue

@@ -0,0 +1,118 @@
+<template>
+    <a-card :bordered="false">
+        <s-table
+            ref="table"
+            :columns="columns"
+            :data="loadData"
+            :alert="options.alert.show"
+            bordered
+            :row-key="(record) => record.recordId"
+            :tool-config="toolConfig"
+            :row-selection="options.rowSelection"
+        >
+            <template #operator class="table-operator">
+                <a-space>
+                    <a-button type="primary" @click="formRef.onOpen()" v-if="hasPerm('forumSensitivityRecordAdd')">
+                        <template #icon><plus-outlined /></template>
+                        新增
+                    </a-button>
+                    <xn-batch-delete
+                        v-if="hasPerm('forumSensitivityRecordBatchDelete')"
+                        :selectedRowKeys="selectedRowKeys"
+                        @batchDelete="deleteBatchForumSensitivityRecord"
+                    />
+                </a-space>
+            </template>
+            <template #bodyCell="{ column, record }">
+                <template v-if="column.dataIndex === 'action'">
+                    <a-space>
+                        <a @click="formRef.onOpen(record)" v-if="hasPerm('forumSensitivityRecordEdit')">编辑</a>
+                        <a-divider type="vertical" v-if="hasPerm(['forumSensitivityRecordEdit', 'forumSensitivityRecordDelete'], 'and')" />
+                        <a-popconfirm title="确定要删除吗?" @confirm="deleteForumSensitivityRecord(record)">
+                            <a-button type="link" danger size="small" v-if="hasPerm('forumSensitivityRecordDelete')">删除</a-button>
+                        </a-popconfirm>
+                    </a-space>
+                </template>
+            </template>
+        </s-table>
+    </a-card>
+    <Form ref="formRef" @successful="table.refresh(true)" />
+</template>
+
+<script setup name="sensitivityrecord">
+    import Form from './form.vue'
+    import forumSensitivityRecordApi from '@/api/forum/forumSensitivityRecordApi'
+    const table = ref()
+    const formRef = ref()
+    const toolConfig = { refresh: true, height: true, columnSetting: true, striped: false }
+    const columns = [
+        {
+            title: '过滤词语',
+            dataIndex: 'sensitivityWord'
+        },
+        {
+            title: '帖子id',
+            dataIndex: 'postId'
+        },
+        {
+            title: '过滤类型 0发帖 1回复',
+            dataIndex: 'recordType'
+        },
+        {
+            title: '用户id',
+            dataIndex: 'userId'
+        },
+    ]
+    // 操作栏通过权限判断是否显示
+    if (hasPerm(['forumSensitivityRecordEdit', 'forumSensitivityRecordDelete'])) {
+        columns.push({
+            title: '操作',
+            dataIndex: 'action',
+            align: 'center',
+            width: '150px'
+        })
+    }
+    const selectedRowKeys = ref([])
+    // 列表选择配置
+    const options = {
+        // columns数字类型字段加入 needTotal: true 可以勾选自动算账
+        alert: {
+            show: true,
+            clear: () => {
+                selectedRowKeys.value = ref([])
+            }
+        },
+        rowSelection: {
+            onChange: (selectedRowKey, selectedRows) => {
+                selectedRowKeys.value = selectedRowKey
+            }
+        }
+    }
+    const loadData = (parameter) => {
+        return forumSensitivityRecordApi.forumSensitivityRecordPage(parameter).then((data) => {
+            return data
+        })
+    }
+    // 重置
+    const reset = () => {
+        searchFormRef.value.resetFields()
+        table.value.refresh(true)
+    }
+    // 删除
+    const deleteForumSensitivityRecord = (record) => {
+        let params = [
+            {
+                recordId: record.recordId
+            }
+        ]
+        forumSensitivityRecordApi.forumSensitivityRecordDelete(params).then(() => {
+            table.value.refresh(true)
+        })
+    }
+    // 批量删除
+    const deleteBatchForumSensitivityRecord = (params) => {
+        forumSensitivityRecordApi.forumSensitivityRecordDelete(params).then(() => {
+            table.value.clearRefreshSelected()
+        })
+    }
+</script>

+ 2 - 2
src/views/myResources/personalResources/index.vue

@@ -117,8 +117,8 @@
 		// 	return false
 		// }
 		switch (indexType.value) {
-			case 'home':
-				return defineAsyncComponent(() => import('@/views/myResources/personalResources/Home.vue'))
+			// case 'home':
+			// 	return defineAsyncComponent(() => import('@/views/myResources/personalResources/Home.vue'))
 			case 'resources':
 				return myResources
 			case 'favorites':

Datei-Diff unterdrückt, da er zu groß ist
+ 146 - 0
stats.html


Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.