Merge branch 'developer' of ssh://code.cyweb.top:30033/scj/zhxy/v3/cloud-ui into developer
This commit is contained in:
@@ -42,7 +42,7 @@ export const getObj = (id: string | number) => {
|
|||||||
*/
|
*/
|
||||||
export const delObj = (id: string | number) => {
|
export const delObj = (id: string | number) => {
|
||||||
return request({
|
return request({
|
||||||
url: `/recruit/recruitstudentplan/deletById`,
|
url: `/recruit/recruitstudentplan/deleteById`,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: { id :id},
|
data: { id :id},
|
||||||
});
|
});
|
||||||
|
|||||||
14
src/api/stuwork/classassets.ts
Normal file
14
src/api/stuwork/classassets.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import request from '/@/utils/request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 教室公物编辑及门锁密码
|
||||||
|
* 接口文档:POST /api/stuwork/classassets/edit
|
||||||
|
* @param data 教室公物数据(包含 buildingNo, deptName, classCode, position, platformType, tyType, tvType, chairCnt, tableCnt, remarks, password 等)
|
||||||
|
*/
|
||||||
|
export const editAssets = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/classassets/edit',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
};
|
||||||
35
src/api/stuwork/dormsignrecord.ts
Normal file
35
src/api/stuwork/dormsignrecord.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import request from '/@/utils/request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询宿舍点名列表
|
||||||
|
* @param query
|
||||||
|
*/
|
||||||
|
export const fetchList = (query?: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/dormsignrecord/page',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增宿舍点名
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export const addObj = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/dormsignrecord',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化宿舍学生信息用于考勤
|
||||||
|
*/
|
||||||
|
export const initDormStuInfoForAttendance = () => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/dormsignrecord/task/initDormStuInfoForAttendance',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
};
|
||||||
85
src/api/stuwork/filemanager.ts
Normal file
85
src/api/stuwork/filemanager.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import request from '/@/utils/request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件列表(分页)
|
||||||
|
* @param query
|
||||||
|
*/
|
||||||
|
export const fetchList = (query?: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/filemanager/page',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件列表(不分页,按层级)
|
||||||
|
* @param query
|
||||||
|
*/
|
||||||
|
export const getList = (query?: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/filemanager/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件详情
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
export const getDetail = (id: string) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/filemanager/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增文件
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export const addObj = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/filemanager',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑文件
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export const editObj = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/filemanager/edit',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑文件夹
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export const editFile = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/filemanager/editFile',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除文件
|
||||||
|
* @param ids
|
||||||
|
*/
|
||||||
|
export const delObj = (ids: string[]) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/filemanager/delete',
|
||||||
|
method: 'post',
|
||||||
|
data: ids
|
||||||
|
});
|
||||||
|
};
|
||||||
61
src/api/stuwork/moralplan.ts
Normal file
61
src/api/stuwork/moralplan.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import request from '/@/utils/request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询德育计划列表
|
||||||
|
* @param query
|
||||||
|
*/
|
||||||
|
export const fetchList = (query?: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/moralplan/page',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增德育计划
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export const addObj = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/moralplan',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取详情
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
export const getDetail = (id: string) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/moralplan/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑德育计划
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export const editObj = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/moralplan/edit',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除德育计划
|
||||||
|
* @param ids
|
||||||
|
*/
|
||||||
|
export const delObj = (ids: string[]) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/moralplan/delete',
|
||||||
|
method: 'post',
|
||||||
|
data: ids
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -25,3 +25,16 @@ export const addClassRoomAssign = (data: { buildingNo?: string | number; positio
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消教室安排
|
||||||
|
* 接口文档:POST /api/stuwork/teachclassroomassign/delClassRoomAssign
|
||||||
|
* @param data 教室基础数据(包含 id, classCode, position 等)
|
||||||
|
*/
|
||||||
|
export const delClassRoomAssign = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/teachclassroomassign/delClassRoomAssign',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|||||||
61
src/api/stuwork/termactivity.ts
Normal file
61
src/api/stuwork/termactivity.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import request from '/@/utils/request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询学期活动列表
|
||||||
|
* @param query
|
||||||
|
*/
|
||||||
|
export const fetchList = (query?: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/termactivity/page',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取学期活动详情
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
export const getDetail = (id: string) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/termactivity/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增学期活动
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export const addObj = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/termactivity',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑学期活动
|
||||||
|
* @param data
|
||||||
|
*/
|
||||||
|
export const editObj = (data: any) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/termactivity/edit',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除学期活动
|
||||||
|
* @param ids
|
||||||
|
*/
|
||||||
|
export const delObj = (ids: string[]) => {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/termactivity/delete',
|
||||||
|
method: 'post',
|
||||||
|
data: ids
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -155,6 +155,10 @@ export function useTableColumnControl(
|
|||||||
* 根据 visibleColumns 和 columnOrder 计算最终显示的列
|
* 根据 visibleColumns 和 columnOrder 计算最终显示的列
|
||||||
*/
|
*/
|
||||||
const visibleColumnsSorted = computed(() => {
|
const visibleColumnsSorted = computed(() => {
|
||||||
|
// 如果 visibleColumns 为空,显示所有列(初始化时)
|
||||||
|
if (visibleColumns.value.length === 0) {
|
||||||
|
return tableColumns.filter(col => !col.alwaysShow && !col.fixed)
|
||||||
|
}
|
||||||
// 过滤出可见的列
|
// 过滤出可见的列
|
||||||
const columns = tableColumns.filter(col => {
|
const columns = tableColumns.filter(col => {
|
||||||
const key = col.prop || col.label || ''
|
const key = col.prop || col.label || ''
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-upload
|
<el-upload
|
||||||
|
ref="uploadRef"
|
||||||
class="upload-demo"
|
class="upload-demo"
|
||||||
:action="uploadUrl"
|
:action="uploadUrl"
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
@@ -34,7 +35,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed, nextTick } from 'vue';
|
||||||
import { ElNotification } from 'element-plus';
|
import { ElNotification } from 'element-plus';
|
||||||
import { Download, UploadFilled } from '@element-plus/icons-vue';
|
import { Download, UploadFilled } from '@element-plus/icons-vue';
|
||||||
import { Session } from '/@/utils/storage';
|
import { Session } from '/@/utils/storage';
|
||||||
@@ -54,6 +55,7 @@ const headers = computed(() => {
|
|||||||
|
|
||||||
const uploadUrl = ref('')
|
const uploadUrl = ref('')
|
||||||
const currentType = ref('')
|
const currentType = ref('')
|
||||||
|
const uploadRef = ref<{ clearFiles?: () => void }>()
|
||||||
const titleMap: Record<string, string> = {
|
const titleMap: Record<string, string> = {
|
||||||
titleRelation: '职称信息导入',
|
titleRelation: '职称信息导入',
|
||||||
quaRelation: '职业资格信息导入',
|
quaRelation: '职业资格信息导入',
|
||||||
@@ -68,6 +70,9 @@ const init = (type: any) => {
|
|||||||
uploadUrl.value = '/professional/file/importTeacherOtherInfo?type=' + type
|
uploadUrl.value = '/professional/file/importTeacherOtherInfo?type=' + type
|
||||||
title.value = titleMap[type] || '信息导入'
|
title.value = titleMap[type] || '信息导入'
|
||||||
visible.value = true
|
visible.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
uploadRef.value?.clearFiles()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emits
|
// Emits
|
||||||
|
|||||||
@@ -17,12 +17,14 @@
|
|||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
|
|
||||||
<el-upload
|
<el-upload
|
||||||
|
ref="uploadRef"
|
||||||
class="upload-demo"
|
class="upload-demo"
|
||||||
action="/professional/file/makeImportTeacherInfoSimpleTask"
|
action="/professional/file/makeImportTeacherInfoSimpleTask"
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
:accept="'.xls,.xlsx'"
|
:accept="'.xls,.xlsx'"
|
||||||
:on-success="handleUploadSuccess"
|
:on-success="handleUploadSuccess"
|
||||||
:on-error="handleAvatarError"
|
:on-error="handleAvatarError"
|
||||||
|
:limit="1"
|
||||||
drag>
|
drag>
|
||||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||||
<div class="el-upload__text">
|
<div class="el-upload__text">
|
||||||
@@ -38,7 +40,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed, nextTick } from 'vue'
|
||||||
import { ElNotification } from 'element-plus'
|
import { ElNotification } from 'element-plus'
|
||||||
import { Download, UploadFilled } from '@element-plus/icons-vue'
|
import { Download, UploadFilled } from '@element-plus/icons-vue'
|
||||||
import { Session } from '/@/utils/storage'
|
import { Session } from '/@/utils/storage'
|
||||||
@@ -53,9 +55,14 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const uploadRef = ref<{ clearFiles?: () => void }>()
|
||||||
|
|
||||||
// 方法
|
// 方法
|
||||||
const init = () => {
|
const init = () => {
|
||||||
visible.value = true
|
visible.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
uploadRef.value?.clearFiles?.()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleUploadSuccess = () => {
|
const handleUploadSuccess = () => {
|
||||||
|
|||||||
@@ -1,10 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog v-model="visible" :title="title" width="600" append-to-body>
|
<el-dialog v-model="visible" :title="title" width="600" append-to-body>
|
||||||
<div style="text-align: center; margin-bottom: 20px">
|
<div style="text-align: center; margin-bottom: 20px" v-if="currentType!='R10003'">
|
||||||
<el-button type="success" :icon="Download" @click="handleDownloadTemplate">下载模板</el-button>
|
<el-button type="success" :icon="Download" @click="handleDownloadTemplate">下载模板</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-upload
|
<el-alert
|
||||||
|
v-if="currentType=='R10003'"
|
||||||
|
type="warning"
|
||||||
|
:closable="false"
|
||||||
|
show-icon
|
||||||
|
style="margin-bottom: 20px;">
|
||||||
|
<template #title>
|
||||||
|
<span> 请从中招平台导出数据后导入</span>
|
||||||
|
</template>
|
||||||
|
</el-alert>
|
||||||
|
|
||||||
|
|
||||||
|
<el-upload
|
||||||
|
ref="uploadRef"
|
||||||
class="upload-demo"
|
class="upload-demo"
|
||||||
:action="uploadUrl"
|
:action="uploadUrl"
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
@@ -24,7 +37,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed, nextTick } from 'vue';
|
||||||
import { ElNotification } from 'element-plus';
|
import { ElNotification } from 'element-plus';
|
||||||
import { Download, UploadFilled } from '@element-plus/icons-vue';
|
import { Download, UploadFilled } from '@element-plus/icons-vue';
|
||||||
import { Session } from '/@/utils/storage';
|
import { Session } from '/@/utils/storage';
|
||||||
@@ -44,20 +57,26 @@ const headers = computed(() => {
|
|||||||
|
|
||||||
const uploadUrl = ref('')
|
const uploadUrl = ref('')
|
||||||
const currentType = ref('')
|
const currentType = ref('')
|
||||||
|
const uploadRef = ref<{ clearFiles?: () => void }>()
|
||||||
const titleMap: Record<string, string> = {
|
const titleMap: Record<string, string> = {
|
||||||
planMajor: '计划专业导入'
|
R10001: '计划专业导入',
|
||||||
|
R10002: '地区分数导入',
|
||||||
|
R10003: '中招平台数据导入'
|
||||||
}
|
}
|
||||||
// 方法
|
// 方法
|
||||||
const init = (type: any) => {
|
const init = (type: any) => {
|
||||||
currentType.value = type
|
currentType.value = type
|
||||||
uploadUrl.value = '/professional/file/importTeacherOtherInfo?type=' + type
|
uploadUrl.value = '/api/recruit/file/importRecruitInfo?type=' + type
|
||||||
title.value = titleMap[type] || '信息导入'
|
title.value = titleMap[type] || '信息导入'
|
||||||
visible.value = true
|
visible.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
uploadRef.value?.clearFiles()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emits
|
// Emits
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'refreshData'): void
|
(e: 'refreshDataList'): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const handleUploadSuccess = () => {
|
const handleUploadSuccess = () => {
|
||||||
@@ -68,7 +87,7 @@ const handleUploadSuccess = () => {
|
|||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
|
|
||||||
emit('refreshData')
|
emit('refreshDataList')
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAvatarError = (err: any) => {
|
const handleAvatarError = (err: any) => {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
<el-button v-if="hasAuth('recruit_recruitplanmajor_add')" type="primary" icon="FolderAdd" @click="addOrUpdateHandle"> 新 增 </el-button>
|
<el-button v-if="hasAuth('recruit_recruitplanmajor_add')" type="primary" icon="FolderAdd" @click="addOrUpdateHandle"> 新 增 </el-button>
|
||||||
|
|
||||||
<el-button
|
<el-button
|
||||||
v-auth="'professional_teacherinfo_import'"
|
v-auth="'recruit_major_import'"
|
||||||
type="primary"
|
type="primary"
|
||||||
plain
|
plain
|
||||||
icon="UploadFilled"
|
icon="UploadFilled"
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
<!-- 弹窗, 新增 / 修改 -->
|
<!-- 弹窗, 新增 / 修改 -->
|
||||||
<table-form ref="addOrUpdateRef" @refreshDataList="getDataList" />
|
<table-form ref="addOrUpdateRef" @refreshDataList="getDataList" />
|
||||||
|
|
||||||
<import-recruit-info ref="ImportRecruitInfoRef"></import-recruit-info>
|
<import-recruit-info ref="ImportRecruitInfoRef" @refreshDataList="getDataList"></import-recruit-info>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -283,7 +283,7 @@ const resetQuery = () => {
|
|||||||
const exportLoading = ref(false);
|
const exportLoading = ref(false);
|
||||||
|
|
||||||
const handleImportDialog = () => {
|
const handleImportDialog = () => {
|
||||||
ImportRecruitInfoRef.value?.init("planMajor");
|
ImportRecruitInfoRef.value?.init("R10001");
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@@ -29,6 +29,15 @@
|
|||||||
>
|
>
|
||||||
新 增
|
新 增
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-auth="'recruit_areascore_import'"
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
icon="UploadFilled"
|
||||||
|
:loading="exportLoading"
|
||||||
|
@click="handleImportDialog"
|
||||||
|
>导入信息
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
@@ -83,6 +92,9 @@
|
|||||||
|
|
||||||
<!-- 弹窗, 新增 / 修改 -->
|
<!-- 弹窗, 新增 / 修改 -->
|
||||||
<table-form ref="addOrUpdateRef" @refreshDataList="getDataList" />
|
<table-form ref="addOrUpdateRef" @refreshDataList="getDataList" />
|
||||||
|
|
||||||
|
<import-recruit-info ref="ImportRecruitInfoRef" @refreshDataList="getDataList"></import-recruit-info>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -94,6 +106,8 @@ import { BasicTableProps, useTable } from '/@/hooks/table'
|
|||||||
import { useMessage, useMessageBox } from '/@/hooks/message'
|
import { useMessage, useMessageBox } from '/@/hooks/message'
|
||||||
import { getList } from '/@/api/recruit/recruitstudentplangroup'
|
import { getList } from '/@/api/recruit/recruitstudentplangroup'
|
||||||
import { fetchList, delObj } from '/@/api/recruit/recruitstudentplancorrectscoreconfig'
|
import { fetchList, delObj } from '/@/api/recruit/recruitstudentplancorrectscoreconfig'
|
||||||
|
const ImportRecruitInfo = defineAsyncComponent(() => import('/@/views/recruit/common/import-recruit-info.vue'));
|
||||||
|
const ImportRecruitInfoRef=ref<any>();
|
||||||
|
|
||||||
const TableForm = defineAsyncComponent(() => import('./detaiform.vue'))
|
const TableForm = defineAsyncComponent(() => import('./detaiform.vue'))
|
||||||
const { hasAuth } = useAuth()
|
const { hasAuth } = useAuth()
|
||||||
@@ -183,6 +197,12 @@ const resetQuery = () => {
|
|||||||
getDataList()
|
getDataList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const exportLoading = ref(false);
|
||||||
|
|
||||||
|
const handleImportDialog = () => {
|
||||||
|
ImportRecruitInfoRef.value?.init("R10002");
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
init()
|
init()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -281,19 +281,28 @@
|
|||||||
@click="handleAddData">新增
|
@click="handleAddData">新增
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="hasAuth('zipExport')"
|
v-auth="'recruit_zzpt_import'"
|
||||||
type="warning"
|
type="primary"
|
||||||
plain
|
plain
|
||||||
icon="Download"
|
icon="UploadFilled"
|
||||||
@click="downZip()">招生名单打包导出
|
:loading="exportLoading"
|
||||||
</el-button>
|
@click="handleImportDialog"
|
||||||
<el-button
|
>导入中招平台数据
|
||||||
class="ml10"
|
|
||||||
type="warning"
|
|
||||||
plain
|
|
||||||
icon="Download"
|
|
||||||
@click="handleExport()">名单导出
|
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<!-- <el-button-->
|
||||||
|
<!-- v-if="hasAuth('zipExport')"-->
|
||||||
|
<!-- type="warning"-->
|
||||||
|
<!-- plain-->
|
||||||
|
<!-- icon="Download"-->
|
||||||
|
<!-- @click="downZip()">招生名单打包导出-->
|
||||||
|
<!-- </el-button>-->
|
||||||
|
<!-- <el-button -->
|
||||||
|
<!-- class="ml10"-->
|
||||||
|
<!-- type="warning"-->
|
||||||
|
<!-- plain-->
|
||||||
|
<!-- icon="Download"-->
|
||||||
|
<!-- @click="handleExport()">名单导出-->
|
||||||
|
<!-- </el-button>-->
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
@@ -634,7 +643,9 @@
|
|||||||
<AdmissionNoticeDialog ref="admissionNoticeDialogRef" @refresh="getDataList"></AdmissionNoticeDialog>
|
<AdmissionNoticeDialog ref="admissionNoticeDialogRef" @refresh="getDataList"></AdmissionNoticeDialog>
|
||||||
|
|
||||||
<InterviewForm ref="interviewFormRef" @refresh="getDataList"></InterviewForm>
|
<InterviewForm ref="interviewFormRef" @refresh="getDataList"></InterviewForm>
|
||||||
</div>
|
|
||||||
|
<import-recruit-info ref="ImportRecruitInfoRef" @refreshDataList="getDataList"></import-recruit-info>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -681,6 +692,8 @@ const InterviewForm = defineAsyncComponent(() => import('/@/views/recruit/recrui
|
|||||||
const PayQrcodeDialog = defineAsyncComponent(() => import('./PayQrcodeDialog.vue'))
|
const PayQrcodeDialog = defineAsyncComponent(() => import('./PayQrcodeDialog.vue'))
|
||||||
const AdmissionNoticeDialog = defineAsyncComponent(() => import('./AdmissionNoticeDialog.vue'))
|
const AdmissionNoticeDialog = defineAsyncComponent(() => import('./AdmissionNoticeDialog.vue'))
|
||||||
const ActionDropdown = defineAsyncComponent(() => import('/@/components/tools/action-dropdown.vue'))
|
const ActionDropdown = defineAsyncComponent(() => import('/@/components/tools/action-dropdown.vue'))
|
||||||
|
const ImportRecruitInfo = defineAsyncComponent(() => import('/@/views/recruit/common/import-recruit-info.vue'));
|
||||||
|
const ImportRecruitInfoRef=ref<any>();
|
||||||
const { hasAuth } = useAuth()
|
const { hasAuth } = useAuth()
|
||||||
// 消息提示 hooks
|
// 消息提示 hooks
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
@@ -1131,6 +1144,10 @@ watch(() => dataForm.groupId, () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const handleImportDialog = () => {
|
||||||
|
ImportRecruitInfoRef.value?.init("R10003");
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
init()
|
init()
|
||||||
})
|
})
|
||||||
|
|||||||
327
src/views/stuwork/classroombase/assets.vue
Normal file
327
src/views/stuwork/classroombase/assets.vue
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
title="教室公物编辑"
|
||||||
|
v-model="visible"
|
||||||
|
:width="800"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
draggable>
|
||||||
|
<el-form
|
||||||
|
ref="dataFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="dataRules"
|
||||||
|
label-width="120px"
|
||||||
|
v-loading="loading">
|
||||||
|
<el-row :gutter="24">
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="楼号" prop="buildingNo">
|
||||||
|
<el-input
|
||||||
|
v-model="form.buildingNo"
|
||||||
|
placeholder="楼号"
|
||||||
|
disabled />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="学院" prop="deptName">
|
||||||
|
<el-input
|
||||||
|
v-model="form.deptName"
|
||||||
|
placeholder="学院"
|
||||||
|
disabled />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="班级" prop="classCode">
|
||||||
|
<el-input
|
||||||
|
v-model="form.classNo"
|
||||||
|
placeholder="班级"
|
||||||
|
disabled />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="教室位置" prop="position">
|
||||||
|
<el-input
|
||||||
|
v-model="form.position"
|
||||||
|
placeholder="教室位置"
|
||||||
|
disabled />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="讲台类型" prop="platformType">
|
||||||
|
<el-select
|
||||||
|
v-model="form.platformType"
|
||||||
|
placeholder="请选择讲台类型"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in platformTypeList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="投影类型" prop="tyType">
|
||||||
|
<el-select
|
||||||
|
v-model="form.tyType"
|
||||||
|
placeholder="请选择投影类型"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in tyTypeList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="电视机" prop="tvType">
|
||||||
|
<el-select
|
||||||
|
v-model="form.tvType"
|
||||||
|
placeholder="请选择电视机"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in tvTypeList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="方凳数量" prop="chairCnt">
|
||||||
|
<el-input-number
|
||||||
|
v-model="form.chairCnt"
|
||||||
|
:min="0"
|
||||||
|
placeholder="请输入方凳数量"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="课桌数量" prop="tableCnt">
|
||||||
|
<el-input-number
|
||||||
|
v-model="form.tableCnt"
|
||||||
|
:min="0"
|
||||||
|
placeholder="请输入课桌数量"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="备注" prop="remarks">
|
||||||
|
<el-input
|
||||||
|
v-model="form.remarks"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
placeholder="请输入备注" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="visible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="onSubmit" :disabled="loading">确 认</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="ClassroomAssetsDialog">
|
||||||
|
import { ref, reactive, nextTick, onMounted } from 'vue'
|
||||||
|
import { useMessage } from '/@/hooks/message'
|
||||||
|
import { editAssets } from '/@/api/stuwork/classassets'
|
||||||
|
import { getDicts } from '/@/api/admin/dict'
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
|
// 定义变量内容
|
||||||
|
const dataFormRef = ref()
|
||||||
|
const visible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const platformTypeList = ref<any[]>([])
|
||||||
|
const tyTypeList = ref<any[]>([])
|
||||||
|
const tvTypeList = ref<any[]>([])
|
||||||
|
|
||||||
|
// 提交表单数据
|
||||||
|
const form = reactive({
|
||||||
|
id: '',
|
||||||
|
buildingNo: '',
|
||||||
|
deptName: '',
|
||||||
|
deptCode: '',
|
||||||
|
classCode: '',
|
||||||
|
classNo: '',
|
||||||
|
position: '',
|
||||||
|
platformType: '',
|
||||||
|
tyType: '',
|
||||||
|
tvType: '',
|
||||||
|
chairCnt: 0,
|
||||||
|
tableCnt: 0,
|
||||||
|
remarks: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 定义校验规则
|
||||||
|
const dataRules = {
|
||||||
|
platformType: [
|
||||||
|
{ required: true, message: '请选择讲台类型', trigger: 'change' }
|
||||||
|
],
|
||||||
|
tyType: [
|
||||||
|
{ required: true, message: '请选择投影类型', trigger: 'change' }
|
||||||
|
],
|
||||||
|
tvType: [
|
||||||
|
{ required: true, message: '请选择电视机', trigger: 'change' }
|
||||||
|
],
|
||||||
|
chairCnt: [
|
||||||
|
{ required: true, message: '请填写方凳数量', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
tableCnt: [
|
||||||
|
{ required: true, message: '请填写课桌数量', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
const openDialog = async (row: any) => {
|
||||||
|
visible.value = true
|
||||||
|
resetForm()
|
||||||
|
|
||||||
|
// 填充现有数据
|
||||||
|
if (row) {
|
||||||
|
form.id = row.id || ''
|
||||||
|
form.buildingNo = row.buildingNo || ''
|
||||||
|
form.deptName = row.deptName || ''
|
||||||
|
form.deptCode = row.deptCode || ''
|
||||||
|
form.classCode = row.classCode || ''
|
||||||
|
form.classNo = row.classNo || ''
|
||||||
|
form.position = row.position || ''
|
||||||
|
form.platformType = row.platformType || ''
|
||||||
|
form.tyType = row.tyType || ''
|
||||||
|
form.tvType = row.tvType || ''
|
||||||
|
form.chairCnt = row.chairCnt || 0
|
||||||
|
form.tableCnt = row.tableCnt || 0
|
||||||
|
form.remarks = row.remarks || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
dataFormRef.value?.clearValidate()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
const resetForm = () => {
|
||||||
|
form.id = ''
|
||||||
|
form.buildingNo = ''
|
||||||
|
form.deptName = ''
|
||||||
|
form.deptCode = ''
|
||||||
|
form.classCode = ''
|
||||||
|
form.classNo = ''
|
||||||
|
form.position = ''
|
||||||
|
form.platformType = ''
|
||||||
|
form.tyType = ''
|
||||||
|
form.tvType = ''
|
||||||
|
form.chairCnt = 0
|
||||||
|
form.tableCnt = 0
|
||||||
|
form.remarks = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const onSubmit = async () => {
|
||||||
|
if (!dataFormRef.value) return
|
||||||
|
|
||||||
|
await dataFormRef.value.validate(async (valid: boolean) => {
|
||||||
|
if (!valid) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const submitData = {
|
||||||
|
id: form.id,
|
||||||
|
buildingNo: form.buildingNo,
|
||||||
|
deptName: form.deptName,
|
||||||
|
deptCode: form.deptCode,
|
||||||
|
classCode: form.classCode,
|
||||||
|
classNo: form.classNo,
|
||||||
|
position: form.position,
|
||||||
|
platformType: form.platformType,
|
||||||
|
tyType: form.tyType,
|
||||||
|
tvType: form.tvType,
|
||||||
|
chairCnt: form.chairCnt,
|
||||||
|
tableCnt: form.tableCnt,
|
||||||
|
remarks: form.remarks
|
||||||
|
}
|
||||||
|
await editAssets(submitData)
|
||||||
|
useMessage().success('保存成功')
|
||||||
|
visible.value = false
|
||||||
|
emit('refresh')
|
||||||
|
} catch (_err) {
|
||||||
|
// 错误由 request 拦截器统一提示
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取讲台类型字典
|
||||||
|
const getPlatformTypeDict = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getDicts('platform_type')
|
||||||
|
if (res.data) {
|
||||||
|
platformTypeList.value = Array.isArray(res.data) ? res.data.map((item: any) => ({
|
||||||
|
label: item.label || item.dictLabel || item.name,
|
||||||
|
value: item.value || item.dictValue || item.code
|
||||||
|
})) : []
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
platformTypeList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取投影类型字典
|
||||||
|
const getTyTypeDict = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getDicts('ty_type')
|
||||||
|
if (res.data) {
|
||||||
|
tyTypeList.value = Array.isArray(res.data) ? res.data.map((item: any) => ({
|
||||||
|
label: item.label || item.dictLabel || item.name,
|
||||||
|
value: item.value || item.dictValue || item.code
|
||||||
|
})) : []
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
tyTypeList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取电视机类型字典
|
||||||
|
const getTvTypeDict = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getDicts('tv_type')
|
||||||
|
if (res.data) {
|
||||||
|
tvTypeList.value = Array.isArray(res.data) ? res.data.map((item: any) => ({
|
||||||
|
label: item.label || item.dictLabel || item.name,
|
||||||
|
value: item.value || item.dictValue || item.code
|
||||||
|
})) : []
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
tvTypeList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
getPlatformTypeDict()
|
||||||
|
getTyTypeDict()
|
||||||
|
getTvTypeDict()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 暴露方法
|
||||||
|
defineExpose({
|
||||||
|
openDialog
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
</style>
|
||||||
@@ -181,19 +181,43 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
<el-table-column label="操作" width="380" align="center" fixed="right">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><Setting /></el-icon>
|
<el-icon><Setting /></el-icon>
|
||||||
<span style="margin-left: 4px">操作</span>
|
<span style="margin-left: 4px">操作</span>
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
|
v-if="!scope.row.classCode || !scope.row.position"
|
||||||
icon="Setting"
|
icon="Setting"
|
||||||
link
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="handleArrange(scope.row)">
|
@click="handleArrange(scope.row)">
|
||||||
教室安排
|
教室安排
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Close"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleCancelArrange(scope.row)">
|
||||||
|
取消教室安排
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Collection"
|
||||||
|
link
|
||||||
|
type="success"
|
||||||
|
@click="handleAssets(scope.row)"
|
||||||
|
class="ml10">
|
||||||
|
教室公物
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Lock"
|
||||||
|
link
|
||||||
|
type="warning"
|
||||||
|
@click="handlePassword(scope.row)"
|
||||||
|
class="ml10">
|
||||||
|
门锁密码
|
||||||
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<template #empty>
|
<template #empty>
|
||||||
@@ -213,6 +237,10 @@
|
|||||||
|
|
||||||
<!-- 教室安排表单弹窗 -->
|
<!-- 教室安排表单弹窗 -->
|
||||||
<arrange-dialog ref="arrangeDialogRef" @refresh="getDataList" />
|
<arrange-dialog ref="arrangeDialogRef" @refresh="getDataList" />
|
||||||
|
<!-- 教室公物编辑弹窗 -->
|
||||||
|
<assets-dialog ref="assetsDialogRef" @refresh="getDataList" />
|
||||||
|
<!-- 门锁密码编辑弹窗 -->
|
||||||
|
<password-dialog ref="passwordDialogRef" @refresh="getDataList" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -226,7 +254,10 @@ import { getDicts } from "/@/api/admin/dict";
|
|||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import ArrangeDialog from './arrange.vue'
|
import ArrangeDialog from './arrange.vue'
|
||||||
import { List, OfficeBuilding, CircleCheck, Location, UserFilled, Collection, Setting, Menu, Calendar, Search, Document } from '@element-plus/icons-vue'
|
import AssetsDialog from './assets.vue'
|
||||||
|
import PasswordDialog from './password.vue'
|
||||||
|
import { delClassRoomAssign } from '/@/api/stuwork/teachclassroomassign'
|
||||||
|
import { List, OfficeBuilding, CircleCheck, Location, UserFilled, Collection, Setting, Menu, Calendar, Search, Document, Close, Lock } from '@element-plus/icons-vue'
|
||||||
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
@@ -240,6 +271,8 @@ const platformTypeList = ref<any[]>([])
|
|||||||
const tyTypeList = ref<any[]>([])
|
const tyTypeList = ref<any[]>([])
|
||||||
const tvTypeList = ref<any[]>([])
|
const tvTypeList = ref<any[]>([])
|
||||||
const arrangeDialogRef = ref()
|
const arrangeDialogRef = ref()
|
||||||
|
const assetsDialogRef = ref()
|
||||||
|
const passwordDialogRef = ref()
|
||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
@@ -416,6 +449,33 @@ const handleArrange = (row: any) => {
|
|||||||
arrangeDialogRef.value?.openDialog(row)
|
arrangeDialogRef.value?.openDialog(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 取消教室安排
|
||||||
|
const handleCancelArrange = async (row: any) => {
|
||||||
|
const { confirm } = useMessageBox()
|
||||||
|
try {
|
||||||
|
await confirm('确定要取消教室安排吗?')
|
||||||
|
await delClassRoomAssign(row)
|
||||||
|
useMessage().success('取消成功')
|
||||||
|
getDataList()
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err !== 'cancel') {
|
||||||
|
if (!err?._messageShown) {
|
||||||
|
useMessage().error(err?.msg || '取消失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 教室公物
|
||||||
|
const handleAssets = (row: any) => {
|
||||||
|
assetsDialogRef.value?.openDialog(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 门锁密码
|
||||||
|
const handlePassword = (row: any) => {
|
||||||
|
passwordDialogRef.value?.openDialog(row)
|
||||||
|
}
|
||||||
|
|
||||||
// 获取系部列表
|
// 获取系部列表
|
||||||
const getDeptListData = async () => {
|
const getDeptListData = async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
156
src/views/stuwork/classroombase/password.vue
Normal file
156
src/views/stuwork/classroombase/password.vue
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
title="门锁密码"
|
||||||
|
v-model="visible"
|
||||||
|
:width="500"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
draggable>
|
||||||
|
<el-form
|
||||||
|
ref="dataFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="dataRules"
|
||||||
|
label-width="120px"
|
||||||
|
v-loading="loading">
|
||||||
|
<el-row :gutter="24">
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="楼号">
|
||||||
|
<el-input
|
||||||
|
v-model="form.buildingNo"
|
||||||
|
placeholder="楼号"
|
||||||
|
disabled />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="教室位置">
|
||||||
|
<el-input
|
||||||
|
v-model="form.position"
|
||||||
|
placeholder="教室位置"
|
||||||
|
disabled />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="门锁密码" prop="password">
|
||||||
|
<el-input
|
||||||
|
v-model="form.password"
|
||||||
|
type="password"
|
||||||
|
show-password
|
||||||
|
placeholder="请输入门锁密码"
|
||||||
|
clearable />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="visible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="onSubmit" :disabled="loading">确 认</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="ClassroomPasswordDialog">
|
||||||
|
import { ref, reactive, nextTick } from 'vue'
|
||||||
|
import { useMessage } from '/@/hooks/message'
|
||||||
|
import { editAssets } from '/@/api/stuwork/classassets'
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
|
// 定义变量内容
|
||||||
|
const dataFormRef = ref()
|
||||||
|
const visible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
// 提交表单数据
|
||||||
|
const form = reactive({
|
||||||
|
id: '',
|
||||||
|
buildingNo: '',
|
||||||
|
deptName: '',
|
||||||
|
deptCode: '',
|
||||||
|
classCode: '',
|
||||||
|
classNo: '',
|
||||||
|
position: '',
|
||||||
|
password: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 定义校验规则
|
||||||
|
const dataRules = {
|
||||||
|
password: [
|
||||||
|
{ required: true, message: '请输入门锁密码', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
const openDialog = async (row: any) => {
|
||||||
|
visible.value = true
|
||||||
|
resetForm()
|
||||||
|
|
||||||
|
// 填充现有数据
|
||||||
|
if (row) {
|
||||||
|
form.id = row.id || ''
|
||||||
|
form.buildingNo = row.buildingNo || ''
|
||||||
|
form.deptName = row.deptName || ''
|
||||||
|
form.deptCode = row.deptCode || ''
|
||||||
|
form.classCode = row.classCode || ''
|
||||||
|
form.classNo = row.classNo || ''
|
||||||
|
form.position = row.position || ''
|
||||||
|
form.password = row.password || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
dataFormRef.value?.clearValidate()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
const resetForm = () => {
|
||||||
|
form.id = ''
|
||||||
|
form.buildingNo = ''
|
||||||
|
form.deptName = ''
|
||||||
|
form.deptCode = ''
|
||||||
|
form.classCode = ''
|
||||||
|
form.classNo = ''
|
||||||
|
form.position = ''
|
||||||
|
form.password = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const onSubmit = async () => {
|
||||||
|
if (!dataFormRef.value) return
|
||||||
|
|
||||||
|
await dataFormRef.value.validate(async (valid: boolean) => {
|
||||||
|
if (!valid) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
// 只提交必要的字段用于修改门锁密码
|
||||||
|
const submitData = {
|
||||||
|
id: form.id,
|
||||||
|
buildingNo: form.buildingNo,
|
||||||
|
deptName: form.deptName,
|
||||||
|
deptCode: form.deptCode,
|
||||||
|
classCode: form.classCode,
|
||||||
|
classNo: form.classNo,
|
||||||
|
position: form.position,
|
||||||
|
password: form.password
|
||||||
|
}
|
||||||
|
await editAssets(submitData)
|
||||||
|
useMessage().success('保存成功')
|
||||||
|
visible.value = false
|
||||||
|
emit('refresh')
|
||||||
|
} catch (_err) {
|
||||||
|
// 错误由 request 拦截器统一提示
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暴露方法
|
||||||
|
defineExpose({
|
||||||
|
openDialog
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
</style>
|
||||||
@@ -495,7 +495,9 @@ const confirmCheck = async () => {
|
|||||||
checkDialogVisible.value = false
|
checkDialogVisible.value = false
|
||||||
getDataList()
|
getDataList()
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
useMessage().error(err.msg || '考核失败')
|
if (!err?._messageShown) {
|
||||||
|
useMessage().error(err?.msg || '考核失败')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,7 +514,9 @@ const handleDelete = async (ids: string[]) => {
|
|||||||
getDataList()
|
getDataList()
|
||||||
useMessage().success('删除成功')
|
useMessage().success('删除成功')
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
useMessage().error(err.msg || '删除失败')
|
if (!err?._messageShown) {
|
||||||
|
useMessage().error(err?.msg || '删除失败')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
216
src/views/stuwork/dormsignrecord/form.vue
Normal file
216
src/views/stuwork/dormsignrecord/form.vue
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
title="新增宿舍点名"
|
||||||
|
v-model="visible"
|
||||||
|
:width="600"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
draggable>
|
||||||
|
<el-form
|
||||||
|
ref="dataFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="dataRules"
|
||||||
|
label-width="100px"
|
||||||
|
v-loading="loading">
|
||||||
|
<el-row :gutter="24">
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="点名类型" prop="type">
|
||||||
|
<el-select
|
||||||
|
v-model="form.type"
|
||||||
|
placeholder="请选择点名类型"
|
||||||
|
clearable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option label="普通住宿点名" value="1" />
|
||||||
|
<el-option label="留宿点名" value="2" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="楼号" prop="buildId">
|
||||||
|
<el-select
|
||||||
|
v-model="form.buildId"
|
||||||
|
placeholder="请选择楼号"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in buildingList"
|
||||||
|
:key="item.buildingNo"
|
||||||
|
:label="item.buildingNo"
|
||||||
|
:value="item.buildingNo" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="房间号" prop="roomNo">
|
||||||
|
<el-input
|
||||||
|
v-model="form.roomNo"
|
||||||
|
placeholder="请输入房间号"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="学生列表" prop="list">
|
||||||
|
<el-input
|
||||||
|
v-model="studentNosInput"
|
||||||
|
type="textarea"
|
||||||
|
:rows="5"
|
||||||
|
placeholder="请输入学号,多个学号用逗号或换行分隔"
|
||||||
|
style="width: 100%" />
|
||||||
|
<div class="form-tip">提示:多个学号可用逗号或换行分隔</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="visible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="onSubmit" :disabled="loading">确 认</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="DormSignRecordFormDialog">
|
||||||
|
import { ref, reactive, nextTick, computed, onMounted } from 'vue'
|
||||||
|
import { useMessage } from '/@/hooks/message'
|
||||||
|
import { addObj } from '/@/api/stuwork/dormsignrecord'
|
||||||
|
import { getBuildingList } from '/@/api/stuwork/dormbuilding'
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
|
// 定义变量内容
|
||||||
|
const dataFormRef = ref()
|
||||||
|
const visible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const buildingList = ref<any[]>([])
|
||||||
|
|
||||||
|
// 提交表单数据
|
||||||
|
const form = reactive({
|
||||||
|
type: '',
|
||||||
|
buildId: '',
|
||||||
|
roomNo: '',
|
||||||
|
list: [] as Array<{ stuNo: string }>
|
||||||
|
})
|
||||||
|
|
||||||
|
// 学生学号输入(用于显示和编辑)
|
||||||
|
const studentNosInput = ref('')
|
||||||
|
|
||||||
|
// 定义校验规则
|
||||||
|
const dataRules = {
|
||||||
|
type: [
|
||||||
|
{ required: true, message: '请选择点名类型', trigger: 'change' }
|
||||||
|
],
|
||||||
|
buildId: [
|
||||||
|
{ required: true, message: '请选择楼号', trigger: 'change' }
|
||||||
|
],
|
||||||
|
roomNo: [
|
||||||
|
{ required: true, message: '请输入房间号', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
list: [
|
||||||
|
{ required: true, message: '请输入至少一个学号', trigger: 'change' },
|
||||||
|
{
|
||||||
|
validator: (rule: any, value: any, callback: any) => {
|
||||||
|
if (!value || value.length === 0) {
|
||||||
|
callback(new Error('请输入至少一个学号'))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: 'change'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
const openDialog = async () => {
|
||||||
|
visible.value = true
|
||||||
|
|
||||||
|
// 重置表单数据
|
||||||
|
nextTick(() => {
|
||||||
|
dataFormRef.value?.resetFields()
|
||||||
|
form.type = ''
|
||||||
|
form.buildId = ''
|
||||||
|
form.roomNo = ''
|
||||||
|
form.list = []
|
||||||
|
studentNosInput.value = ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析学号输入(支持逗号和换行分隔)
|
||||||
|
const parseStudentNos = (input: string): string[] => {
|
||||||
|
if (!input || !input.trim()) return []
|
||||||
|
return input
|
||||||
|
.split(/[,\n]/)
|
||||||
|
.map(s => s.trim())
|
||||||
|
.filter(s => s.length > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const onSubmit = async () => {
|
||||||
|
if (!dataFormRef.value) return
|
||||||
|
|
||||||
|
// 解析学号列表
|
||||||
|
const stuNos = parseStudentNos(studentNosInput.value)
|
||||||
|
if (stuNos.length === 0) {
|
||||||
|
useMessage().warning('请输入至少一个学号')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
form.list = stuNos.map(stuNo => ({ stuNo }))
|
||||||
|
|
||||||
|
await dataFormRef.value.validate(async (valid: boolean) => {
|
||||||
|
if (!valid) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
await addObj({
|
||||||
|
type: form.type,
|
||||||
|
buildId: form.buildId,
|
||||||
|
roomNo: form.roomNo,
|
||||||
|
list: form.list
|
||||||
|
})
|
||||||
|
useMessage().success('新增成功')
|
||||||
|
visible.value = false
|
||||||
|
emit('refresh')
|
||||||
|
} catch (err: any) {
|
||||||
|
if (!err?._messageShown) {
|
||||||
|
useMessage().error(err?.msg || '新增失败')
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取楼号列表
|
||||||
|
const getBuildingListData = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getBuildingList()
|
||||||
|
buildingList.value = res?.data && Array.isArray(res.data) ? res.data : []
|
||||||
|
} catch (err) {
|
||||||
|
buildingList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
getBuildingListData()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 暴露方法
|
||||||
|
defineExpose({
|
||||||
|
openDialog
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.mb20 {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-tip {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
367
src/views/stuwork/dormsignrecord/index.vue
Normal file
367
src/views/stuwork/dormsignrecord/index.vue
Normal file
@@ -0,0 +1,367 @@
|
|||||||
|
<template>
|
||||||
|
<div class="modern-page-container">
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<!-- 搜索表单卡片 -->
|
||||||
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
:model="state.queryForm"
|
||||||
|
ref="searchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
@keyup.enter="getDataList"
|
||||||
|
class="search-form">
|
||||||
|
<el-form-item label="学院" prop="deptCode">
|
||||||
|
<el-select
|
||||||
|
v-model="state.queryForm.deptCode"
|
||||||
|
placeholder="请选择学院"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 200px">
|
||||||
|
<el-option
|
||||||
|
v-for="item in deptList"
|
||||||
|
:key="item.deptCode"
|
||||||
|
:label="item.deptName"
|
||||||
|
:value="item.deptCode">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="楼号" prop="buildNo">
|
||||||
|
<el-select
|
||||||
|
v-model="state.queryForm.buildNo"
|
||||||
|
placeholder="请选择楼号"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 200px">
|
||||||
|
<el-option
|
||||||
|
v-for="item in buildingList"
|
||||||
|
:key="item.buildingNo"
|
||||||
|
:label="item.buildingNo"
|
||||||
|
:value="item.buildingNo">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="房间号" prop="roomNo">
|
||||||
|
<el-input
|
||||||
|
v-model="state.queryForm.roomNo"
|
||||||
|
placeholder="请输入房间号"
|
||||||
|
clearable
|
||||||
|
style="width: 200px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="学号" prop="stuNo">
|
||||||
|
<el-input
|
||||||
|
v-model="state.queryForm.stuNo"
|
||||||
|
placeholder="请输入学号"
|
||||||
|
clearable
|
||||||
|
style="width: 200px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="点名类型" prop="type">
|
||||||
|
<el-select
|
||||||
|
v-model="state.queryForm.type"
|
||||||
|
placeholder="请选择点名类型"
|
||||||
|
clearable
|
||||||
|
style="width: 200px">
|
||||||
|
<el-option label="普通住宿点名" value="1" />
|
||||||
|
<el-option label="留宿点名" value="2" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="考勤状态" prop="sign">
|
||||||
|
<el-select
|
||||||
|
v-model="state.queryForm.sign"
|
||||||
|
placeholder="请选择考勤状态"
|
||||||
|
clearable
|
||||||
|
style="width: 200px">
|
||||||
|
<el-option label="未到" :value="0" />
|
||||||
|
<el-option label="已到" :value="1" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="考勤日期" prop="date">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="state.queryForm.date"
|
||||||
|
type="date"
|
||||||
|
placeholder="请选择考勤日期"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
clearable
|
||||||
|
style="width: 200px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" plain icon="Search" @click="getDataList">查询</el-button>
|
||||||
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 内容卡片 -->
|
||||||
|
<el-card class="content-card" shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
|
宿舍点名管理列表
|
||||||
|
</span>
|
||||||
|
<div class="header-actions">
|
||||||
|
<el-button
|
||||||
|
icon="FolderAdd"
|
||||||
|
type="primary"
|
||||||
|
@click="formDialogRef.openDialog()">
|
||||||
|
新增点名
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="RefreshLeft"
|
||||||
|
type="warning"
|
||||||
|
class="ml10"
|
||||||
|
@click="handleInit">
|
||||||
|
初始化
|
||||||
|
</el-button>
|
||||||
|
<right-toolbar
|
||||||
|
v-model:showSearch="showSearch"
|
||||||
|
class="ml10"
|
||||||
|
@queryTable="getDataList">
|
||||||
|
<TableColumnControl
|
||||||
|
ref="columnControlRef"
|
||||||
|
:columns="tableColumns"
|
||||||
|
v-model="visibleColumns"
|
||||||
|
trigger-type="default"
|
||||||
|
trigger-circle
|
||||||
|
@change="handleColumnChange"
|
||||||
|
@order-change="handleColumnOrderChange"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
|
<el-button circle style="margin-left: 0;">
|
||||||
|
<el-icon><Menu /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格 -->
|
||||||
|
<el-table
|
||||||
|
:data="state.dataList"
|
||||||
|
v-loading="state.loading"
|
||||||
|
stripe
|
||||||
|
:cell-style="tableStyle.cellStyle"
|
||||||
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
|
class="modern-table">
|
||||||
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><List /></el-icon>
|
||||||
|
</template>
|
||||||
|
<template #default="{ $index }">
|
||||||
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
|
<el-table-column
|
||||||
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
:width="col.width"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
||||||
|
:align="col.align || 'center'">
|
||||||
|
<template #header>
|
||||||
|
<el-icon v-if="col.icon"><component :is="col.icon" /></el-icon>
|
||||||
|
<span :style="{ marginLeft: col.icon ? '4px' : '0' }">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 点名类型列特殊模板 -->
|
||||||
|
<template v-if="col.prop === 'type'" #default="scope">
|
||||||
|
<el-tag size="small" :type="scope.row.type === '1' ? 'primary' : 'success'" effect="plain">
|
||||||
|
{{ scope.row.type === '1' ? '普通住宿点名' : '留宿点名' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 考勤状态列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'sign'" #default="scope">
|
||||||
|
<el-tag size="small" :type="scope.row.sign === '1' || scope.row.sign === 1 ? 'success' : 'danger'" effect="plain">
|
||||||
|
{{ scope.row.sign === '1' || scope.row.sign === 1 ? '已到' : '未到' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 是否扫过脸列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'isFace'" #default="scope">
|
||||||
|
<el-tag size="small" :type="scope.row.isFace === '1' || scope.row.isFace === 1 ? 'success' : 'info'" effect="plain">
|
||||||
|
{{ scope.row.isFace === '1' || scope.row.isFace === 1 ? '是' : '否' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 是否请假列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'isApply'" #default="scope">
|
||||||
|
<el-tag size="small" :type="scope.row.isApply === '1' || scope.row.isApply === 1 ? 'warning' : 'info'" effect="plain">
|
||||||
|
{{ scope.row.isApply === '1' || scope.row.isApply === 1 ? '是' : '否' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 新增点名弹窗 -->
|
||||||
|
<form-dialog ref="formDialogRef" @refresh="getDataList" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="DormSignRecord">
|
||||||
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
|
import { fetchList, initDormStuInfoForAttendance } from "/@/api/stuwork/dormsignrecord";
|
||||||
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
|
import { getDeptList } from "/@/api/basic/basicclass";
|
||||||
|
import { getBuildingList } from "/@/api/stuwork/dormbuilding";
|
||||||
|
import FormDialog from './form.vue'
|
||||||
|
import { Search, Document, List, Menu, RefreshLeft, OfficeBuilding, Grid, User, CreditCard, House, Check, Calendar, UserFilled } from '@element-plus/icons-vue'
|
||||||
|
import RightToolbar from '/@/components/RightToolbar/index.vue'
|
||||||
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const formDialogRef = ref()
|
||||||
|
const showSearch = ref(true)
|
||||||
|
const searchFormRef = ref()
|
||||||
|
const columnControlRef = ref<any>()
|
||||||
|
const deptList = ref<any[]>([])
|
||||||
|
const buildingList = ref<any[]>([])
|
||||||
|
|
||||||
|
// 表格列配置
|
||||||
|
const tableColumns = [
|
||||||
|
{ prop: 'deptName', label: '学院', minWidth: 120, icon: OfficeBuilding },
|
||||||
|
{ prop: 'className', label: '班级', minWidth: 120, icon: Grid },
|
||||||
|
{ prop: 'stuName', label: '姓名', width: 100, icon: User },
|
||||||
|
{ prop: 'stuNo', label: '学号', width: 120, icon: CreditCard },
|
||||||
|
{ prop: 'roomNo', label: '房间号', width: 100, icon: House },
|
||||||
|
{ prop: 'buildId', label: '楼号', width: 80, icon: OfficeBuilding },
|
||||||
|
{ prop: 'type', label: '点名类型', width: 120, icon: Document },
|
||||||
|
{ prop: 'sign', label: '考勤状态', width: 100, icon: Check },
|
||||||
|
{ prop: 'date', label: '考勤日期', width: 120, icon: Calendar },
|
||||||
|
{ prop: 'isFace', label: '是否扫过脸', width: 100, icon: UserFilled },
|
||||||
|
{ prop: 'isApply', label: '是否请假', width: 100, icon: Document }
|
||||||
|
]
|
||||||
|
|
||||||
|
// 使用表格列控制
|
||||||
|
const {
|
||||||
|
visibleColumns,
|
||||||
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
|
handleColumnChange,
|
||||||
|
handleColumnOrderChange,
|
||||||
|
loadSavedConfig
|
||||||
|
} = useTableColumnControl(tableColumns, { storageKey: route.path })
|
||||||
|
|
||||||
|
// 立即加载配置
|
||||||
|
loadSavedConfig()
|
||||||
|
|
||||||
|
// 配置 useTable
|
||||||
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
|
queryForm: {
|
||||||
|
deptCode: '',
|
||||||
|
buildNo: '',
|
||||||
|
roomNo: '',
|
||||||
|
stuNo: '',
|
||||||
|
type: '',
|
||||||
|
sign: '',
|
||||||
|
date: ''
|
||||||
|
},
|
||||||
|
pageList: async (params: any) => {
|
||||||
|
const res = await fetchList(params)
|
||||||
|
const data = res?.data
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
records: data?.records ?? [],
|
||||||
|
total: data?.total ?? 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
item: 'records',
|
||||||
|
totalCount: 'total'
|
||||||
|
},
|
||||||
|
createdIsNeed: true,
|
||||||
|
isPage: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// table hook
|
||||||
|
const {
|
||||||
|
getDataList,
|
||||||
|
currentChangeHandle,
|
||||||
|
sizeChangeHandle,
|
||||||
|
tableStyle
|
||||||
|
} = useTable(state)
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const handleReset = () => {
|
||||||
|
searchFormRef.value?.resetFields()
|
||||||
|
state.queryForm.deptCode = ''
|
||||||
|
state.queryForm.buildNo = ''
|
||||||
|
state.queryForm.roomNo = ''
|
||||||
|
state.queryForm.stuNo = ''
|
||||||
|
state.queryForm.type = ''
|
||||||
|
state.queryForm.sign = ''
|
||||||
|
state.queryForm.date = ''
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
const handleInit = async () => {
|
||||||
|
const { confirm } = useMessageBox()
|
||||||
|
try {
|
||||||
|
await confirm('确定要初始化宿舍学生信息用于考勤吗?')
|
||||||
|
await initDormStuInfoForAttendance()
|
||||||
|
useMessage().success('初始化成功')
|
||||||
|
getDataList()
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err !== 'cancel') {
|
||||||
|
if (!err?._messageShown) {
|
||||||
|
useMessage().error(err?.msg || '初始化失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取学院列表
|
||||||
|
const getDeptListData = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getDeptList()
|
||||||
|
deptList.value = res?.data && Array.isArray(res.data) ? res.data : []
|
||||||
|
} catch (err) {
|
||||||
|
deptList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取楼号列表
|
||||||
|
const getBuildingListData = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getBuildingList()
|
||||||
|
buildingList.value = res?.data && Array.isArray(res.data) ? res.data : []
|
||||||
|
} catch (err) {
|
||||||
|
buildingList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
getDeptListData()
|
||||||
|
getBuildingListData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
@@ -184,11 +184,24 @@ const TimeRuleTableComponent = defineComponent({
|
|||||||
default: ({ row }: any) => {
|
default: ({ row }: any) => {
|
||||||
const timeList = getTimeList(row)
|
const timeList = getTimeList(row)
|
||||||
return h('div', { class: 'time-slots-container' },
|
return h('div', { class: 'time-slots-container' },
|
||||||
timeList.map((timeSlot: any, index: number) =>
|
timeList.map((timeSlot: any, index: number) => {
|
||||||
h('div', { key: index, class: 'time-slot-item' }, [
|
// 通过数组索引更新,确保响应式
|
||||||
|
const updateStartTime = (val: string) => {
|
||||||
|
const list = getTimeList(row)
|
||||||
|
if (list[index]) {
|
||||||
|
list[index].startTime = val || ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const updateEndTime = (val: string) => {
|
||||||
|
const list = getTimeList(row)
|
||||||
|
if (list[index]) {
|
||||||
|
list[index].endTime = val || ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return h('div', { key: index, class: 'time-slot-item' }, [
|
||||||
h(ElTimePicker, {
|
h(ElTimePicker, {
|
||||||
modelValue: timeSlot.startTime,
|
modelValue: timeSlot.startTime || '',
|
||||||
'onUpdate:modelValue': (val: string) => { timeSlot.startTime = val },
|
'onUpdate:modelValue': updateStartTime,
|
||||||
format: 'HH:mm',
|
format: 'HH:mm',
|
||||||
valueFormat: 'HH:mm',
|
valueFormat: 'HH:mm',
|
||||||
placeholder: '开始时间',
|
placeholder: '开始时间',
|
||||||
@@ -196,8 +209,8 @@ const TimeRuleTableComponent = defineComponent({
|
|||||||
}),
|
}),
|
||||||
h('span', { style: { margin: '0 10px' } }, '至'),
|
h('span', { style: { margin: '0 10px' } }, '至'),
|
||||||
h(ElTimePicker, {
|
h(ElTimePicker, {
|
||||||
modelValue: timeSlot.endTime,
|
modelValue: timeSlot.endTime || '',
|
||||||
'onUpdate:modelValue': (val: string) => { timeSlot.endTime = val },
|
'onUpdate:modelValue': updateEndTime,
|
||||||
format: 'HH:mm',
|
format: 'HH:mm',
|
||||||
valueFormat: 'HH:mm',
|
valueFormat: 'HH:mm',
|
||||||
placeholder: '结束时间',
|
placeholder: '结束时间',
|
||||||
@@ -213,7 +226,7 @@ const TimeRuleTableComponent = defineComponent({
|
|||||||
style: { marginLeft: '10px' }
|
style: { marginLeft: '10px' }
|
||||||
})
|
})
|
||||||
])
|
])
|
||||||
)
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|||||||
144
src/views/stuwork/filemanager/folder.vue
Normal file
144
src/views/stuwork/filemanager/folder.vue
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:title="form.id ? '编辑文件夹' : '新建文件夹'"
|
||||||
|
v-model="visible"
|
||||||
|
:width="500"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
draggable>
|
||||||
|
<el-form
|
||||||
|
ref="dataFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="dataRules"
|
||||||
|
label-width="100px"
|
||||||
|
v-loading="loading">
|
||||||
|
<el-row :gutter="24">
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="文件夹名称" prop="classification">
|
||||||
|
<el-input
|
||||||
|
v-model="form.classification"
|
||||||
|
placeholder="请输入文件夹名称"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="visible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="onSubmit" :disabled="loading">确 认</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="FileManagerFolderDialog">
|
||||||
|
import { ref, reactive, nextTick } from 'vue'
|
||||||
|
import { useMessage } from '/@/hooks/message'
|
||||||
|
import { editFile, getDetail } from '/@/api/stuwork/filemanager'
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
|
// 定义变量内容
|
||||||
|
const dataFormRef = ref()
|
||||||
|
const visible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const operType = ref('add') // add 或 edit
|
||||||
|
|
||||||
|
// 提交表单数据
|
||||||
|
const form = reactive({
|
||||||
|
id: '',
|
||||||
|
classification: '',
|
||||||
|
parentId: null as string | null
|
||||||
|
})
|
||||||
|
|
||||||
|
// 定义校验规则
|
||||||
|
const dataRules = {
|
||||||
|
classification: [
|
||||||
|
{ required: true, message: '请输入文件夹名称', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
const openDialog = async (type: string = 'add', row?: any) => {
|
||||||
|
visible.value = true
|
||||||
|
operType.value = type
|
||||||
|
|
||||||
|
// 重置表单数据
|
||||||
|
nextTick(() => {
|
||||||
|
dataFormRef.value?.resetFields()
|
||||||
|
form.id = ''
|
||||||
|
form.classification = ''
|
||||||
|
form.parentId = row?.parentId ?? null
|
||||||
|
|
||||||
|
// 编辑时填充数据
|
||||||
|
if (type === 'edit' && row) {
|
||||||
|
form.id = row.id
|
||||||
|
form.classification = row.classification || ''
|
||||||
|
form.parentId = row.parentId || null
|
||||||
|
|
||||||
|
// 如果需要获取详情
|
||||||
|
if (row.id && !row.classification) {
|
||||||
|
loading.value = true
|
||||||
|
getDetail(row.id).then((res: any) => {
|
||||||
|
if (res.data) {
|
||||||
|
form.classification = res.data.classification || ''
|
||||||
|
form.parentId = res.data.parentId || form.parentId
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单(新增文件夹需要通过新增文件接口,但只传 classification 和 parentId)
|
||||||
|
const onSubmit = async () => {
|
||||||
|
if (!dataFormRef.value) return
|
||||||
|
|
||||||
|
await dataFormRef.value.validate(async (valid: boolean) => {
|
||||||
|
if (!valid) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
if (operType.value === 'add') {
|
||||||
|
// 新增文件夹:使用 addObj,但只传 classification 和 parentId
|
||||||
|
const { addObj } = await import('/@/api/stuwork/filemanager')
|
||||||
|
await addObj({
|
||||||
|
classification: form.classification,
|
||||||
|
parentId: form.parentId || '-1',
|
||||||
|
level: '0' // 文件夹层级为 0
|
||||||
|
})
|
||||||
|
useMessage().success('新建成功')
|
||||||
|
} else {
|
||||||
|
// 编辑文件夹:使用 editFile 接口
|
||||||
|
await editFile({
|
||||||
|
id: form.id,
|
||||||
|
parentId: form.parentId || '-1',
|
||||||
|
classification: form.classification
|
||||||
|
})
|
||||||
|
useMessage().success('编辑成功')
|
||||||
|
}
|
||||||
|
visible.value = false
|
||||||
|
emit('refresh')
|
||||||
|
} catch (err: any) {
|
||||||
|
if (!err?._messageShown) {
|
||||||
|
useMessage().error(err?.msg || (operType.value === 'add' ? '新建失败' : '编辑失败'))
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暴露方法
|
||||||
|
defineExpose({
|
||||||
|
openDialog
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.mb20 {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
181
src/views/stuwork/filemanager/form.vue
Normal file
181
src/views/stuwork/filemanager/form.vue
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:title="form.id ? '编辑文件' : '上传文件'"
|
||||||
|
v-model="visible"
|
||||||
|
:width="600"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
draggable>
|
||||||
|
<el-form
|
||||||
|
ref="dataFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="dataRules"
|
||||||
|
label-width="100px"
|
||||||
|
v-loading="loading">
|
||||||
|
<el-row :gutter="24">
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="文件名称" prop="fileName">
|
||||||
|
<el-input
|
||||||
|
v-model="form.fileName"
|
||||||
|
placeholder="请输入文件名称"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="文件上传" prop="fileUrl">
|
||||||
|
<Upload
|
||||||
|
v-model="form.fileUrl"
|
||||||
|
:limit="1"
|
||||||
|
uploadFileUrl="/stuwork/file/upload"
|
||||||
|
type="default" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="备注" prop="remarks">
|
||||||
|
<el-input
|
||||||
|
v-model="form.remarks"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
placeholder="请输入备注"
|
||||||
|
:maxlength="250"
|
||||||
|
show-word-limit
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="visible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="onSubmit" :disabled="loading">确 认</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="FileManagerFormDialog">
|
||||||
|
import { ref, reactive, nextTick } from 'vue'
|
||||||
|
import { useMessage } from '/@/hooks/message'
|
||||||
|
import { addObj, editObj, getDetail } from '/@/api/stuwork/filemanager'
|
||||||
|
import Upload from '/@/components/Upload/index.vue'
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
|
// 定义变量内容
|
||||||
|
const dataFormRef = ref()
|
||||||
|
const visible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const operType = ref('add') // add 或 edit
|
||||||
|
|
||||||
|
// 提交表单数据
|
||||||
|
const form = reactive({
|
||||||
|
id: '',
|
||||||
|
fileName: '',
|
||||||
|
fileUrl: '',
|
||||||
|
remarks: '',
|
||||||
|
parentId: null as string | null,
|
||||||
|
level: '1'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 定义校验规则
|
||||||
|
const dataRules = {
|
||||||
|
fileName: [
|
||||||
|
{ required: true, message: '请输入文件名称', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
fileUrl: [
|
||||||
|
{ required: true, message: '请上传文件', trigger: 'change' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
const openDialog = async (type: string = 'add', row?: any) => {
|
||||||
|
visible.value = true
|
||||||
|
operType.value = type
|
||||||
|
|
||||||
|
// 重置表单数据
|
||||||
|
nextTick(() => {
|
||||||
|
dataFormRef.value?.resetFields()
|
||||||
|
form.id = ''
|
||||||
|
form.fileName = ''
|
||||||
|
form.fileUrl = ''
|
||||||
|
form.remarks = ''
|
||||||
|
form.parentId = row?.parentId ?? null
|
||||||
|
form.level = row?.level || '1'
|
||||||
|
|
||||||
|
// 编辑时填充数据
|
||||||
|
if (type === 'edit' && row) {
|
||||||
|
form.id = row.id
|
||||||
|
form.fileName = row.fileName || ''
|
||||||
|
form.fileUrl = row.fileUrl || ''
|
||||||
|
form.remarks = row.remarks || ''
|
||||||
|
form.parentId = row.parentId || null
|
||||||
|
form.level = row.level || '1'
|
||||||
|
|
||||||
|
// 如果需要获取详情
|
||||||
|
if (row.id && !row.fileUrl) {
|
||||||
|
loading.value = true
|
||||||
|
getDetail(row.id).then((res: any) => {
|
||||||
|
if (res.data) {
|
||||||
|
form.fileName = res.data.fileName || form.fileName
|
||||||
|
form.fileUrl = res.data.fileUrl || ''
|
||||||
|
form.remarks = res.data.remarks || ''
|
||||||
|
form.parentId = res.data.parentId || form.parentId
|
||||||
|
form.level = res.data.level || form.level
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const onSubmit = async () => {
|
||||||
|
if (!dataFormRef.value) return
|
||||||
|
|
||||||
|
await dataFormRef.value.validate(async (valid: boolean) => {
|
||||||
|
if (!valid) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const submitData = {
|
||||||
|
fileName: form.fileName,
|
||||||
|
fileUrl: form.fileUrl,
|
||||||
|
remarks: form.remarks,
|
||||||
|
parentId: form.parentId || '-1',
|
||||||
|
level: form.level
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operType.value === 'add') {
|
||||||
|
await addObj(submitData)
|
||||||
|
useMessage().success('新增成功')
|
||||||
|
} else {
|
||||||
|
await editObj({
|
||||||
|
id: form.id,
|
||||||
|
...submitData
|
||||||
|
})
|
||||||
|
useMessage().success('编辑成功')
|
||||||
|
}
|
||||||
|
visible.value = false
|
||||||
|
emit('refresh')
|
||||||
|
} catch (err: any) {
|
||||||
|
if (!err?._messageShown) {
|
||||||
|
useMessage().error(err?.msg || (operType.value === 'add' ? '新增失败' : '编辑失败'))
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暴露方法
|
||||||
|
defineExpose({
|
||||||
|
openDialog
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.mb20 {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
345
src/views/stuwork/filemanager/index.vue
Normal file
345
src/views/stuwork/filemanager/index.vue
Normal file
@@ -0,0 +1,345 @@
|
|||||||
|
<template>
|
||||||
|
<div class="modern-page-container">
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<!-- 内容卡片 -->
|
||||||
|
<el-card class="content-card" shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
|
学工文件管理
|
||||||
|
</span>
|
||||||
|
<div class="header-actions">
|
||||||
|
<el-button
|
||||||
|
icon="FolderAdd"
|
||||||
|
type="primary"
|
||||||
|
@click="handleAddFolder">
|
||||||
|
新建文件夹
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="FolderAdd"
|
||||||
|
type="success"
|
||||||
|
class="ml10"
|
||||||
|
@click="handleAddFile">
|
||||||
|
上传文件
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Refresh"
|
||||||
|
class="ml10"
|
||||||
|
@click="getDataList">
|
||||||
|
刷新
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 面包屑导航 -->
|
||||||
|
<div class="breadcrumb-wrapper mb16" v-if="breadcrumbList.length > 0">
|
||||||
|
<el-breadcrumb separator="/">
|
||||||
|
<el-breadcrumb-item>
|
||||||
|
<el-button link @click="handleBreadcrumbClick(null)">
|
||||||
|
<el-icon><Folder /></el-icon>
|
||||||
|
根目录
|
||||||
|
</el-button>
|
||||||
|
</el-breadcrumb-item>
|
||||||
|
<el-breadcrumb-item v-for="(item, index) in breadcrumbList" :key="item.id">
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
@click="handleBreadcrumbClick(item)"
|
||||||
|
:disabled="index === breadcrumbList.length - 1">
|
||||||
|
{{ item.classification || item.fileName }}
|
||||||
|
</el-button>
|
||||||
|
</el-breadcrumb-item>
|
||||||
|
</el-breadcrumb>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表格 -->
|
||||||
|
<el-table
|
||||||
|
:data="state.dataList"
|
||||||
|
v-loading="state.loading"
|
||||||
|
stripe
|
||||||
|
:cell-style="tableStyle.cellStyle"
|
||||||
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
|
class="modern-table">
|
||||||
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><List /></el-icon>
|
||||||
|
</template>
|
||||||
|
<template #default="{ $index }">
|
||||||
|
{{ $index + 1 }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="classification" label="文件夹名称" min-width="200" show-overflow-tooltip>
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Folder /></el-icon>
|
||||||
|
<span style="margin-left: 4px">文件夹名称</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row.classification">{{ scope.row.classification }}</span>
|
||||||
|
<span v-else class="text-gray-400">-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="fileName" label="文件名称" min-width="200" show-overflow-tooltip>
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Document /></el-icon>
|
||||||
|
<span style="margin-left: 4px">文件名称</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row.fileName">{{ scope.row.fileName }}</span>
|
||||||
|
<span v-else class="text-gray-400">-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="level" label="层级" width="80" align="center">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Grid /></el-icon>
|
||||||
|
<span style="margin-left: 4px">层级</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="remarks" label="备注" min-width="150" show-overflow-tooltip>
|
||||||
|
<template #header>
|
||||||
|
<el-icon><EditPen /></el-icon>
|
||||||
|
<span style="margin-left: 4px">备注</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<span v-if="scope.row.remarks">{{ scope.row.remarks }}</span>
|
||||||
|
<span v-else class="text-gray-400">-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="createTime" label="创建时间" width="180" align="center">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Clock /></el-icon>
|
||||||
|
<span style="margin-left: 4px">创建时间</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="250" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<!-- 文件夹:进入、编辑、删除 -->
|
||||||
|
<template v-if="scope.row.classification">
|
||||||
|
<el-button
|
||||||
|
icon="FolderOpened"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleEnterFolder(scope.row)">
|
||||||
|
进入
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Edit"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleEditFolder(scope.row)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<!-- 文件:下载、编辑、删除 -->
|
||||||
|
<template v-else>
|
||||||
|
<el-button
|
||||||
|
icon="Download"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleDownload(scope.row)">
|
||||||
|
下载
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Edit"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleEditFile(scope.row)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 新增/编辑文件弹窗 -->
|
||||||
|
<form-dialog ref="formDialogRef" @refresh="getDataList" />
|
||||||
|
|
||||||
|
<!-- 新增/编辑文件夹弹窗 -->
|
||||||
|
<folder-dialog ref="folderDialogRef" @refresh="getDataList" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="FileManager">
|
||||||
|
import { reactive, ref, onMounted, computed } from 'vue'
|
||||||
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
|
import { fetchList, delObj } from "/@/api/stuwork/filemanager";
|
||||||
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
|
import FormDialog from './form.vue'
|
||||||
|
import FolderDialog from './folder.vue'
|
||||||
|
import { List, Document, Folder, Grid, EditPen, Clock, Setting, FolderOpened, Download } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
// 定义变量内容
|
||||||
|
const formDialogRef = ref()
|
||||||
|
const folderDialogRef = ref()
|
||||||
|
const currentParentId = ref<string | null>(null)
|
||||||
|
const breadcrumbList = ref<any[]>([])
|
||||||
|
|
||||||
|
// 配置 useTable(接口返回数组,非分页结构)
|
||||||
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
|
queryForm: {
|
||||||
|
parentId: null as string | null
|
||||||
|
},
|
||||||
|
pageList: async (params: any) => {
|
||||||
|
const res = await fetchList(params)
|
||||||
|
// 数据链是 data.data.records,需要正确解析
|
||||||
|
let list: any[] = []
|
||||||
|
if (res?.data) {
|
||||||
|
// 如果 res.data 是数组,直接使用
|
||||||
|
if (Array.isArray(res.data)) {
|
||||||
|
list = res.data
|
||||||
|
}
|
||||||
|
// 如果 res.data.data 存在且是对象,尝试取 records
|
||||||
|
else if (res.data.data && res.data.data.records && Array.isArray(res.data.data.records)) {
|
||||||
|
list = res.data.data.records
|
||||||
|
}
|
||||||
|
// 如果 res.data.records 存在且是数组,直接使用
|
||||||
|
else if (res.data.records && Array.isArray(res.data.records)) {
|
||||||
|
list = res.data.records
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
data: list // isPage: false 时,直接返回数组
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
item: 'records',
|
||||||
|
totalCount: 'total'
|
||||||
|
},
|
||||||
|
createdIsNeed: true,
|
||||||
|
isPage: false // 不分页
|
||||||
|
})
|
||||||
|
|
||||||
|
// table hook
|
||||||
|
const {
|
||||||
|
getDataList,
|
||||||
|
tableStyle
|
||||||
|
} = useTable(state)
|
||||||
|
|
||||||
|
// 进入文件夹
|
||||||
|
const handleEnterFolder = (row: any) => {
|
||||||
|
currentParentId.value = row.id
|
||||||
|
breadcrumbList.value.push(row)
|
||||||
|
state.queryForm.parentId = row.id
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 面包屑点击
|
||||||
|
const handleBreadcrumbClick = (item: any | null) => {
|
||||||
|
if (item === null) {
|
||||||
|
// 返回根目录
|
||||||
|
currentParentId.value = null
|
||||||
|
breadcrumbList.value = []
|
||||||
|
state.queryForm.parentId = null
|
||||||
|
} else {
|
||||||
|
// 返回指定目录
|
||||||
|
const index = breadcrumbList.value.findIndex(b => b.id === item.id)
|
||||||
|
if (index >= 0) {
|
||||||
|
breadcrumbList.value = breadcrumbList.value.slice(0, index + 1)
|
||||||
|
currentParentId.value = item.id
|
||||||
|
state.queryForm.parentId = item.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增文件夹
|
||||||
|
const handleAddFolder = () => {
|
||||||
|
folderDialogRef.value?.openDialog('add', { parentId: currentParentId.value })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增文件
|
||||||
|
const handleAddFile = () => {
|
||||||
|
formDialogRef.value?.openDialog('add', { parentId: currentParentId.value })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑文件夹
|
||||||
|
const handleEditFolder = (row: any) => {
|
||||||
|
folderDialogRef.value?.openDialog('edit', row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑文件
|
||||||
|
const handleEditFile = (row: any) => {
|
||||||
|
formDialogRef.value?.openDialog('edit', row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载文件
|
||||||
|
const handleDownload = (row: any) => {
|
||||||
|
if (!row.fileUrl) {
|
||||||
|
useMessage().warning('文件地址无效')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// fileUrl 可能是完整 URL 或相对路径
|
||||||
|
if (row.fileUrl.startsWith('http://') || row.fileUrl.startsWith('https://')) {
|
||||||
|
window.open(row.fileUrl, '_blank')
|
||||||
|
} else {
|
||||||
|
// 相对路径,需要拼接 baseURL
|
||||||
|
const baseURL = import.meta.env.VITE_API_URL || ''
|
||||||
|
const url = baseURL + (row.fileUrl.startsWith('/') ? row.fileUrl : '/' + row.fileUrl)
|
||||||
|
window.open(url, '_blank')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const handleDelete = async (row: any) => {
|
||||||
|
const { confirm } = useMessageBox()
|
||||||
|
try {
|
||||||
|
const name = row.classification || row.fileName || '该项'
|
||||||
|
await confirm(`确定要删除${name}吗?`)
|
||||||
|
await delObj([row.id])
|
||||||
|
useMessage().success('删除成功')
|
||||||
|
getDataList()
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err !== 'cancel') {
|
||||||
|
useMessage().error(err?.msg || '删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
getDataList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
|
||||||
|
.breadcrumb-wrapper {
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
:deep(.el-breadcrumb) {
|
||||||
|
.el-breadcrumb__item {
|
||||||
|
.el-button {
|
||||||
|
padding: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb16 {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
253
src/views/stuwork/moralplan/form.vue
Normal file
253
src/views/stuwork/moralplan/form.vue
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:title="form.id ? '编辑' : '新增'"
|
||||||
|
v-model="visible"
|
||||||
|
:width="800"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
draggable>
|
||||||
|
<el-form
|
||||||
|
ref="dataFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="dataRules"
|
||||||
|
label-width="100px"
|
||||||
|
v-loading="loading">
|
||||||
|
<el-row :gutter="24">
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
|
<el-select
|
||||||
|
v-model="form.schoolYear"
|
||||||
|
placeholder="请选择学年"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in schoolYearList"
|
||||||
|
:key="item.year"
|
||||||
|
:label="item.year"
|
||||||
|
:value="item.year" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="学期" prop="schoolTerm">
|
||||||
|
<el-select
|
||||||
|
v-model="form.schoolTerm"
|
||||||
|
placeholder="请选择学期"
|
||||||
|
clearable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in schoolTermList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="标题" prop="title">
|
||||||
|
<el-input
|
||||||
|
v-model="form.title"
|
||||||
|
placeholder="请输入标题"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="作者" prop="author">
|
||||||
|
<el-input
|
||||||
|
v-model="form.author"
|
||||||
|
placeholder="请输入作者"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="内容" prop="content">
|
||||||
|
<Editor
|
||||||
|
v-model:getHtml="form.content"
|
||||||
|
:height="'400'"
|
||||||
|
placeholder="请输入内容" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="visible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="onSubmit" :disabled="loading">确 认</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="MoralPlanFormDialog">
|
||||||
|
import { ref, reactive, nextTick, onMounted } from 'vue'
|
||||||
|
import { useMessage } from '/@/hooks/message'
|
||||||
|
import { addObj, editObj, getDetail } from '/@/api/stuwork/moralplan'
|
||||||
|
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
|
||||||
|
import { getDicts } from '/@/api/admin/dict'
|
||||||
|
import Editor from '/@/components/Editor/index.vue'
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
|
// 定义变量内容
|
||||||
|
const dataFormRef = ref()
|
||||||
|
const visible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const operType = ref('add') // add 或 edit
|
||||||
|
const schoolYearList = ref<any[]>([])
|
||||||
|
const schoolTermList = ref<any[]>([])
|
||||||
|
|
||||||
|
// 提交表单数据
|
||||||
|
const form = reactive({
|
||||||
|
id: '',
|
||||||
|
schoolYear: '',
|
||||||
|
schoolTerm: '',
|
||||||
|
title: '',
|
||||||
|
content: '',
|
||||||
|
author: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 定义校验规则
|
||||||
|
const dataRules = {
|
||||||
|
schoolYear: [
|
||||||
|
{ required: true, message: '请选择学年', trigger: 'change' }
|
||||||
|
],
|
||||||
|
schoolTerm: [
|
||||||
|
{ required: true, message: '请选择学期', trigger: 'change' }
|
||||||
|
],
|
||||||
|
title: [
|
||||||
|
{ required: true, message: '请输入标题', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
content: [
|
||||||
|
{ required: true, message: '请输入内容', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
author: [
|
||||||
|
{ required: true, message: '请输入作者', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
const openDialog = async (type: string = 'add', row?: any) => {
|
||||||
|
visible.value = true
|
||||||
|
operType.value = type
|
||||||
|
|
||||||
|
// 重置表单数据
|
||||||
|
nextTick(() => {
|
||||||
|
dataFormRef.value?.resetFields()
|
||||||
|
form.id = ''
|
||||||
|
form.schoolYear = ''
|
||||||
|
form.schoolTerm = ''
|
||||||
|
form.title = ''
|
||||||
|
form.content = ''
|
||||||
|
form.author = ''
|
||||||
|
|
||||||
|
// 编辑时填充数据
|
||||||
|
if (type === 'edit' && row) {
|
||||||
|
form.id = row.id
|
||||||
|
form.schoolYear = row.schoolYear || ''
|
||||||
|
form.schoolTerm = row.schoolTerm || ''
|
||||||
|
form.title = row.title || ''
|
||||||
|
form.content = row.content || ''
|
||||||
|
form.author = row.author || ''
|
||||||
|
|
||||||
|
// 如果需要获取详情
|
||||||
|
if (row.id && !row.content) {
|
||||||
|
loading.value = true
|
||||||
|
getDetail(row.id).then((res: any) => {
|
||||||
|
if (res.data) {
|
||||||
|
form.schoolYear = res.data.schoolYear || form.schoolYear
|
||||||
|
form.schoolTerm = res.data.schoolTerm || form.schoolTerm
|
||||||
|
form.title = res.data.title || ''
|
||||||
|
form.content = res.data.content || ''
|
||||||
|
form.author = res.data.author || ''
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 学年列表(班级管理-学年接口)
|
||||||
|
const getSchoolYearList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await queryAllSchoolYear()
|
||||||
|
schoolYearList.value = res?.data && Array.isArray(res.data) ? res.data : []
|
||||||
|
} catch (err) {
|
||||||
|
schoolYearList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 学期字典(系统通用)
|
||||||
|
const getSchoolTermDict = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getDicts('school_term')
|
||||||
|
if (res?.data && Array.isArray(res.data)) {
|
||||||
|
schoolTermList.value = res.data.map((item: any) => ({
|
||||||
|
label: item.label ?? item.dictLabel ?? item.name,
|
||||||
|
value: item.value ?? item.dictValue ?? item.code
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
schoolTermList.value = []
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
schoolTermList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const onSubmit = async () => {
|
||||||
|
if (!dataFormRef.value) return
|
||||||
|
|
||||||
|
await dataFormRef.value.validate(async (valid: boolean) => {
|
||||||
|
if (!valid) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const submitData = {
|
||||||
|
schoolYear: form.schoolYear,
|
||||||
|
schoolTerm: form.schoolTerm,
|
||||||
|
title: form.title,
|
||||||
|
content: form.content,
|
||||||
|
author: form.author
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operType.value === 'add') {
|
||||||
|
await addObj(submitData)
|
||||||
|
useMessage().success('新增成功')
|
||||||
|
} else {
|
||||||
|
await editObj({
|
||||||
|
id: form.id,
|
||||||
|
...submitData
|
||||||
|
})
|
||||||
|
useMessage().success('编辑成功')
|
||||||
|
}
|
||||||
|
visible.value = false
|
||||||
|
emit('refresh')
|
||||||
|
} catch (err: any) {
|
||||||
|
if (!err?._messageShown) {
|
||||||
|
useMessage().error(err?.msg || (operType.value === 'add' ? '新增失败' : '编辑失败'))
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化:加载学年、学期
|
||||||
|
onMounted(() => {
|
||||||
|
getSchoolYearList()
|
||||||
|
getSchoolTermDict()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 暴露方法
|
||||||
|
defineExpose({
|
||||||
|
openDialog
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.mb20 {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
356
src/views/stuwork/moralplan/index.vue
Normal file
356
src/views/stuwork/moralplan/index.vue
Normal file
@@ -0,0 +1,356 @@
|
|||||||
|
<template>
|
||||||
|
<div class="modern-page-container">
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<!-- 搜索表单卡片 -->
|
||||||
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
:model="state.queryForm"
|
||||||
|
ref="searchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
@keyup.enter="getDataList"
|
||||||
|
class="search-form">
|
||||||
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
|
<el-select
|
||||||
|
v-model="state.queryForm.schoolYear"
|
||||||
|
placeholder="请选择学年"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 200px">
|
||||||
|
<el-option
|
||||||
|
v-for="item in schoolYearList"
|
||||||
|
:key="item.year"
|
||||||
|
:label="item.year"
|
||||||
|
:value="item.year">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="学期" prop="schoolTerm">
|
||||||
|
<el-select
|
||||||
|
v-model="state.queryForm.schoolTerm"
|
||||||
|
placeholder="请选择学期"
|
||||||
|
clearable
|
||||||
|
style="width: 200px">
|
||||||
|
<el-option
|
||||||
|
v-for="item in schoolTermList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="标题" prop="title">
|
||||||
|
<el-input
|
||||||
|
v-model="state.queryForm.title"
|
||||||
|
placeholder="请输入标题"
|
||||||
|
clearable
|
||||||
|
style="width: 200px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="内容" prop="content">
|
||||||
|
<el-input
|
||||||
|
v-model="state.queryForm.content"
|
||||||
|
placeholder="请输入内容"
|
||||||
|
clearable
|
||||||
|
style="width: 200px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="作者" prop="author">
|
||||||
|
<el-input
|
||||||
|
v-model="state.queryForm.author"
|
||||||
|
placeholder="请输入作者"
|
||||||
|
clearable
|
||||||
|
style="width: 200px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" plain icon="Search" @click="getDataList">查询</el-button>
|
||||||
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 内容卡片 -->
|
||||||
|
<el-card class="content-card" shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
|
德育计划列表
|
||||||
|
</span>
|
||||||
|
<div class="header-actions">
|
||||||
|
<el-button
|
||||||
|
icon="FolderAdd"
|
||||||
|
type="primary"
|
||||||
|
@click="formDialogRef.openDialog()">
|
||||||
|
新增
|
||||||
|
</el-button>
|
||||||
|
<right-toolbar
|
||||||
|
v-model:showSearch="showSearch"
|
||||||
|
class="ml10"
|
||||||
|
@queryTable="getDataList">
|
||||||
|
<TableColumnControl
|
||||||
|
ref="columnControlRef"
|
||||||
|
:columns="tableColumns"
|
||||||
|
v-model="visibleColumns"
|
||||||
|
trigger-type="default"
|
||||||
|
trigger-circle
|
||||||
|
@change="handleColumnChange"
|
||||||
|
@order-change="handleColumnOrderChange"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
|
<el-button circle style="margin-left: 0;">
|
||||||
|
<el-icon><Menu /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格 -->
|
||||||
|
<el-table
|
||||||
|
:data="state.dataList"
|
||||||
|
v-loading="state.loading"
|
||||||
|
stripe
|
||||||
|
:cell-style="tableStyle.cellStyle"
|
||||||
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
|
class="modern-table">
|
||||||
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><List /></el-icon>
|
||||||
|
</template>
|
||||||
|
<template #default="{ $index }">
|
||||||
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
|
<el-table-column
|
||||||
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
:width="col.width"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
||||||
|
:align="col.align || 'center'">
|
||||||
|
<template #header>
|
||||||
|
<el-icon v-if="col.icon"><component :is="col.icon" /></el-icon>
|
||||||
|
<span :style="{ marginLeft: col.icon ? '4px' : '0' }">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 学期列特殊模板 -->
|
||||||
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
|
{{ formatSchoolTerm(scope.row.schoolTerm) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 内容列特殊模板(可能较长,截断显示) -->
|
||||||
|
<template v-else-if="col.prop === 'content'" #default="scope">
|
||||||
|
<span :title="scope.row.content">{{ scope.row.content ? (scope.row.content.length > 50 ? scope.row.content.substring(0, 50) + '...' : scope.row.content) : '-' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="Edit"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleEdit(scope.row)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
|
<form-dialog ref="formDialogRef" @refresh="getDataList" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="MoralPlan">
|
||||||
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
|
import { fetchList, delObj } from "/@/api/stuwork/moralplan";
|
||||||
|
import { queryAllSchoolYear } from "/@/api/basic/basicyear";
|
||||||
|
import { getDicts } from "/@/api/admin/dict";
|
||||||
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
|
import FormDialog from './form.vue'
|
||||||
|
import { List, Calendar, Clock, Document, User, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
|
// 定义变量内容
|
||||||
|
const route = useRoute()
|
||||||
|
const formDialogRef = ref()
|
||||||
|
const columnControlRef = ref<any>()
|
||||||
|
const searchFormRef = ref()
|
||||||
|
const showSearch = ref(true)
|
||||||
|
const schoolYearList = ref<any[]>([])
|
||||||
|
const schoolTermList = ref<any[]>([])
|
||||||
|
|
||||||
|
// 表格列配置
|
||||||
|
const tableColumns = [
|
||||||
|
{ prop: 'schoolYear', label: '学年' },
|
||||||
|
{ prop: 'schoolTerm', label: '学期' },
|
||||||
|
{ prop: 'title', label: '标题', minWidth: 200 },
|
||||||
|
{ prop: 'content', label: '内容', minWidth: 250 },
|
||||||
|
{ prop: 'author', label: '作者' },
|
||||||
|
{ prop: 'createTime', label: '创建时间', width: 180 },
|
||||||
|
{ prop: 'updateTime', label: '更新时间', width: 180 }
|
||||||
|
]
|
||||||
|
|
||||||
|
// 列配置映射(用于图标)
|
||||||
|
const columnConfigMap: Record<string, { icon: any }> = {
|
||||||
|
schoolYear: { icon: Calendar },
|
||||||
|
schoolTerm: { icon: Clock },
|
||||||
|
title: { icon: Document },
|
||||||
|
content: { icon: Document },
|
||||||
|
author: { icon: User },
|
||||||
|
createTime: { icon: Clock },
|
||||||
|
updateTime: { icon: Clock }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用表格列控制hook
|
||||||
|
const {
|
||||||
|
visibleColumns,
|
||||||
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
|
handleColumnChange,
|
||||||
|
handleColumnOrderChange
|
||||||
|
} = useTableColumnControl(tableColumns, { storageKey: route.path })
|
||||||
|
|
||||||
|
// 配置 useTable
|
||||||
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
|
queryForm: {
|
||||||
|
schoolYear: '',
|
||||||
|
schoolTerm: '',
|
||||||
|
title: '',
|
||||||
|
content: '',
|
||||||
|
author: ''
|
||||||
|
},
|
||||||
|
pageList: fetchList,
|
||||||
|
props: {
|
||||||
|
item: 'records',
|
||||||
|
totalCount: 'total'
|
||||||
|
},
|
||||||
|
createdIsNeed: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// table hook
|
||||||
|
const {
|
||||||
|
getDataList,
|
||||||
|
currentChangeHandle,
|
||||||
|
sizeChangeHandle,
|
||||||
|
tableStyle
|
||||||
|
} = useTable(state)
|
||||||
|
|
||||||
|
// 格式化学期
|
||||||
|
const formatSchoolTerm = (value: string | number) => {
|
||||||
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
const dictItem = schoolTermList.value.find(item => item.value == value)
|
||||||
|
return dictItem ? dictItem.label : value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const handleReset = () => {
|
||||||
|
searchFormRef.value?.resetFields()
|
||||||
|
state.queryForm.schoolYear = ''
|
||||||
|
state.queryForm.schoolTerm = ''
|
||||||
|
state.queryForm.title = ''
|
||||||
|
state.queryForm.content = ''
|
||||||
|
state.queryForm.author = ''
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
const handleEdit = (row: any) => {
|
||||||
|
formDialogRef.value?.openDialog('edit', row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const handleDelete = async (row: any) => {
|
||||||
|
const { confirm } = useMessageBox()
|
||||||
|
try {
|
||||||
|
await confirm('确定要删除该德育计划吗?')
|
||||||
|
await delObj([row.id])
|
||||||
|
useMessage().success('删除成功')
|
||||||
|
getDataList()
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err !== 'cancel') {
|
||||||
|
useMessage().error(err.msg || '删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取学年列表
|
||||||
|
const getSchoolYearList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await queryAllSchoolYear()
|
||||||
|
if (res.data && Array.isArray(res.data)) {
|
||||||
|
schoolYearList.value = res.data
|
||||||
|
} else {
|
||||||
|
schoolYearList.value = []
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
schoolYearList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取学期字典
|
||||||
|
const getSchoolTermDict = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getDicts('school_term')
|
||||||
|
if (res.data && Array.isArray(res.data)) {
|
||||||
|
schoolTermList.value = res.data.map((item: any) => ({
|
||||||
|
label: item.label || item.dictLabel || item.name,
|
||||||
|
value: item.value || item.dictValue || item.code
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
schoolTermList.value = []
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
schoolTermList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
getSchoolYearList()
|
||||||
|
getSchoolTermDict()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
@@ -10,7 +10,8 @@
|
|||||||
:model="form"
|
:model="form"
|
||||||
:rules="dataRules"
|
:rules="dataRules"
|
||||||
label-width="120px"
|
label-width="120px"
|
||||||
v-loading="loading">
|
v-loading="loading"
|
||||||
|
:key="form.turnoverType">
|
||||||
<el-row :gutter="24">
|
<el-row :gutter="24">
|
||||||
<el-col :span="12" class="mb20">
|
<el-col :span="12" class="mb20">
|
||||||
<el-form-item label="学年" prop="schoolYear">
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
@@ -47,7 +48,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col :span="12" class="mb20">
|
<el-col :span="12" class="mb20" v-if="!isDropoutType">
|
||||||
<el-form-item label="原班级" prop="oldClassCode">
|
<el-form-item label="原班级" prop="oldClassCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="form.oldClassCode"
|
v-model="form.oldClassCode"
|
||||||
@@ -66,7 +67,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col :span="12" class="mb20">
|
<el-col :span="12" class="mb20" v-if="!isDropoutType">
|
||||||
<el-form-item label="现班级" prop="newClassCode">
|
<el-form-item label="现班级" prop="newClassCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="form.newClassCode"
|
v-model="form.newClassCode"
|
||||||
@@ -90,7 +91,8 @@
|
|||||||
v-model="form.turnoverType"
|
v-model="form.turnoverType"
|
||||||
placeholder="请选择异动类型"
|
placeholder="请选择异动类型"
|
||||||
clearable
|
clearable
|
||||||
style="width: 100%">
|
style="width: 100%"
|
||||||
|
@change="handleTurnoverTypeChange">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in turnoverTypeList"
|
v-for="item in turnoverTypeList"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
@@ -299,30 +301,52 @@ const selectedStudentsText = computed(() => {
|
|||||||
return `已选择 ${form.selectedStudents.length} 名学生`
|
return `已选择 ${form.selectedStudents.length} 名学生`
|
||||||
})
|
})
|
||||||
|
|
||||||
// 定义校验规则
|
// 判断是否为退学类型
|
||||||
const dataRules = {
|
const isDropoutType = computed(() => {
|
||||||
schoolYear: [
|
if (!form.turnoverType) return false
|
||||||
{ required: true, message: '请选择学年', trigger: 'change' }
|
// 查找异动类型字典中 label 包含"退学"的项
|
||||||
],
|
const dropoutItem = turnoverTypeList.value.find((item: any) => {
|
||||||
schoolTerm: [
|
const label = item.label || item.dictLabel || item.name || ''
|
||||||
{ required: true, message: '请选择学期', trigger: 'change' }
|
return label.includes('退学')
|
||||||
],
|
})
|
||||||
oldClassCode: [
|
if (dropoutItem) {
|
||||||
{ required: true, message: '请选择原班级', trigger: 'change' }
|
return dropoutItem.value === form.turnoverType || dropoutItem.dictValue === form.turnoverType || dropoutItem.code === form.turnoverType
|
||||||
],
|
}
|
||||||
newClassCode: [
|
return false
|
||||||
{ required: true, message: '请选择现班级', trigger: 'change' }
|
})
|
||||||
],
|
|
||||||
turnoverType: [
|
// 定义校验规则(动态)
|
||||||
{ required: true, message: '请选择异动类型', trigger: 'change' }
|
const dataRules = computed(() => {
|
||||||
],
|
const rules: any = {
|
||||||
turnoverDate: [
|
schoolYear: [
|
||||||
{ required: true, message: '请选择异动时间', trigger: 'change' }
|
{ required: true, message: '请选择学年', trigger: 'change' }
|
||||||
],
|
],
|
||||||
selectedStudents: [
|
schoolTerm: [
|
||||||
{ required: true, message: '请至少选择一个学生', trigger: 'change', type: 'array', min: 1 }
|
{ required: true, message: '请选择学期', trigger: 'change' }
|
||||||
]
|
],
|
||||||
}
|
turnoverType: [
|
||||||
|
{ required: true, message: '请选择异动类型', trigger: 'change' }
|
||||||
|
],
|
||||||
|
turnoverDate: [
|
||||||
|
{ required: true, message: '请选择异动时间', trigger: 'change' }
|
||||||
|
],
|
||||||
|
selectedStudents: [
|
||||||
|
{ required: true, message: '请至少选择一个学生', trigger: 'change', type: 'array', min: 1 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果不是退学类型,原班级和现班级为必填
|
||||||
|
if (!isDropoutType.value) {
|
||||||
|
rules.oldClassCode = [
|
||||||
|
{ required: true, message: '请选择原班级', trigger: 'change' }
|
||||||
|
]
|
||||||
|
rules.newClassCode = [
|
||||||
|
{ required: true, message: '请选择现班级', trigger: 'change' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return rules
|
||||||
|
})
|
||||||
|
|
||||||
// 原班级变化时,更新学生搜索条件
|
// 原班级变化时,更新学生搜索条件
|
||||||
const handleOldClassChange = () => {
|
const handleOldClassChange = () => {
|
||||||
@@ -331,14 +355,30 @@ const handleOldClassChange = () => {
|
|||||||
form.selectedStudents = []
|
form.selectedStudents = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 异动类型变化时处理
|
||||||
|
const handleTurnoverTypeChange = () => {
|
||||||
|
// 如果切换为退学类型,清空原班级和现班级
|
||||||
|
if (isDropoutType.value) {
|
||||||
|
form.oldClassCode = ''
|
||||||
|
form.newClassCode = ''
|
||||||
|
// 清空已选学生(因为退学不需要原班级,学生选择逻辑会受影响)
|
||||||
|
form.selectedStudents = []
|
||||||
|
}
|
||||||
|
// 重新验证表单
|
||||||
|
nextTick(() => {
|
||||||
|
dataFormRef.value?.clearValidate(['oldClassCode', 'newClassCode'])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 打开学生选择弹窗
|
// 打开学生选择弹窗
|
||||||
const openStudentDialog = () => {
|
const openStudentDialog = () => {
|
||||||
if (!form.oldClassCode) {
|
// 如果是退学类型,不需要原班级
|
||||||
|
if (!isDropoutType.value && !form.oldClassCode) {
|
||||||
useMessage().warning('请先选择原班级')
|
useMessage().warning('请先选择原班级')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
studentDialogVisible.value = true
|
studentDialogVisible.value = true
|
||||||
studentSearchForm.classCode = form.oldClassCode
|
studentSearchForm.classCode = form.oldClassCode || ''
|
||||||
studentSearchForm.classNo = ''
|
studentSearchForm.classNo = ''
|
||||||
studentSearchForm.stuNo = ''
|
studentSearchForm.stuNo = ''
|
||||||
studentSearchForm.realName = ''
|
studentSearchForm.realName = ''
|
||||||
@@ -354,10 +394,13 @@ const handleStudentSearch = async () => {
|
|||||||
const params: any = {
|
const params: any = {
|
||||||
current: studentPagination.currentPage,
|
current: studentPagination.currentPage,
|
||||||
size: studentPagination.pageSize,
|
size: studentPagination.pageSize,
|
||||||
classCode: studentSearchForm.classCode || undefined,
|
|
||||||
stuNo: studentSearchForm.stuNo || undefined,
|
stuNo: studentSearchForm.stuNo || undefined,
|
||||||
realName: studentSearchForm.realName || undefined
|
realName: studentSearchForm.realName || undefined
|
||||||
}
|
}
|
||||||
|
// 如果不是退学类型且有原班级,才添加 classCode 条件
|
||||||
|
if (!isDropoutType.value && studentSearchForm.classCode) {
|
||||||
|
params.classCode = studentSearchForm.classCode
|
||||||
|
}
|
||||||
const res = await getStudentList(params)
|
const res = await getStudentList(params)
|
||||||
if (res.data && res.data.records) {
|
if (res.data && res.data.records) {
|
||||||
studentTableData.value = res.data.records
|
studentTableData.value = res.data.records
|
||||||
@@ -494,8 +537,6 @@ const onSubmit = async () => {
|
|||||||
const submitData: any = {
|
const submitData: any = {
|
||||||
schoolYear: form.schoolYear,
|
schoolYear: form.schoolYear,
|
||||||
schoolTerm: form.schoolTerm,
|
schoolTerm: form.schoolTerm,
|
||||||
oldClassCode: form.oldClassCode,
|
|
||||||
newClassCode: form.newClassCode,
|
|
||||||
turnoverType: form.turnoverType,
|
turnoverType: form.turnoverType,
|
||||||
turnYear: form.turnYear || '',
|
turnYear: form.turnYear || '',
|
||||||
turnoverDate: form.turnoverDate,
|
turnoverDate: form.turnoverDate,
|
||||||
@@ -506,6 +547,12 @@ const onSubmit = async () => {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果不是退学类型,才添加原班级和现班级
|
||||||
|
if (!isDropoutType.value) {
|
||||||
|
submitData.oldClassCode = form.oldClassCode
|
||||||
|
submitData.newClassCode = form.newClassCode
|
||||||
|
}
|
||||||
|
|
||||||
// 编辑时需要包含id和单个学生信息
|
// 编辑时需要包含id和单个学生信息
|
||||||
if (operType.value === 'edit') {
|
if (operType.value === 'edit') {
|
||||||
submitData.id = form.id
|
submitData.id = form.id
|
||||||
|
|||||||
253
src/views/stuwork/termactivity/form.vue
Normal file
253
src/views/stuwork/termactivity/form.vue
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:title="form.id ? '编辑' : '新增'"
|
||||||
|
v-model="visible"
|
||||||
|
:width="800"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
draggable>
|
||||||
|
<el-form
|
||||||
|
ref="dataFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="dataRules"
|
||||||
|
label-width="100px"
|
||||||
|
v-loading="loading">
|
||||||
|
<el-row :gutter="24">
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
|
<el-select
|
||||||
|
v-model="form.schoolYear"
|
||||||
|
placeholder="请选择学年"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in schoolYearList"
|
||||||
|
:key="item.year"
|
||||||
|
:label="item.year"
|
||||||
|
:value="item.year" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="学期" prop="schoolTerm">
|
||||||
|
<el-select
|
||||||
|
v-model="form.schoolTerm"
|
||||||
|
placeholder="请选择学期"
|
||||||
|
clearable
|
||||||
|
style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in schoolTermList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="标题" prop="title">
|
||||||
|
<el-input
|
||||||
|
v-model="form.title"
|
||||||
|
placeholder="请输入标题"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="作者" prop="author">
|
||||||
|
<el-input
|
||||||
|
v-model="form.author"
|
||||||
|
placeholder="请输入作者"
|
||||||
|
style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24" class="mb20">
|
||||||
|
<el-form-item label="内容" prop="content">
|
||||||
|
<Editor
|
||||||
|
v-model:getHtml="form.content"
|
||||||
|
:height="'400'"
|
||||||
|
placeholder="请输入内容" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="visible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="onSubmit" :disabled="loading">确 认</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="TermActivityFormDialog">
|
||||||
|
import { ref, reactive, nextTick, onMounted } from 'vue'
|
||||||
|
import { useMessage } from '/@/hooks/message'
|
||||||
|
import { addObj, editObj, getDetail } from '/@/api/stuwork/termactivity'
|
||||||
|
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
|
||||||
|
import { getDicts } from '/@/api/admin/dict'
|
||||||
|
import Editor from '/@/components/Editor/index.vue'
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
|
// 定义变量内容
|
||||||
|
const dataFormRef = ref()
|
||||||
|
const visible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const operType = ref('add') // add 或 edit
|
||||||
|
const schoolYearList = ref<any[]>([])
|
||||||
|
const schoolTermList = ref<any[]>([])
|
||||||
|
|
||||||
|
// 提交表单数据
|
||||||
|
const form = reactive({
|
||||||
|
id: '',
|
||||||
|
schoolYear: '',
|
||||||
|
schoolTerm: '',
|
||||||
|
title: '',
|
||||||
|
content: '',
|
||||||
|
author: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 定义校验规则
|
||||||
|
const dataRules = {
|
||||||
|
schoolYear: [
|
||||||
|
{ required: true, message: '请选择学年', trigger: 'change' }
|
||||||
|
],
|
||||||
|
schoolTerm: [
|
||||||
|
{ required: true, message: '请选择学期', trigger: 'change' }
|
||||||
|
],
|
||||||
|
title: [
|
||||||
|
{ required: true, message: '请输入标题', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
content: [
|
||||||
|
{ required: true, message: '请输入内容', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
author: [
|
||||||
|
{ required: true, message: '请输入作者', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
const openDialog = async (type: string = 'add', row?: any) => {
|
||||||
|
visible.value = true
|
||||||
|
operType.value = type
|
||||||
|
|
||||||
|
// 重置表单数据
|
||||||
|
nextTick(() => {
|
||||||
|
dataFormRef.value?.resetFields()
|
||||||
|
form.id = ''
|
||||||
|
form.schoolYear = ''
|
||||||
|
form.schoolTerm = ''
|
||||||
|
form.title = ''
|
||||||
|
form.content = ''
|
||||||
|
form.author = ''
|
||||||
|
|
||||||
|
// 编辑时填充数据
|
||||||
|
if (type === 'edit' && row) {
|
||||||
|
form.id = row.id
|
||||||
|
form.schoolYear = row.schoolYear || ''
|
||||||
|
form.schoolTerm = row.schoolTerm || ''
|
||||||
|
form.title = row.title || ''
|
||||||
|
form.content = row.content || ''
|
||||||
|
form.author = row.author || ''
|
||||||
|
|
||||||
|
// 如果需要获取详情
|
||||||
|
if (row.id && !row.content) {
|
||||||
|
loading.value = true
|
||||||
|
getDetail(row.id).then((res: any) => {
|
||||||
|
if (res.data) {
|
||||||
|
form.schoolYear = res.data.schoolYear || form.schoolYear
|
||||||
|
form.schoolTerm = res.data.schoolTerm || form.schoolTerm
|
||||||
|
form.title = res.data.title || ''
|
||||||
|
form.content = res.data.content || ''
|
||||||
|
form.author = res.data.author || ''
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 学年列表(班级管理-学年接口)
|
||||||
|
const getSchoolYearList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await queryAllSchoolYear()
|
||||||
|
schoolYearList.value = res?.data && Array.isArray(res.data) ? res.data : []
|
||||||
|
} catch (err) {
|
||||||
|
schoolYearList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 学期字典(系统通用)
|
||||||
|
const getSchoolTermDict = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getDicts('school_term')
|
||||||
|
if (res?.data && Array.isArray(res.data)) {
|
||||||
|
schoolTermList.value = res.data.map((item: any) => ({
|
||||||
|
label: item.label ?? item.dictLabel ?? item.name,
|
||||||
|
value: item.value ?? item.dictValue ?? item.code
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
schoolTermList.value = []
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
schoolTermList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const onSubmit = async () => {
|
||||||
|
if (!dataFormRef.value) return
|
||||||
|
|
||||||
|
await dataFormRef.value.validate(async (valid: boolean) => {
|
||||||
|
if (!valid) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const submitData = {
|
||||||
|
schoolYear: form.schoolYear,
|
||||||
|
schoolTerm: form.schoolTerm,
|
||||||
|
title: form.title,
|
||||||
|
content: form.content,
|
||||||
|
author: form.author
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operType.value === 'add') {
|
||||||
|
await addObj(submitData)
|
||||||
|
useMessage().success('新增成功')
|
||||||
|
} else {
|
||||||
|
await editObj({
|
||||||
|
id: form.id,
|
||||||
|
...submitData
|
||||||
|
})
|
||||||
|
useMessage().success('编辑成功')
|
||||||
|
}
|
||||||
|
visible.value = false
|
||||||
|
emit('refresh')
|
||||||
|
} catch (err: any) {
|
||||||
|
if (!err?._messageShown) {
|
||||||
|
useMessage().error(err?.msg || (operType.value === 'add' ? '新增失败' : '编辑失败'))
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化:加载学年、学期
|
||||||
|
onMounted(() => {
|
||||||
|
getSchoolYearList()
|
||||||
|
getSchoolTermDict()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 暴露方法
|
||||||
|
defineExpose({
|
||||||
|
openDialog
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.mb20 {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
366
src/views/stuwork/termactivity/index.vue
Normal file
366
src/views/stuwork/termactivity/index.vue
Normal file
@@ -0,0 +1,366 @@
|
|||||||
|
<template>
|
||||||
|
<div class="modern-page-container">
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<!-- 搜索表单卡片 -->
|
||||||
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
:model="state.queryForm"
|
||||||
|
ref="searchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
@keyup.enter="getDataList"
|
||||||
|
class="search-form">
|
||||||
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
|
<el-select
|
||||||
|
v-model="state.queryForm.schoolYear"
|
||||||
|
placeholder="请选择学年"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 200px">
|
||||||
|
<el-option
|
||||||
|
v-for="item in schoolYearList"
|
||||||
|
:key="item.year"
|
||||||
|
:label="item.year"
|
||||||
|
:value="item.year">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="学期" prop="schoolTerm">
|
||||||
|
<el-select
|
||||||
|
v-model="state.queryForm.schoolTerm"
|
||||||
|
placeholder="请选择学期"
|
||||||
|
clearable
|
||||||
|
style="width: 200px">
|
||||||
|
<el-option
|
||||||
|
v-for="item in schoolTermList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="标题" prop="title">
|
||||||
|
<el-input
|
||||||
|
v-model="state.queryForm.title"
|
||||||
|
placeholder="请输入标题"
|
||||||
|
clearable
|
||||||
|
style="width: 200px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="作者" prop="author">
|
||||||
|
<el-input
|
||||||
|
v-model="state.queryForm.author"
|
||||||
|
placeholder="请输入作者"
|
||||||
|
clearable
|
||||||
|
style="width: 200px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" plain icon="Search" @click="getDataList">查询</el-button>
|
||||||
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 内容卡片 -->
|
||||||
|
<el-card class="content-card" shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
|
学期活动列表
|
||||||
|
</span>
|
||||||
|
<div class="header-actions">
|
||||||
|
<el-button
|
||||||
|
icon="FolderAdd"
|
||||||
|
type="primary"
|
||||||
|
@click="formDialogRef.openDialog()">
|
||||||
|
新增
|
||||||
|
</el-button>
|
||||||
|
<right-toolbar
|
||||||
|
v-model:showSearch="showSearch"
|
||||||
|
class="ml10"
|
||||||
|
@queryTable="getDataList">
|
||||||
|
<TableColumnControl
|
||||||
|
ref="columnControlRef"
|
||||||
|
:columns="tableColumns"
|
||||||
|
v-model="visibleColumns"
|
||||||
|
trigger-type="default"
|
||||||
|
trigger-circle
|
||||||
|
@change="handleColumnChange"
|
||||||
|
@order-change="handleColumnOrderChange"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
|
<el-button circle style="margin-left: 0;">
|
||||||
|
<el-icon><Menu /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格 -->
|
||||||
|
<el-table
|
||||||
|
:data="state.dataList"
|
||||||
|
v-loading="state.loading"
|
||||||
|
stripe
|
||||||
|
:cell-style="tableStyle.cellStyle"
|
||||||
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
|
class="modern-table">
|
||||||
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><List /></el-icon>
|
||||||
|
</template>
|
||||||
|
<template #default="{ $index }">
|
||||||
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
|
<el-table-column
|
||||||
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
:width="col.width"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
||||||
|
:align="col.align || 'center'">
|
||||||
|
<template #header>
|
||||||
|
<el-icon v-if="col.icon"><component :is="col.icon" /></el-icon>
|
||||||
|
<span :style="{ marginLeft: col.icon ? '4px' : '0' }">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 学期列特殊模板 -->
|
||||||
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
|
{{ formatSchoolTerm(scope.row.schoolTerm) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 内容列特殊模板(截断显示) -->
|
||||||
|
<template v-else-if="col.prop === 'content'" #default="scope">
|
||||||
|
<el-tooltip
|
||||||
|
v-if="scope.row.content"
|
||||||
|
:content="scope.row.content.replace(/<[^>]*>/g, '')"
|
||||||
|
placement="top"
|
||||||
|
effect="dark">
|
||||||
|
<div class="content-preview" v-html="getContentPreview(scope.row.content)"></div>
|
||||||
|
</el-tooltip>
|
||||||
|
<span v-else class="text-gray-400">-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="180" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="Edit"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleEdit(scope.row)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 新增/编辑弹窗 -->
|
||||||
|
<form-dialog ref="formDialogRef" @refresh="getDataList" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="TermActivity">
|
||||||
|
import { reactive, ref, computed, onMounted } from 'vue'
|
||||||
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
|
import { fetchList, delObj } from "/@/api/stuwork/termactivity";
|
||||||
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
|
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
|
||||||
|
import { getDicts } from '/@/api/admin/dict'
|
||||||
|
import FormDialog from './form.vue'
|
||||||
|
import { Search, Document, List, Setting, Menu, Calendar, EditPen, User, Clock } from '@element-plus/icons-vue'
|
||||||
|
import RightToolbar from '/@/components/RightToolbar/index.vue'
|
||||||
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const formDialogRef = ref()
|
||||||
|
const showSearch = ref(true)
|
||||||
|
const searchFormRef = ref()
|
||||||
|
const schoolYearList = ref<any[]>([])
|
||||||
|
const schoolTermList = ref<any[]>([])
|
||||||
|
|
||||||
|
// 表格列配置
|
||||||
|
const tableColumns = [
|
||||||
|
{ prop: 'schoolYear', label: '学年', width: 120, icon: Calendar },
|
||||||
|
{ prop: 'schoolTerm', label: '学期', width: 100, icon: Calendar },
|
||||||
|
{ prop: 'title', label: '标题', minWidth: 200, icon: Document },
|
||||||
|
{ prop: 'content', label: '内容', minWidth: 300, icon: EditPen },
|
||||||
|
{ prop: 'author', label: '作者', width: 120, icon: User },
|
||||||
|
{ prop: 'createTime', label: '创建时间', width: 180, icon: Clock }
|
||||||
|
]
|
||||||
|
|
||||||
|
// 使用表格列控制
|
||||||
|
const {
|
||||||
|
visibleColumns,
|
||||||
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
|
handleColumnChange,
|
||||||
|
handleColumnOrderChange,
|
||||||
|
loadSavedConfig
|
||||||
|
} = useTableColumnControl(tableColumns, { storageKey: route.path })
|
||||||
|
|
||||||
|
// 立即加载配置,确保初始化时列可见
|
||||||
|
loadSavedConfig()
|
||||||
|
|
||||||
|
// 配置 useTable
|
||||||
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
|
queryForm: {
|
||||||
|
schoolYear: '',
|
||||||
|
schoolTerm: '',
|
||||||
|
title: '',
|
||||||
|
author: ''
|
||||||
|
},
|
||||||
|
pageList: async (params: any) => {
|
||||||
|
const res = await fetchList(params)
|
||||||
|
const data = res?.data
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
records: data?.records ?? [],
|
||||||
|
total: data?.total ?? 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
item: 'records',
|
||||||
|
totalCount: 'total'
|
||||||
|
},
|
||||||
|
createdIsNeed: true,
|
||||||
|
isPage: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// table hook
|
||||||
|
const {
|
||||||
|
getDataList,
|
||||||
|
currentChangeHandle,
|
||||||
|
sizeChangeHandle,
|
||||||
|
tableStyle
|
||||||
|
} = useTable(state)
|
||||||
|
|
||||||
|
// 格式化学期
|
||||||
|
const formatSchoolTerm = (value: string | number) => {
|
||||||
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
const dictItem = schoolTermList.value.find(item => item.value == value)
|
||||||
|
return dictItem ? dictItem.label : value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取内容预览(去除HTML标签,截断)
|
||||||
|
const getContentPreview = (content: string) => {
|
||||||
|
if (!content) return '-'
|
||||||
|
const text = content.replace(/<[^>]*>/g, '')
|
||||||
|
return text.length > 50 ? text.substring(0, 50) + '...' : text
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const handleReset = () => {
|
||||||
|
searchFormRef.value?.resetFields()
|
||||||
|
state.queryForm.schoolYear = ''
|
||||||
|
state.queryForm.schoolTerm = ''
|
||||||
|
state.queryForm.title = ''
|
||||||
|
state.queryForm.author = ''
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
const handleEdit = (row: any) => {
|
||||||
|
formDialogRef.value?.openDialog('edit', row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const handleDelete = async (row: any) => {
|
||||||
|
const { confirm } = useMessageBox()
|
||||||
|
try {
|
||||||
|
await confirm(`确定要删除"${row.title || '该项'}"吗?`)
|
||||||
|
await delObj([row.id])
|
||||||
|
useMessage().success('删除成功')
|
||||||
|
getDataList()
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err !== 'cancel') {
|
||||||
|
useMessage().error(err?.msg || '删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取学年列表
|
||||||
|
const getSchoolYearList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await queryAllSchoolYear()
|
||||||
|
schoolYearList.value = res?.data && Array.isArray(res.data) ? res.data : []
|
||||||
|
} catch (err) {
|
||||||
|
schoolYearList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取学期字典
|
||||||
|
const getSchoolTermDict = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getDicts('school_term')
|
||||||
|
if (res?.data && Array.isArray(res.data)) {
|
||||||
|
schoolTermList.value = res.data.map((item: any) => ({
|
||||||
|
label: item.label ?? item.dictLabel ?? item.name,
|
||||||
|
value: item.value ?? item.dictValue ?? item.code
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
schoolTermList.value = []
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
schoolTermList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
getSchoolYearList()
|
||||||
|
getSchoolTermDict()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
|
||||||
|
.content-preview {
|
||||||
|
max-width: 300px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user