已完成“组织与权限”优化:

基础数据拆分为:组织机构、学年学期、教学场所。
人员档案拆分为:教师档案、学生档案。
基础数据、教师、学生、用户权限均支持 Excel 模板下载、批量导入和导出。
导入按编码/工号/学号/账号新增或更新;整批校验,失败不写入,限制 2000 条、10 MB。
用户列表新增密码重置,包含双重确认、最低 8 位校验和服务端权限保护。
导入严格执行角色及学院数据范围校验,并保护当前超级管理员账号。
保留旧地址重定向,原有入口不会失效。
This commit is contained in:
biss committed 2026-07-24 18:12:56 +08:00
1 parent 662aa1051f
commit 022e79c48d
13 files changed
+2133 -59

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
import type { AxiosRequestConfig } from 'axios'
import http from './http'
function responseFileName(header: string | undefined, fallback: string) {
if (!header) return fallback
const encoded = header.match(/filename\*=UTF-8''([^;]+)/i)?.[1]
if (encoded) return decodeURIComponent(encoded)
const plain = header.match(/filename="?([^";]+)"?/i)?.[1]
return plain || fallback
}
export async function downloadApiFile(
path: string,
fallbackName: string,
config?: AxiosRequestConfig,
) {
const response = await http.get(path, {
...config,
responseType: 'blob',
})
const url = URL.createObjectURL(response.data)
const anchor = document.createElement('a')
anchor.href = url
anchor.download = responseFileName(response.headers['content-disposition'], fallbackName)
document.body.appendChild(anchor)
anchor.click()
anchor.remove()
URL.revokeObjectURL(url)
}
export async function importExcel(path: string, file: File) {
const form = new FormData()
form.append('file', file)
return http.post(path, form, {
headers: { 'Content-Type': 'multipart/form-data' },
timeout: 60000,
})
}
+14 -2
View File
@@ -49,11 +49,23 @@ const navigationGroups = computed<NavigationGroup[]>(() => [
items: [
...whenVisible(
hasAnyRole(['SuperAdmin', 'AcademicAdmin']),
{ path: '/base-data', label: '基础数据' },
{ path: '/base-data/organization', label: '组织机构' },
),
...whenVisible(
hasAnyRole(['SuperAdmin', 'AcademicAdmin']),
{ path: '/base-data/terms', label: '学年学期' },
),
...whenVisible(
hasAnyRole(['SuperAdmin', 'AcademicAdmin']),
{ path: '/base-data/facilities', label: '教学场所' },
),
...whenVisible(
hasAnyRole(['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Counselor']),
{ path: '/personnel', label: '人员档案' },
{ path: '/teachers', label: '教师档案' },
),
...whenVisible(
hasAnyRole(['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Counselor']),
{ path: '/students', label: '学生档案' },
),
...whenVisible(auth.isSuperAdmin, { path: '/users', label: '用户与权限' }),
],
+42 -3
View File
@@ -23,16 +23,55 @@ const router = createRouter({
},
{
path: 'base-data',
name: 'base-data',
redirect: '/base-data/organization',
},
{
path: 'base-data/organization',
name: 'organization-data',
component: () => import('../views/BaseDataView.vue'),
meta: { roles: ['SuperAdmin', 'AcademicAdmin'] },
meta: {
roles: ['SuperAdmin', 'AcademicAdmin'],
baseGroup: 'organization',
},
},
{
path: 'base-data/terms',
name: 'term-data',
component: () => import('../views/BaseDataView.vue'),
meta: {
roles: ['SuperAdmin', 'AcademicAdmin'],
baseGroup: 'terms',
},
},
{
path: 'base-data/facilities',
name: 'facility-data',
component: () => import('../views/BaseDataView.vue'),
meta: {
roles: ['SuperAdmin', 'AcademicAdmin'],
baseGroup: 'facilities',
},
},
{
path: 'personnel',
name: 'personnel',
redirect: '/teachers',
},
{
path: 'teachers',
name: 'teachers',
component: () => import('../views/PersonnelView.vue'),
meta: {
roles: ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Counselor'],
registry: 'teachers',
},
},
{
path: 'students',
name: 'students',
component: () => import('../views/PersonnelView.vue'),
meta: {
roles: ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Counselor'],
registry: 'students',
},
},
{
Loaded 3 of 13 files, more files were not shown because too many files have changed in this diff. Show more