From ad09c8ff325357e592b24232ee0851a252be0cc0 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 30 Mar 2026 19:38:49 +0800 Subject: [PATCH] feat(admin): implement group-centric relations and system/module/company linkage views --- src/App.vue | 1 - src/api/companies.js | 1 + src/api/modules.js | 2 + src/api/permission-groups.js | 3 + src/api/systems.js | 2 + src/pages/admin/CompaniesPage.vue | 36 +- src/pages/admin/ModulesPage.vue | 61 ++- src/pages/admin/PermissionGroupsPage.vue | 446 +++++++++--------- src/pages/admin/SystemsPage.vue | 59 ++- src/pages/permissions/PermissionAdminPage.vue | 11 +- 10 files changed, 375 insertions(+), 247 deletions(-) diff --git a/src/App.vue b/src/App.vue index 21b53cf..92e211f 100644 --- a/src/App.vue +++ b/src/App.vue @@ -56,7 +56,6 @@ const userTabs = [ ] const adminTabs = [ - { to: '/admin/permissions', label: '權限管理' }, { to: '/admin/systems', label: '系統' }, { to: '/admin/modules', label: '模組' }, { to: '/admin/companies', label: '公司' }, diff --git a/src/api/companies.js b/src/api/companies.js index 9411dd3..732817c 100644 --- a/src/api/companies.js +++ b/src/api/companies.js @@ -3,3 +3,4 @@ import { adminHttp } from './http' export const getCompanies = () => adminHttp.get('/admin/companies') export const createCompany = (data) => adminHttp.post('/admin/companies', data) export const updateCompany = (companyKey, data) => adminHttp.patch(`/admin/companies/${companyKey}`, data) +export const getCompanySites = (companyKey) => adminHttp.get(`/admin/companies/${companyKey}/sites`) diff --git a/src/api/modules.js b/src/api/modules.js index 3d0ba34..2e48669 100644 --- a/src/api/modules.js +++ b/src/api/modules.js @@ -3,3 +3,5 @@ import { adminHttp } from './http' export const getModules = () => adminHttp.get('/admin/modules') export const createModule = (data) => adminHttp.post('/admin/modules', data) export const updateModule = (moduleKey, data) => adminHttp.patch(`/admin/modules/${moduleKey}`, data) +export const getModuleGroups = (moduleKey) => adminHttp.get(`/admin/modules/${moduleKey}/groups`) +export const getModuleMembers = (moduleKey) => adminHttp.get(`/admin/modules/${moduleKey}/members`) diff --git a/src/api/permission-groups.js b/src/api/permission-groups.js index 2845f97..9d5a238 100644 --- a/src/api/permission-groups.js +++ b/src/api/permission-groups.js @@ -4,6 +4,9 @@ export const getPermissionGroups = () => adminHttp.get('/admin/permission-groups export const createPermissionGroup = (data) => adminHttp.post('/admin/permission-groups', data) export const updatePermissionGroup = (groupKey, data) => adminHttp.patch(`/admin/permission-groups/${groupKey}`, data) export const getPermissionGroupPermissions = (groupKey) => adminHttp.get(`/admin/permission-groups/${groupKey}/permissions`) +export const getPermissionGroupBindings = (groupKey) => adminHttp.get(`/admin/permission-groups/${groupKey}/bindings`) +export const updatePermissionGroupBindings = (groupKey, data) => + adminHttp.put(`/admin/permission-groups/${groupKey}/bindings`, data) export const addMemberToGroup = (groupKey, authentikSub) => adminHttp.post(`/admin/permission-groups/${groupKey}/members/${authentikSub}`) diff --git a/src/api/systems.js b/src/api/systems.js index 90cac1a..3e87b58 100644 --- a/src/api/systems.js +++ b/src/api/systems.js @@ -3,3 +3,5 @@ import { adminHttp } from './http' export const getSystems = () => adminHttp.get('/admin/systems') export const createSystem = (data) => adminHttp.post('/admin/systems', data) export const updateSystem = (systemKey, data) => adminHttp.patch(`/admin/systems/${systemKey}`, data) +export const getSystemGroups = (systemKey) => adminHttp.get(`/admin/systems/${systemKey}/groups`) +export const getSystemMembers = (systemKey) => adminHttp.get(`/admin/systems/${systemKey}/members`) diff --git a/src/pages/admin/CompaniesPage.vue b/src/pages/admin/CompaniesPage.vue index 5eca360..05f5023 100644 --- a/src/pages/admin/CompaniesPage.vue +++ b/src/pages/admin/CompaniesPage.vue @@ -13,9 +13,10 @@ - + @@ -47,6 +48,18 @@ 儲存 + + + + + + + + + + @@ -54,7 +67,7 @@ import { ref, onMounted } from 'vue' import { ElMessage } from 'element-plus' import { Plus } from '@element-plus/icons-vue' -import { getCompanies, createCompany, updateCompany } from '@/api/companies' +import { getCompanies, createCompany, updateCompany, getCompanySites } from '@/api/companies' const companies = ref([]) const loading = ref(false) @@ -73,6 +86,11 @@ const rules = { name: [{ required: true, message: '請輸入名稱', trigger: 'blur' }] } +const showSitesDialog = ref(false) +const sitesLoading = ref(false) +const selectedCompanyKey = ref('') +const companySites = ref([]) + async function load() { loading.value = true error.value = false @@ -133,5 +151,19 @@ async function handleEdit() { } } +async function openSites(row) { + selectedCompanyKey.value = row.company_key + showSitesDialog.value = true + sitesLoading.value = true + try { + const res = await getCompanySites(row.company_key) + companySites.value = res.data?.items || [] + } catch (err) { + ElMessage.error('載入公司站台失敗') + } finally { + sitesLoading.value = false + } +} + onMounted(load) diff --git a/src/pages/admin/ModulesPage.vue b/src/pages/admin/ModulesPage.vue index f913b57..bd2fca9 100644 --- a/src/pages/admin/ModulesPage.vue +++ b/src/pages/admin/ModulesPage.vue @@ -19,12 +19,14 @@ - + - + @@ -69,6 +71,33 @@ 儲存 + + + + + + + + + + + + + + + + + + + + + + + + + @@ -76,7 +105,7 @@ import { ref, onMounted } from 'vue' import { ElMessage } from 'element-plus' import { Plus } from '@element-plus/icons-vue' -import { getModules, createModule, updateModule } from '@/api/modules' +import { getModules, createModule, updateModule, getModuleGroups, getModuleMembers } from '@/api/modules' import { getSystems } from '@/api/systems' const modules = ref([]) @@ -98,6 +127,13 @@ const rules = { name: [{ required: true, message: '請輸入名稱', trigger: 'blur' }] } +const showRelationDialog = ref(false) +const relationLoading = ref(false) +const relationModuleKey = ref('') +const relationTab = ref('groups') +const relationGroups = ref([]) +const relationMembers = ref([]) + async function load() { loading.value = true error.value = false @@ -166,5 +202,24 @@ async function handleEdit() { } } +async function openRelations(row, tab) { + relationModuleKey.value = row.module_key + relationTab.value = tab + showRelationDialog.value = true + relationLoading.value = true + try { + const [groupsRes, membersRes] = await Promise.all([ + getModuleGroups(row.module_key), + getModuleMembers(row.module_key) + ]) + relationGroups.value = groupsRes.data?.items || [] + relationMembers.value = membersRes.data?.items || [] + } catch (err) { + ElMessage.error('載入模組關聯資料失敗') + } finally { + relationLoading.value = false + } +} + onMounted(load) diff --git a/src/pages/admin/PermissionGroupsPage.vue b/src/pages/admin/PermissionGroupsPage.vue index 954faa8..d0e91ff 100644 --- a/src/pages/admin/PermissionGroupsPage.vue +++ b/src/pages/admin/PermissionGroupsPage.vue @@ -1,105 +1,96 @@ - - -
- Group: {{ selectedGroupKey }} -
- - - - - - - - - -
diff --git a/src/pages/permissions/PermissionAdminPage.vue b/src/pages/permissions/PermissionAdminPage.vue index f8cca8e..49bd5c1 100644 --- a/src/pages/permissions/PermissionAdminPage.vue +++ b/src/pages/permissions/PermissionAdminPage.vue @@ -27,7 +27,6 @@ - @@ -99,7 +98,6 @@ - @@ -162,7 +160,6 @@
- 查詢 @@ -210,7 +207,7 @@ const modules = ref([]) const companies = ref([]) const sites = ref([]) const members = ref([]) -const actionOptions = ['view', 'edit', 'manage', 'admin'] +const actionOptions = ['view', 'edit'] const listFilters = reactive({ keyword: '', scope_type: '' }) const listLoading = ref(false) const directPermissions = ref([]) @@ -246,9 +243,6 @@ const grantModuleOptions = computed(() => { }) const grantScopeOptions = computed(() => { - if (grantForm.scope_type === 'company') { - return companies.value.map(c => ({ value: c.company_key, label: `${c.name} (${c.company_key})` })) - } if (grantForm.scope_type === 'site') { return sites.value.map(s => ({ value: s.site_key, label: `${s.name} (${s.site_key})` })) } @@ -318,9 +312,6 @@ const revokeModuleOptions = computed(() => { }) const revokeScopeOptions = computed(() => { - if (revokeForm.scope_type === 'company') { - return companies.value.map(c => ({ value: c.company_key, label: `${c.name} (${c.company_key})` })) - } if (revokeForm.scope_type === 'site') { return sites.value.map(s => ({ value: s.site_key, label: `${s.name} (${s.site_key})` })) }