feat(admin): add edit flows for all catalogs and member authentik sync

This commit is contained in:
Chris
2026-03-30 03:25:53 +08:00
parent 137861df1c
commit 0582b00f5f
13 changed files with 659 additions and 85 deletions

View File

@@ -16,6 +16,12 @@
<template #empty><el-empty description="目前無群組" /></template>
<el-table-column prop="group_key" label="Group Key" width="180" />
<el-table-column prop="name" label="群組名稱" min-width="200" />
<el-table-column prop="status" label="狀態" width="120" />
<el-table-column label="操作" width="120">
<template #default="{ row }">
<el-button size="small" @click="openEditGroup(row)">編輯</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
@@ -60,16 +66,34 @@
</el-select>
</el-form-item>
<el-form-item label="Scope ID">
<el-input v-model="groupPermForm.scope_id" placeholder="company_key or site_key" />
<el-select v-model="groupPermForm.scope_id" placeholder="選擇 Scope ID" filterable style="width: 100%">
<el-option
v-for="s in scopeOptions"
:key="s.value"
:label="s.label"
:value="s.value"
/>
</el-select>
</el-form-item>
<el-form-item label="系統">
<el-input v-model="groupPermForm.system" placeholder="mkt" />
<el-select v-model="groupPermForm.system" placeholder="選擇系統" filterable style="width: 100%">
<el-option v-for="s in systems" :key="s.system_key" :label="`${s.name} (${s.system_key})`" :value="s.system_key" />
</el-select>
</el-form-item>
<el-form-item label="模組(選填)">
<el-input v-model="groupPermForm.module" placeholder="campaign" clearable />
<el-select v-model="groupPermForm.module" placeholder="系統層(留空) 或選模組" clearable filterable style="width: 100%">
<el-option
v-for="m in filteredModuleOptions"
:key="m.value"
:label="m.label"
:value="m.value"
/>
</el-select>
</el-form-item>
<el-form-item label="操作">
<el-input v-model="groupPermForm.action" placeholder="view" />
<el-select v-model="groupPermForm.action" filterable allow-create default-first-option style="width: 100%">
<el-option v-for="a in actionOptions" :key="a" :label="a" :value="a" />
</el-select>
</el-form-item>
<el-form-item>
<el-button
@@ -113,22 +137,73 @@
<el-button type="primary" :loading="creatingGroup" @click="handleCreateGroup">確認</el-button>
</template>
</el-dialog>
<el-dialog v-model="showEditGroup" title="編輯群組" @close="resetEditGroupForm">
<el-form :model="editGroupForm" label-width="120px">
<el-form-item label="Group Key">
<el-input :model-value="editGroupForm.group_key" disabled />
</el-form-item>
<el-form-item label="群組名稱">
<el-input v-model="editGroupForm.name" />
</el-form-item>
<el-form-item label="狀態">
<el-select v-model="editGroupForm.status" style="width: 100%">
<el-option label="active" value="active" />
<el-option label="inactive" value="inactive" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="showEditGroup = false">取消</el-button>
<el-button type="primary" :loading="savingGroup" @click="handleEditGroup">儲存</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ref, reactive, onMounted, computed, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import {
getPermissionGroups,
createPermissionGroup,
updatePermissionGroup,
addMemberToGroup,
groupGrant,
groupRevoke
} from '@/api/permission-groups'
import { getSystems } from '@/api/systems'
import { getModules } from '@/api/modules'
import { getCompanies } from '@/api/companies'
import { getSites } from '@/api/sites'
const activeTab = ref('groups')
const systems = ref([])
const modules = ref([])
const companies = ref([])
const sites = ref([])
const actionOptions = ['view', 'edit', 'manage', 'admin']
const filteredModuleOptions = computed(() => {
if (!groupPermForm.system) return []
return modules.value
.filter(m => m.system_key === groupPermForm.system && !m.module_key.endsWith('.__system__'))
.map(m => ({
value: m.module_key.split('.', 2)[1] || m.module_key,
label: `${m.name} (${m.module_key})`
}))
})
const scopeOptions = computed(() => {
if (groupPermForm.scope_type === 'company') {
return companies.value.map(c => ({ value: c.company_key, label: `${c.name} (${c.company_key})` }))
}
if (groupPermForm.scope_type === 'site') {
return sites.value.map(s => ({ value: s.site_key, label: `${s.name} (${s.site_key})` }))
}
return []
})
// Groups
const groups = ref([])
@@ -150,10 +225,26 @@ async function loadGroups() {
}
}
async function loadCatalogs() {
const [systemsRes, modulesRes, companiesRes, sitesRes] = await Promise.all([
getSystems(),
getModules(),
getCompanies(),
getSites()
])
systems.value = systemsRes.data?.items || []
modules.value = modulesRes.data?.items || []
companies.value = companiesRes.data?.items || []
sites.value = sitesRes.data?.items || []
}
// Create Group
const showCreateGroup = ref(false)
const creatingGroup = ref(false)
const createForm = reactive({ group_key: '', name: '' })
const showEditGroup = ref(false)
const savingGroup = ref(false)
const editGroupForm = reactive({ group_key: '', name: '', status: 'active' })
function resetCreateForm() {
createForm.group_key = ''
@@ -179,6 +270,36 @@ async function handleCreateGroup() {
}
}
function openEditGroup(row) {
editGroupForm.group_key = row.group_key
editGroupForm.name = row.name
editGroupForm.status = row.status || 'active'
showEditGroup.value = true
}
function resetEditGroupForm() {
editGroupForm.group_key = ''
editGroupForm.name = ''
editGroupForm.status = 'active'
}
async function handleEditGroup() {
savingGroup.value = true
try {
await updatePermissionGroup(editGroupForm.group_key, {
name: editGroupForm.name,
status: editGroupForm.status
})
ElMessage.success('群組更新成功')
showEditGroup.value = false
await loadGroups()
} catch (err) {
ElMessage.error('群組更新失敗')
} finally {
savingGroup.value = false
}
}
// Add Member
const memberForm = reactive({ groupKey: '', authentikSub: '' })
const addingMember = ref(false)
@@ -245,5 +366,15 @@ async function handleGroupRevoke() {
}
}
onMounted(loadGroups)
watch(() => groupPermForm.scope_type, () => {
groupPermForm.scope_id = ''
})
watch(() => groupPermForm.system, () => {
groupPermForm.module = ''
})
onMounted(async () => {
await Promise.all([loadGroups(), loadCatalogs()])
})
</script>