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) => {
|
||||
return request({
|
||||
url: `/recruit/recruitstudentplan/deletById`,
|
||||
url: `/recruit/recruitstudentplan/deleteById`,
|
||||
method: 'post',
|
||||
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 计算最终显示的列
|
||||
*/
|
||||
const visibleColumnsSorted = computed(() => {
|
||||
// 如果 visibleColumns 为空,显示所有列(初始化时)
|
||||
if (visibleColumns.value.length === 0) {
|
||||
return tableColumns.filter(col => !col.alwaysShow && !col.fixed)
|
||||
}
|
||||
// 过滤出可见的列
|
||||
const columns = tableColumns.filter(col => {
|
||||
const key = col.prop || col.label || ''
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
</div>
|
||||
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
class="upload-demo"
|
||||
:action="uploadUrl"
|
||||
:headers="headers"
|
||||
@@ -34,7 +35,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import { ref, computed, nextTick } from 'vue';
|
||||
import { ElNotification } from 'element-plus';
|
||||
import { Download, UploadFilled } from '@element-plus/icons-vue';
|
||||
import { Session } from '/@/utils/storage';
|
||||
@@ -54,6 +55,7 @@ const headers = computed(() => {
|
||||
|
||||
const uploadUrl = ref('')
|
||||
const currentType = ref('')
|
||||
const uploadRef = ref<{ clearFiles?: () => void }>()
|
||||
const titleMap: Record<string, string> = {
|
||||
titleRelation: '职称信息导入',
|
||||
quaRelation: '职业资格信息导入',
|
||||
@@ -68,6 +70,9 @@ const init = (type: any) => {
|
||||
uploadUrl.value = '/professional/file/importTeacherOtherInfo?type=' + type
|
||||
title.value = titleMap[type] || '信息导入'
|
||||
visible.value = true
|
||||
nextTick(() => {
|
||||
uploadRef.value?.clearFiles()
|
||||
})
|
||||
}
|
||||
|
||||
// Emits
|
||||
|
||||
@@ -17,12 +17,14 @@
|
||||
<!-- </div>-->
|
||||
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
class="upload-demo"
|
||||
action="/professional/file/makeImportTeacherInfoSimpleTask"
|
||||
:headers="headers"
|
||||
:accept="'.xls,.xlsx'"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-error="handleAvatarError"
|
||||
:limit="1"
|
||||
drag>
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
@@ -38,7 +40,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { ref, computed, nextTick } from 'vue'
|
||||
import { ElNotification } from 'element-plus'
|
||||
import { Download, UploadFilled } from '@element-plus/icons-vue'
|
||||
import { Session } from '/@/utils/storage'
|
||||
@@ -53,9 +55,14 @@
|
||||
}
|
||||
})
|
||||
|
||||
const uploadRef = ref<{ clearFiles?: () => void }>()
|
||||
|
||||
// 方法
|
||||
const init = () => {
|
||||
visible.value = true
|
||||
nextTick(() => {
|
||||
uploadRef.value?.clearFiles?.()
|
||||
})
|
||||
}
|
||||
|
||||
const handleUploadSuccess = () => {
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
<template>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<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"
|
||||
:action="uploadUrl"
|
||||
:headers="headers"
|
||||
@@ -24,7 +37,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import { ref, computed, nextTick } from 'vue';
|
||||
import { ElNotification } from 'element-plus';
|
||||
import { Download, UploadFilled } from '@element-plus/icons-vue';
|
||||
import { Session } from '/@/utils/storage';
|
||||
@@ -44,20 +57,26 @@ const headers = computed(() => {
|
||||
|
||||
const uploadUrl = ref('')
|
||||
const currentType = ref('')
|
||||
const uploadRef = ref<{ clearFiles?: () => void }>()
|
||||
const titleMap: Record<string, string> = {
|
||||
planMajor: '计划专业导入'
|
||||
R10001: '计划专业导入',
|
||||
R10002: '地区分数导入',
|
||||
R10003: '中招平台数据导入'
|
||||
}
|
||||
// 方法
|
||||
const init = (type: any) => {
|
||||
currentType.value = type
|
||||
uploadUrl.value = '/professional/file/importTeacherOtherInfo?type=' + type
|
||||
uploadUrl.value = '/api/recruit/file/importRecruitInfo?type=' + type
|
||||
title.value = titleMap[type] || '信息导入'
|
||||
visible.value = true
|
||||
nextTick(() => {
|
||||
uploadRef.value?.clearFiles()
|
||||
})
|
||||
}
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits<{
|
||||
(e: 'refreshData'): void
|
||||
(e: 'refreshDataList'): void
|
||||
}>()
|
||||
|
||||
const handleUploadSuccess = () => {
|
||||
@@ -68,7 +87,7 @@ const handleUploadSuccess = () => {
|
||||
type: 'success',
|
||||
});
|
||||
|
||||
emit('refreshData')
|
||||
emit('refreshDataList')
|
||||
};
|
||||
|
||||
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-auth="'professional_teacherinfo_import'"
|
||||
v-auth="'recruit_major_import'"
|
||||
type="primary"
|
||||
plain
|
||||
icon="UploadFilled"
|
||||
@@ -124,7 +124,7 @@
|
||||
<!-- 弹窗, 新增 / 修改 -->
|
||||
<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>
|
||||
</template>
|
||||
@@ -283,7 +283,7 @@ const resetQuery = () => {
|
||||
const exportLoading = ref(false);
|
||||
|
||||
const handleImportDialog = () => {
|
||||
ImportRecruitInfoRef.value?.init("planMajor");
|
||||
ImportRecruitInfoRef.value?.init("R10001");
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -29,6 +29,15 @@
|
||||
>
|
||||
新 增
|
||||
</el-button>
|
||||
<el-button
|
||||
v-auth="'recruit_areascore_import'"
|
||||
type="primary"
|
||||
plain
|
||||
icon="UploadFilled"
|
||||
:loading="exportLoading"
|
||||
@click="handleImportDialog"
|
||||
>导入信息
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 表格 -->
|
||||
@@ -83,6 +92,9 @@
|
||||
|
||||
<!-- 弹窗, 新增 / 修改 -->
|
||||
<table-form ref="addOrUpdateRef" @refreshDataList="getDataList" />
|
||||
|
||||
<import-recruit-info ref="ImportRecruitInfoRef" @refreshDataList="getDataList"></import-recruit-info>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -94,6 +106,8 @@ import { BasicTableProps, useTable } from '/@/hooks/table'
|
||||
import { useMessage, useMessageBox } from '/@/hooks/message'
|
||||
import { getList } from '/@/api/recruit/recruitstudentplangroup'
|
||||
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 { hasAuth } = useAuth()
|
||||
@@ -183,6 +197,12 @@ const resetQuery = () => {
|
||||
getDataList()
|
||||
}
|
||||
|
||||
const exportLoading = ref(false);
|
||||
|
||||
const handleImportDialog = () => {
|
||||
ImportRecruitInfoRef.value?.init("R10002");
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
|
||||
@@ -281,19 +281,28 @@
|
||||
@click="handleAddData">新增
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="hasAuth('zipExport')"
|
||||
type="warning"
|
||||
v-auth="'recruit_zzpt_import'"
|
||||
type="primary"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="downZip()">招生名单打包导出
|
||||
</el-button>
|
||||
<el-button
|
||||
class="ml10"
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleExport()">名单导出
|
||||
icon="UploadFilled"
|
||||
:loading="exportLoading"
|
||||
@click="handleImportDialog"
|
||||
>导入中招平台数据
|
||||
</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>
|
||||
</el-row>
|
||||
|
||||
@@ -634,6 +643,8 @@
|
||||
<AdmissionNoticeDialog ref="admissionNoticeDialogRef" @refresh="getDataList"></AdmissionNoticeDialog>
|
||||
|
||||
<InterviewForm ref="interviewFormRef" @refresh="getDataList"></InterviewForm>
|
||||
|
||||
<import-recruit-info ref="ImportRecruitInfoRef" @refreshDataList="getDataList"></import-recruit-info>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -681,6 +692,8 @@ const InterviewForm = defineAsyncComponent(() => import('/@/views/recruit/recrui
|
||||
const PayQrcodeDialog = defineAsyncComponent(() => import('./PayQrcodeDialog.vue'))
|
||||
const AdmissionNoticeDialog = defineAsyncComponent(() => import('./AdmissionNoticeDialog.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()
|
||||
// 消息提示 hooks
|
||||
const message = useMessage()
|
||||
@@ -1131,6 +1144,10 @@ watch(() => dataForm.groupId, () => {
|
||||
}
|
||||
})
|
||||
|
||||
const handleImportDialog = () => {
|
||||
ImportRecruitInfoRef.value?.init("R10003");
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
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>
|
||||
</el-table-column>
|
||||
</template>
|
||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||
<el-table-column label="操作" width="380" align="center" fixed="right">
|
||||
<template #header>
|
||||
<el-icon><Setting /></el-icon>
|
||||
<span style="margin-left: 4px">操作</span>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="!scope.row.classCode || !scope.row.position"
|
||||
icon="Setting"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleArrange(scope.row)">
|
||||
教室安排
|
||||
</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>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
@@ -213,6 +237,10 @@
|
||||
|
||||
<!-- 教室安排表单弹窗 -->
|
||||
<arrange-dialog ref="arrangeDialogRef" @refresh="getDataList" />
|
||||
<!-- 教室公物编辑弹窗 -->
|
||||
<assets-dialog ref="assetsDialogRef" @refresh="getDataList" />
|
||||
<!-- 门锁密码编辑弹窗 -->
|
||||
<password-dialog ref="passwordDialogRef" @refresh="getDataList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -226,7 +254,10 @@ import { getDicts } from "/@/api/admin/dict";
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
import TableColumnControl from '/@/components/TableColumnControl/index.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'
|
||||
|
||||
// 定义变量内容
|
||||
@@ -240,6 +271,8 @@ const platformTypeList = ref<any[]>([])
|
||||
const tyTypeList = ref<any[]>([])
|
||||
const tvTypeList = ref<any[]>([])
|
||||
const arrangeDialogRef = ref()
|
||||
const assetsDialogRef = ref()
|
||||
const passwordDialogRef = ref()
|
||||
|
||||
// 表格列配置
|
||||
const tableColumns = [
|
||||
@@ -416,6 +449,33 @@ const handleArrange = (row: any) => {
|
||||
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 () => {
|
||||
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
|
||||
getDataList()
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg || '考核失败')
|
||||
if (!err?._messageShown) {
|
||||
useMessage().error(err?.msg || '考核失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -512,7 +514,9 @@ const handleDelete = async (ids: string[]) => {
|
||||
getDataList()
|
||||
useMessage().success('删除成功')
|
||||
} 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) => {
|
||||
const timeList = getTimeList(row)
|
||||
return h('div', { class: 'time-slots-container' },
|
||||
timeList.map((timeSlot: any, index: number) =>
|
||||
h('div', { key: index, class: 'time-slot-item' }, [
|
||||
timeList.map((timeSlot: any, index: number) => {
|
||||
// 通过数组索引更新,确保响应式
|
||||
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, {
|
||||
modelValue: timeSlot.startTime,
|
||||
'onUpdate:modelValue': (val: string) => { timeSlot.startTime = val },
|
||||
modelValue: timeSlot.startTime || '',
|
||||
'onUpdate:modelValue': updateStartTime,
|
||||
format: 'HH:mm',
|
||||
valueFormat: 'HH:mm',
|
||||
placeholder: '开始时间',
|
||||
@@ -196,8 +209,8 @@ const TimeRuleTableComponent = defineComponent({
|
||||
}),
|
||||
h('span', { style: { margin: '0 10px' } }, '至'),
|
||||
h(ElTimePicker, {
|
||||
modelValue: timeSlot.endTime,
|
||||
'onUpdate:modelValue': (val: string) => { timeSlot.endTime = val },
|
||||
modelValue: timeSlot.endTime || '',
|
||||
'onUpdate:modelValue': updateEndTime,
|
||||
format: 'HH:mm',
|
||||
valueFormat: 'HH:mm',
|
||||
placeholder: '结束时间',
|
||||
@@ -213,7 +226,7 @@ const TimeRuleTableComponent = defineComponent({
|
||||
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"
|
||||
:rules="dataRules"
|
||||
label-width="120px"
|
||||
v-loading="loading">
|
||||
v-loading="loading"
|
||||
:key="form.turnoverType">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="学年" prop="schoolYear">
|
||||
@@ -47,7 +48,7 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-col :span="12" class="mb20" v-if="!isDropoutType">
|
||||
<el-form-item label="原班级" prop="oldClassCode">
|
||||
<el-select
|
||||
v-model="form.oldClassCode"
|
||||
@@ -66,7 +67,7 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-col :span="12" class="mb20" v-if="!isDropoutType">
|
||||
<el-form-item label="现班级" prop="newClassCode">
|
||||
<el-select
|
||||
v-model="form.newClassCode"
|
||||
@@ -90,7 +91,8 @@
|
||||
v-model="form.turnoverType"
|
||||
placeholder="请选择异动类型"
|
||||
clearable
|
||||
style="width: 100%">
|
||||
style="width: 100%"
|
||||
@change="handleTurnoverTypeChange">
|
||||
<el-option
|
||||
v-for="item in turnoverTypeList"
|
||||
:key="item.value"
|
||||
@@ -299,20 +301,29 @@ const selectedStudentsText = computed(() => {
|
||||
return `已选择 ${form.selectedStudents.length} 名学生`
|
||||
})
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = {
|
||||
// 判断是否为退学类型
|
||||
const isDropoutType = computed(() => {
|
||||
if (!form.turnoverType) return false
|
||||
// 查找异动类型字典中 label 包含"退学"的项
|
||||
const dropoutItem = turnoverTypeList.value.find((item: any) => {
|
||||
const label = item.label || item.dictLabel || item.name || ''
|
||||
return label.includes('退学')
|
||||
})
|
||||
if (dropoutItem) {
|
||||
return dropoutItem.value === form.turnoverType || dropoutItem.dictValue === form.turnoverType || dropoutItem.code === form.turnoverType
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// 定义校验规则(动态)
|
||||
const dataRules = computed(() => {
|
||||
const rules: any = {
|
||||
schoolYear: [
|
||||
{ required: true, message: '请选择学年', trigger: 'change' }
|
||||
],
|
||||
schoolTerm: [
|
||||
{ required: true, message: '请选择学期', trigger: 'change' }
|
||||
],
|
||||
oldClassCode: [
|
||||
{ required: true, message: '请选择原班级', trigger: 'change' }
|
||||
],
|
||||
newClassCode: [
|
||||
{ required: true, message: '请选择现班级', trigger: 'change' }
|
||||
],
|
||||
turnoverType: [
|
||||
{ required: true, message: '请选择异动类型', trigger: 'change' }
|
||||
],
|
||||
@@ -324,6 +335,19 @@ const dataRules = {
|
||||
]
|
||||
}
|
||||
|
||||
// 如果不是退学类型,原班级和现班级为必填
|
||||
if (!isDropoutType.value) {
|
||||
rules.oldClassCode = [
|
||||
{ required: true, message: '请选择原班级', trigger: 'change' }
|
||||
]
|
||||
rules.newClassCode = [
|
||||
{ required: true, message: '请选择现班级', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
return rules
|
||||
})
|
||||
|
||||
// 原班级变化时,更新学生搜索条件
|
||||
const handleOldClassChange = () => {
|
||||
studentSearchForm.classCode = form.oldClassCode
|
||||
@@ -331,14 +355,30 @@ const handleOldClassChange = () => {
|
||||
form.selectedStudents = []
|
||||
}
|
||||
|
||||
// 异动类型变化时处理
|
||||
const handleTurnoverTypeChange = () => {
|
||||
// 如果切换为退学类型,清空原班级和现班级
|
||||
if (isDropoutType.value) {
|
||||
form.oldClassCode = ''
|
||||
form.newClassCode = ''
|
||||
// 清空已选学生(因为退学不需要原班级,学生选择逻辑会受影响)
|
||||
form.selectedStudents = []
|
||||
}
|
||||
// 重新验证表单
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.clearValidate(['oldClassCode', 'newClassCode'])
|
||||
})
|
||||
}
|
||||
|
||||
// 打开学生选择弹窗
|
||||
const openStudentDialog = () => {
|
||||
if (!form.oldClassCode) {
|
||||
// 如果是退学类型,不需要原班级
|
||||
if (!isDropoutType.value && !form.oldClassCode) {
|
||||
useMessage().warning('请先选择原班级')
|
||||
return
|
||||
}
|
||||
studentDialogVisible.value = true
|
||||
studentSearchForm.classCode = form.oldClassCode
|
||||
studentSearchForm.classCode = form.oldClassCode || ''
|
||||
studentSearchForm.classNo = ''
|
||||
studentSearchForm.stuNo = ''
|
||||
studentSearchForm.realName = ''
|
||||
@@ -354,10 +394,13 @@ const handleStudentSearch = async () => {
|
||||
const params: any = {
|
||||
current: studentPagination.currentPage,
|
||||
size: studentPagination.pageSize,
|
||||
classCode: studentSearchForm.classCode || undefined,
|
||||
stuNo: studentSearchForm.stuNo || undefined,
|
||||
realName: studentSearchForm.realName || undefined
|
||||
}
|
||||
// 如果不是退学类型且有原班级,才添加 classCode 条件
|
||||
if (!isDropoutType.value && studentSearchForm.classCode) {
|
||||
params.classCode = studentSearchForm.classCode
|
||||
}
|
||||
const res = await getStudentList(params)
|
||||
if (res.data && res.data.records) {
|
||||
studentTableData.value = res.data.records
|
||||
@@ -494,8 +537,6 @@ const onSubmit = async () => {
|
||||
const submitData: any = {
|
||||
schoolYear: form.schoolYear,
|
||||
schoolTerm: form.schoolTerm,
|
||||
oldClassCode: form.oldClassCode,
|
||||
newClassCode: form.newClassCode,
|
||||
turnoverType: form.turnoverType,
|
||||
turnYear: form.turnYear || '',
|
||||
turnoverDate: form.turnoverDate,
|
||||
@@ -506,6 +547,12 @@ const onSubmit = async () => {
|
||||
}))
|
||||
}
|
||||
|
||||
// 如果不是退学类型,才添加原班级和现班级
|
||||
if (!isDropoutType.value) {
|
||||
submitData.oldClassCode = form.oldClassCode
|
||||
submitData.newClassCode = form.newClassCode
|
||||
}
|
||||
|
||||
// 编辑时需要包含id和单个学生信息
|
||||
if (operType.value === 'edit') {
|
||||
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