272 lines
6.6 KiB
Vue
272 lines
6.6 KiB
Vue
<template>
|
|
<el-dialog :title="getScopeDialogTitle(type)" v-model="visible" width="600px" append-to-body destroy-on-close>
|
|
<el-form :model="form" label-width="100px">
|
|
<el-form-item :label="getScopeName(type)">
|
|
<div v-if="loading" class="loading-container">
|
|
<el-icon class="is-loading"><Loading /></el-icon>
|
|
<span>加载中...</span>
|
|
</div>
|
|
<div v-else-if="type === 'custom'">
|
|
<el-tree
|
|
:data="scopeOptions.custom"
|
|
show-checkbox
|
|
node-key="id"
|
|
:default-checked-keys="form.selectedScopes"
|
|
:props="{ label: 'label', children: 'children' }"
|
|
@check="onTreeCheck"
|
|
default-expand-all
|
|
style="
|
|
width: 100%;
|
|
min-width: 100%;
|
|
overflow-x: auto;
|
|
overflow-y: auto;
|
|
/* border: 1px solid #ebeef5; */
|
|
border-radius: 4px;
|
|
padding: 10px;
|
|
box-sizing: border-box;
|
|
"
|
|
/>
|
|
</div>
|
|
<div v-else-if="type === 'teacher'">
|
|
<el-checkbox-group v-model="form.selectedScopes">
|
|
<el-checkbox v-for="item in scopeOptions.teacher" :key="item.id" :label="item.id">
|
|
{{ item.label }}
|
|
</el-checkbox>
|
|
</el-checkbox-group>
|
|
</div>
|
|
</el-form-item>
|
|
</el-form>
|
|
<template #footer>
|
|
<div class="dialog-footer">
|
|
<el-button @click="cancel">取 消</el-button>
|
|
<el-button type="primary" @click="submitForm" :loading="loading">确 定</el-button>
|
|
</div>
|
|
</template>
|
|
</el-dialog>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, reactive, computed, watch, onMounted } from 'vue';
|
|
import { ElMessage } from 'element-plus';
|
|
import { Loading } from '@element-plus/icons-vue';
|
|
import axios from 'axios';
|
|
import request from '@/utils/request';
|
|
|
|
const props = defineProps({
|
|
modelValue: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
admin: {
|
|
type: Object,
|
|
default: () => ({})
|
|
},
|
|
type: {
|
|
type: String,
|
|
default: 'custom'
|
|
}
|
|
});
|
|
|
|
const emit = defineEmits(['update:modelValue', 'submit']);
|
|
|
|
// 计算属性:对话框可见性
|
|
const visible = computed({
|
|
get: () => props.modelValue,
|
|
set: (value) => emit('update:modelValue', value)
|
|
});
|
|
|
|
// 范围选项(从API获取)
|
|
const scopeOptions = reactive({
|
|
custom: [] as any[],
|
|
teacher: [] as any[]
|
|
});
|
|
|
|
// 加载状态
|
|
const loading = ref(false);
|
|
|
|
// 表单数据
|
|
const form = reactive({
|
|
selectedScopes: [] as number[]
|
|
});
|
|
|
|
// 获取部门树数据
|
|
async function fetchDeptTree() {
|
|
try {
|
|
loading.value = true;
|
|
const response = await request({
|
|
url: '/system/user/deptTree',
|
|
method: 'get',
|
|
params: {
|
|
belongDeptId: 101
|
|
}
|
|
});
|
|
if (response && response.code === 200) {
|
|
// custom类型直接使用树形结构
|
|
if (props.type === 'custom') {
|
|
scopeOptions.custom = response.data || [];
|
|
} else if (props.type === 'teacher') {
|
|
// teacher类型仍可用扁平化
|
|
scopeOptions.teacher = flattenTreeData(response.data || []);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('获取部门树数据失败:', error);
|
|
ElMessage.error('获取范围选项失败');
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
// 将树形数据扁平化
|
|
function flattenTreeData(treeData: any[]): any[] {
|
|
const result: any[] = [];
|
|
|
|
function traverse(nodes: any[]) {
|
|
nodes.forEach((node) => {
|
|
result.push({
|
|
id: node.id,
|
|
label: node.label
|
|
});
|
|
|
|
if (node.children && node.children.length > 0) {
|
|
traverse(node.children);
|
|
}
|
|
});
|
|
}
|
|
|
|
traverse(treeData);
|
|
return result;
|
|
}
|
|
|
|
// 新增:获取历史范围数据
|
|
async function fetchSelectedScopes(managerId: number | string) {
|
|
if (!managerId) return;
|
|
try {
|
|
const res = await request({
|
|
url: '/xgStudentManagerSource/studentManagerSource/list',
|
|
method: 'get',
|
|
params: { managerId }
|
|
});
|
|
if (res && res.code === 200) {
|
|
// 提取rows中的authSource字段作为已选范围ID
|
|
form.selectedScopes = (res.rows || []).map((item: any) => item.authSource);
|
|
}
|
|
} catch (e) {
|
|
form.selectedScopes = [];
|
|
}
|
|
}
|
|
|
|
// 监听对话框显示状态,自动加载数据
|
|
watch(visible, async (newVal) => {
|
|
if (newVal) {
|
|
const managerId = props.admin?.managerId || props.admin?.id;
|
|
await fetchDeptTree();
|
|
await fetchSelectedScopes(managerId);
|
|
} else {
|
|
// 对话框关闭时清空选择
|
|
form.selectedScopes = [];
|
|
}
|
|
});
|
|
|
|
// el-tree勾选事件,确保能正确获取所有勾选节点的id
|
|
function onTreeCheck(checkedNodes: any, info: { checkedKeys: any[]; halfCheckedKeys: any[] }) {
|
|
form.selectedScopes = info.checkedKeys;
|
|
}
|
|
|
|
// 获取范围对话框标题
|
|
function getScopeDialogTitle(type: string) {
|
|
const name = props.admin?.managerName || '';
|
|
switch (type) {
|
|
case 'custom':
|
|
return `为【${name}】分配管理范围`;
|
|
case 'teacher':
|
|
return `为【${name}】分配班级`;
|
|
default:
|
|
return `为【${name}】分配范围`;
|
|
}
|
|
}
|
|
|
|
// 获取范围名称
|
|
function getScopeName(type: string) {
|
|
switch (type) {
|
|
case 'custom':
|
|
return '管理范围';
|
|
case 'teacher':
|
|
return '所管理班级';
|
|
default:
|
|
return '管理范围';
|
|
}
|
|
}
|
|
|
|
// 提交表单
|
|
async function submitForm() {
|
|
// 组装要提交的数据
|
|
const selectedIds = form.selectedScopes;
|
|
console.log(selectedIds);
|
|
const managerId = props.admin?.managerId || props.admin?.id; // 兼容不同字段
|
|
if (!managerId) {
|
|
ElMessage.error('管理员ID缺失,无法提交');
|
|
return;
|
|
}
|
|
loading.value = true;
|
|
try {
|
|
await request({
|
|
url: '/xgStudentManagerSource/studentManagerSource/grantDeptAuth',
|
|
method: 'post',
|
|
params: { managerId },
|
|
data: selectedIds
|
|
});
|
|
ElMessage.success('分配成功');
|
|
visible.value = false;
|
|
emit('submit', {
|
|
sourceIds: selectedIds
|
|
});
|
|
} catch (error) {
|
|
ElMessage.error('分配失败,请重试');
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
// 取消
|
|
function cancel() {
|
|
visible.value = false;
|
|
}
|
|
|
|
// 从props获取类型的响应式引用
|
|
const type = computed(() => props.type);
|
|
|
|
// 暴露组件以修复导出问题
|
|
defineExpose({
|
|
submitForm,
|
|
cancel,
|
|
fetchDeptTree
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.dialog-footer {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
}
|
|
.el-checkbox-group {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
}
|
|
.el-checkbox {
|
|
margin-right: 15px;
|
|
margin-bottom: 10px;
|
|
}
|
|
.loading-container {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 20px;
|
|
color: #909399;
|
|
}
|
|
.loading-container .el-icon {
|
|
margin-right: 8px;
|
|
font-size: 16px;
|
|
}
|
|
</style>
|