feat(admin): add delete APIs and UI actions for all admin resources

This commit is contained in:
Chris
2026-03-31 20:58:20 +08:00
parent c4492a3072
commit f6f86d4bfb
15 changed files with 245 additions and 15 deletions

View File

@@ -1,12 +1,14 @@
import secrets import secrets
from fastapi import APIRouter, Depends, HTTPException, Query, status from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy import select from sqlalchemy import delete, select
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from app.core.keygen import generate_key from app.core.keygen import generate_key
from app.core.config import get_settings from app.core.config import get_settings
from app.db.session import get_db from app.db.session import get_db
from app.models.api_client import ApiClient from app.models.api_client import ApiClient
from app.models.permission_group_member import PermissionGroupMember
from app.models.permission_group_permission import PermissionGroupPermission
from app.repositories.companies_repo import CompaniesRepository from app.repositories.companies_repo import CompaniesRepository
from app.repositories.modules_repo import ModulesRepository from app.repositories.modules_repo import ModulesRepository
from app.repositories.permission_groups_repo import PermissionGroupsRepository from app.repositories.permission_groups_repo import PermissionGroupsRepository
@@ -195,6 +197,21 @@ def update_system(
return SystemItem(id=row.id, system_key=row.system_key, name=row.name, status=row.status) return SystemItem(id=row.id, system_key=row.system_key, name=row.name, status=row.status)
@router.delete("/systems/{system_key}")
def delete_system(
system_key: str,
db: Session = Depends(get_db),
) -> dict[str, int | str]:
repo = SystemsRepository(db)
row = repo.get_by_key(system_key)
if not row:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="system_not_found")
db.execute(delete(PermissionGroupPermission).where(PermissionGroupPermission.system == system_key))
db.delete(row)
db.commit()
return {"deleted": 1, "result": "deleted"}
@router.get("/modules") @router.get("/modules")
def list_modules( def list_modules(
db: Session = Depends(get_db), db: Session = Depends(get_db),
@@ -253,6 +270,21 @@ def update_module(
return ModuleItem(id=row.id, system_key=row.system_key, module_key=row.module_key, name=row.name, status=row.status) return ModuleItem(id=row.id, system_key=row.system_key, module_key=row.module_key, name=row.name, status=row.status)
@router.delete("/modules/{module_key}")
def delete_module(
module_key: str,
db: Session = Depends(get_db),
) -> dict[str, int | str]:
modules_repo = ModulesRepository(db)
row = modules_repo.get_by_key(module_key)
if not row:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="module_not_found")
db.execute(delete(PermissionGroupPermission).where(PermissionGroupPermission.module == module_key))
db.delete(row)
db.commit()
return {"deleted": 1, "result": "deleted"}
@router.get("/systems/{system_key}/groups") @router.get("/systems/{system_key}/groups")
def list_system_groups( def list_system_groups(
system_key: str, system_key: str,
@@ -374,6 +406,30 @@ def update_company(
return CompanyItem(id=row.id, company_key=row.company_key, name=row.name, status=row.status) return CompanyItem(id=row.id, company_key=row.company_key, name=row.name, status=row.status)
@router.delete("/companies/{company_key}")
def delete_company(
company_key: str,
db: Session = Depends(get_db),
) -> dict[str, int | str]:
companies_repo = CompaniesRepository(db)
sites_repo = SitesRepository(db)
company = companies_repo.get_by_key(company_key)
if not company:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="company_not_found")
company_sites, _ = sites_repo.list(company_id=company.id, limit=10000, offset=0)
company_site_keys = [s.site_key for s in company_sites]
if company_site_keys:
db.execute(
delete(PermissionGroupPermission).where(
PermissionGroupPermission.scope_type == "site",
PermissionGroupPermission.scope_id.in_(company_site_keys),
)
)
db.delete(company)
db.commit()
return {"deleted": 1, "result": "deleted"}
@router.get("/companies/{company_key}/sites") @router.get("/companies/{company_key}/sites")
def list_company_sites( def list_company_sites(
company_key: str, company_key: str,
@@ -478,6 +534,26 @@ def update_site(
return SiteItem(id=row.id, site_key=row.site_key, company_key=company_key, name=row.name, status=row.status) return SiteItem(id=row.id, site_key=row.site_key, company_key=company_key, name=row.name, status=row.status)
@router.delete("/sites/{site_key}")
def delete_site(
site_key: str,
db: Session = Depends(get_db),
) -> dict[str, int | str]:
sites_repo = SitesRepository(db)
row = sites_repo.get_by_key(site_key)
if not row:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="site_not_found")
db.execute(
delete(PermissionGroupPermission).where(
PermissionGroupPermission.scope_type == "site",
PermissionGroupPermission.scope_id == site_key,
)
)
db.delete(row)
db.commit()
return {"deleted": 1, "result": "deleted"}
@router.get("/members") @router.get("/members")
def list_members( def list_members(
db: Session = Depends(get_db), db: Session = Depends(get_db),
@@ -595,6 +671,21 @@ def update_member(
) )
@router.delete("/members/{authentik_sub}")
def delete_member(
authentik_sub: str,
db: Session = Depends(get_db),
) -> dict[str, int | str]:
users_repo = UsersRepository(db)
row = users_repo.get_by_sub(authentik_sub)
if not row:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="user_not_found")
db.execute(delete(PermissionGroupMember).where(PermissionGroupMember.authentik_sub == authentik_sub))
db.delete(row)
db.commit()
return {"deleted": 1, "result": "deleted"}
@router.post("/members/{authentik_sub}/password/reset", response_model=MemberPasswordResetResponse) @router.post("/members/{authentik_sub}/password/reset", response_model=MemberPasswordResetResponse)
def reset_member_password( def reset_member_password(
authentik_sub: str, authentik_sub: str,
@@ -759,6 +850,19 @@ def rotate_api_client_key(
return ApiClientRotateKeyResponse(client_key=row.client_key, api_key=api_key) return ApiClientRotateKeyResponse(client_key=row.client_key, api_key=api_key)
@router.delete("/api-clients/{client_key}")
def delete_api_client(
client_key: str,
db: Session = Depends(get_db),
) -> dict[str, int | str]:
row = db.scalar(select(ApiClient).where(ApiClient.client_key == client_key))
if not row:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="api_client_not_found")
db.delete(row)
db.commit()
return {"deleted": 1, "result": "deleted"}
@router.get("/permission-groups") @router.get("/permission-groups")
def list_permission_groups( def list_permission_groups(
db: Session = Depends(get_db), db: Session = Depends(get_db),
@@ -898,6 +1002,20 @@ def update_permission_group(
return PermissionGroupItem(id=row.id, group_key=row.group_key, name=row.name, status=row.status) return PermissionGroupItem(id=row.id, group_key=row.group_key, name=row.name, status=row.status)
@router.delete("/permission-groups/{group_key}")
def delete_permission_group(
group_key: str,
db: Session = Depends(get_db),
) -> dict[str, int | str]:
repo = PermissionGroupsRepository(db)
row = repo.get_by_key(group_key)
if not row:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="group_not_found")
db.delete(row)
db.commit()
return {"deleted": 1, "result": "deleted"}
@router.post("/permission-groups/{group_key}/members/{authentik_sub}") @router.post("/permission-groups/{group_key}/members/{authentik_sub}")
def add_group_member( def add_group_member(
group_key: str, group_key: str,

View File

@@ -4,3 +4,4 @@ export const getApiClients = (params) => adminHttp.get('/admin/api-clients', { p
export const createApiClient = (data) => adminHttp.post('/admin/api-clients', data) export const createApiClient = (data) => adminHttp.post('/admin/api-clients', data)
export const updateApiClient = (clientKey, data) => adminHttp.patch(`/admin/api-clients/${clientKey}`, data) export const updateApiClient = (clientKey, data) => adminHttp.patch(`/admin/api-clients/${clientKey}`, data)
export const rotateApiClientKey = (clientKey) => adminHttp.post(`/admin/api-clients/${clientKey}/rotate-key`) export const rotateApiClientKey = (clientKey) => adminHttp.post(`/admin/api-clients/${clientKey}/rotate-key`)
export const deleteApiClient = (clientKey) => adminHttp.delete(`/admin/api-clients/${clientKey}`)

View File

@@ -3,4 +3,5 @@ import { adminHttp } from './http'
export const getCompanies = () => adminHttp.get('/admin/companies') export const getCompanies = () => adminHttp.get('/admin/companies')
export const createCompany = (data) => adminHttp.post('/admin/companies', data) export const createCompany = (data) => adminHttp.post('/admin/companies', data)
export const updateCompany = (companyKey, data) => adminHttp.patch(`/admin/companies/${companyKey}`, data) export const updateCompany = (companyKey, data) => adminHttp.patch(`/admin/companies/${companyKey}`, data)
export const deleteCompany = (companyKey) => adminHttp.delete(`/admin/companies/${companyKey}`)
export const getCompanySites = (companyKey) => adminHttp.get(`/admin/companies/${companyKey}/sites`) export const getCompanySites = (companyKey) => adminHttp.get(`/admin/companies/${companyKey}/sites`)

View File

@@ -3,6 +3,7 @@ import { adminHttp } from './http'
export const getMembers = () => adminHttp.get('/admin/members') export const getMembers = () => adminHttp.get('/admin/members')
export const upsertMember = (data) => adminHttp.post('/admin/members/upsert', data) export const upsertMember = (data) => adminHttp.post('/admin/members/upsert', data)
export const updateMember = (authentikSub, data) => adminHttp.patch(`/admin/members/${authentikSub}`, data) export const updateMember = (authentikSub, data) => adminHttp.patch(`/admin/members/${authentikSub}`, data)
export const deleteMember = (authentikSub) => adminHttp.delete(`/admin/members/${authentikSub}`)
export const resetMemberPassword = (authentikSub) => adminHttp.post(`/admin/members/${authentikSub}/password/reset`) export const resetMemberPassword = (authentikSub) => adminHttp.post(`/admin/members/${authentikSub}/password/reset`)
export const getMemberPermissionGroups = (authentikSub) => adminHttp.get(`/admin/members/${authentikSub}/permission-groups`) export const getMemberPermissionGroups = (authentikSub) => adminHttp.get(`/admin/members/${authentikSub}/permission-groups`)
export const setMemberPermissionGroups = (authentikSub, groupKeys) => export const setMemberPermissionGroups = (authentikSub, groupKeys) =>

View File

@@ -3,5 +3,6 @@ import { adminHttp } from './http'
export const getModules = () => adminHttp.get('/admin/modules') export const getModules = () => adminHttp.get('/admin/modules')
export const createModule = (data) => adminHttp.post('/admin/modules', data) export const createModule = (data) => adminHttp.post('/admin/modules', data)
export const updateModule = (moduleKey, data) => adminHttp.patch(`/admin/modules/${moduleKey}`, data) export const updateModule = (moduleKey, data) => adminHttp.patch(`/admin/modules/${moduleKey}`, data)
export const deleteModule = (moduleKey) => adminHttp.delete(`/admin/modules/${moduleKey}`)
export const getModuleGroups = (moduleKey) => adminHttp.get(`/admin/modules/${moduleKey}/groups`) export const getModuleGroups = (moduleKey) => adminHttp.get(`/admin/modules/${moduleKey}/groups`)
export const getModuleMembers = (moduleKey) => adminHttp.get(`/admin/modules/${moduleKey}/members`) export const getModuleMembers = (moduleKey) => adminHttp.get(`/admin/modules/${moduleKey}/members`)

View File

@@ -3,6 +3,7 @@ import { adminHttp } from './http'
export const getPermissionGroups = () => adminHttp.get('/admin/permission-groups') export const getPermissionGroups = () => adminHttp.get('/admin/permission-groups')
export const createPermissionGroup = (data) => adminHttp.post('/admin/permission-groups', data) export const createPermissionGroup = (data) => adminHttp.post('/admin/permission-groups', data)
export const updatePermissionGroup = (groupKey, data) => adminHttp.patch(`/admin/permission-groups/${groupKey}`, data) export const updatePermissionGroup = (groupKey, data) => adminHttp.patch(`/admin/permission-groups/${groupKey}`, data)
export const deletePermissionGroup = (groupKey) => adminHttp.delete(`/admin/permission-groups/${groupKey}`)
export const getPermissionGroupPermissions = (groupKey) => adminHttp.get(`/admin/permission-groups/${groupKey}/permissions`) export const getPermissionGroupPermissions = (groupKey) => adminHttp.get(`/admin/permission-groups/${groupKey}/permissions`)
export const getPermissionGroupBindings = (groupKey) => adminHttp.get(`/admin/permission-groups/${groupKey}/bindings`) export const getPermissionGroupBindings = (groupKey) => adminHttp.get(`/admin/permission-groups/${groupKey}/bindings`)
export const updatePermissionGroupBindings = (groupKey, data) => export const updatePermissionGroupBindings = (groupKey, data) =>

View File

@@ -3,3 +3,4 @@ import { adminHttp } from './http'
export const getSites = () => adminHttp.get('/admin/sites') export const getSites = () => adminHttp.get('/admin/sites')
export const createSite = (data) => adminHttp.post('/admin/sites', data) export const createSite = (data) => adminHttp.post('/admin/sites', data)
export const updateSite = (siteKey, data) => adminHttp.patch(`/admin/sites/${siteKey}`, data) export const updateSite = (siteKey, data) => adminHttp.patch(`/admin/sites/${siteKey}`, data)
export const deleteSite = (siteKey) => adminHttp.delete(`/admin/sites/${siteKey}`)

View File

@@ -3,5 +3,6 @@ import { adminHttp } from './http'
export const getSystems = () => adminHttp.get('/admin/systems') export const getSystems = () => adminHttp.get('/admin/systems')
export const createSystem = (data) => adminHttp.post('/admin/systems', data) export const createSystem = (data) => adminHttp.post('/admin/systems', data)
export const updateSystem = (systemKey, data) => adminHttp.patch(`/admin/systems/${systemKey}`, data) export const updateSystem = (systemKey, data) => adminHttp.patch(`/admin/systems/${systemKey}`, data)
export const deleteSystem = (systemKey) => adminHttp.delete(`/admin/systems/${systemKey}`)
export const getSystemGroups = (systemKey) => adminHttp.get(`/admin/systems/${systemKey}/groups`) export const getSystemGroups = (systemKey) => adminHttp.get(`/admin/systems/${systemKey}/groups`)
export const getSystemMembers = (systemKey) => adminHttp.get(`/admin/systems/${systemKey}/members`) export const getSystemMembers = (systemKey) => adminHttp.get(`/admin/systems/${systemKey}/members`)

View File

@@ -31,6 +31,7 @@
<el-button size="small" :type="row.status === 'active' ? 'danger' : 'success'" @click="toggleStatus(row)"> <el-button size="small" :type="row.status === 'active' ? 'danger' : 'success'" @click="toggleStatus(row)">
{{ row.status === 'active' ? '停用' : '啟用' }} {{ row.status === 'active' ? '停用' : '啟用' }}
</el-button> </el-button>
<el-button size="small" type="danger" @click="handleDelete(row)">刪除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -101,7 +102,7 @@
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { Refresh } from '@element-plus/icons-vue' import { Refresh } from '@element-plus/icons-vue'
import { createApiClient, getApiClients, rotateApiClientKey, updateApiClient } from '@/api/api-clients' import { createApiClient, getApiClients, rotateApiClientKey, updateApiClient, deleteApiClient } from '@/api/api-clients'
const items = ref([]) const items = ref([])
const loading = ref(false) const loading = ref(false)
@@ -278,5 +279,17 @@ async function toggleStatus(row) {
} }
} }
async function handleDelete(row) {
try {
await ElMessageBox.confirm(`確認刪除 API Client ${row.name}${row.client_key}`, '刪除確認', { type: 'warning' })
await deleteApiClient(row.client_key)
ElMessage.success('刪除成功')
await load()
} catch (err) {
if (err === 'cancel') return
ElMessage.error(err.response?.data?.detail || '刪除失敗')
}
}
onMounted(load) onMounted(load)
</script> </script>

View File

@@ -13,10 +13,11 @@
<el-table-column prop="company_key" label="Company Key" width="220" /> <el-table-column prop="company_key" label="Company Key" width="220" />
<el-table-column prop="name" label="名稱" min-width="200" /> <el-table-column prop="name" label="名稱" min-width="200" />
<el-table-column prop="status" label="狀態" width="120" /> <el-table-column prop="status" label="狀態" width="120" />
<el-table-column label="操作" width="200"> <el-table-column label="操作" width="280">
<template #default="{ row }"> <template #default="{ row }">
<el-button size="small" @click="openEdit(row)">編輯</el-button> <el-button size="small" @click="openEdit(row)">編輯</el-button>
<el-button size="small" @click="openSites(row)">站台</el-button> <el-button size="small" @click="openSites(row)">站台</el-button>
<el-button size="small" type="danger" @click="handleDelete(row)">刪除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -64,9 +65,9 @@
<script setup> <script setup>
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus } from '@element-plus/icons-vue' import { Plus } from '@element-plus/icons-vue'
import { getCompanies, createCompany, updateCompany, getCompanySites } from '@/api/companies' import { getCompanies, createCompany, updateCompany, deleteCompany, getCompanySites } from '@/api/companies'
const companies = ref([]) const companies = ref([])
const loading = ref(false) const loading = ref(false)
@@ -163,5 +164,21 @@ async function openSites(row) {
} }
} }
async function handleDelete(row) {
try {
await ElMessageBox.confirm(
`確認刪除公司 ${row.name}${row.company_key})?此操作會一併刪除旗下站台與相關授權。`,
'刪除確認',
{ type: 'warning' }
)
await deleteCompany(row.company_key)
ElMessage.success('刪除成功')
await load()
} catch (err) {
if (err === 'cancel') return
ElMessage.error(err.response?.data?.detail || '刪除失敗')
}
}
onMounted(load) onMounted(load)
</script> </script>

View File

@@ -24,6 +24,7 @@
<template #default="{ row }"> <template #default="{ row }">
<el-button size="small" @click="openEdit(row)">編輯</el-button> <el-button size="small" @click="openEdit(row)">編輯</el-button>
<el-button size="small" type="warning" @click="handleResetPassword(row)">重設密碼</el-button> <el-button size="small" type="warning" @click="handleResetPassword(row)">重設密碼</el-button>
<el-button size="small" type="danger" @click="handleDelete(row)">刪除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -71,12 +72,13 @@
<script setup> <script setup>
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { Refresh } from '@element-plus/icons-vue' import { Refresh } from '@element-plus/icons-vue'
import { import {
getMembers, getMembers,
upsertMember, upsertMember,
updateMember, updateMember,
deleteMember,
resetMemberPassword, resetMemberPassword,
getMemberPermissionGroups, getMemberPermissionGroups,
setMemberPermissionGroups setMemberPermissionGroups
@@ -234,5 +236,21 @@ async function handleResetPassword(row) {
} }
} }
async function handleDelete(row) {
try {
await ElMessageBox.confirm(
`確認刪除會員 ${row.display_name || row.email || row.username || row.authentik_sub}`,
'刪除確認',
{ type: 'warning' }
)
await deleteMember(row.authentik_sub)
ElMessage.success('刪除成功')
await load()
} catch (err) {
if (err === 'cancel') return
ElMessage.error(err.response?.data?.detail || '刪除失敗')
}
}
onMounted(load) onMounted(load)
</script> </script>

View File

@@ -29,6 +29,7 @@
<el-button size="small" @click="openEdit(row)">編輯</el-button> <el-button size="small" @click="openEdit(row)">編輯</el-button>
<el-button size="small" @click="openRelations(row, 'groups')">群組</el-button> <el-button size="small" @click="openRelations(row, 'groups')">群組</el-button>
<el-button size="small" @click="openRelations(row, 'members')">會員</el-button> <el-button size="small" @click="openRelations(row, 'members')">會員</el-button>
<el-button size="small" type="danger" @click="handleDelete(row)">刪除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -102,9 +103,9 @@
<script setup> <script setup>
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus } from '@element-plus/icons-vue' import { Plus } from '@element-plus/icons-vue'
import { getModules, createModule, updateModule, getModuleGroups, getModuleMembers } from '@/api/modules' import { getModules, createModule, updateModule, deleteModule, getModuleGroups, getModuleMembers } from '@/api/modules'
import { getSystems } from '@/api/systems' import { getSystems } from '@/api/systems'
const modules = ref([]) const modules = ref([])
@@ -200,6 +201,18 @@ async function handleEdit() {
} }
} }
async function handleDelete(row) {
try {
await ElMessageBox.confirm(`確認刪除模組 ${row.name}${row.module_key}`, '刪除確認', { type: 'warning' })
await deleteModule(row.module_key)
ElMessage.success('刪除成功')
await load()
} catch (err) {
if (err === 'cancel') return
ElMessage.error(err.response?.data?.detail || '刪除失敗')
}
}
async function openRelations(row, tab) { async function openRelations(row, tab) {
relationModuleKey.value = row.module_key relationModuleKey.value = row.module_key
relationTab.value = tab relationTab.value = tab

View File

@@ -17,10 +17,11 @@
<el-table-column prop="group_key" label="Group Key" width="180" /> <el-table-column prop="group_key" label="Group Key" width="180" />
<el-table-column prop="name" label="群組名稱" min-width="220" /> <el-table-column prop="name" label="群組名稱" min-width="220" />
<el-table-column prop="status" label="狀態" width="120" /> <el-table-column prop="status" label="狀態" width="120" />
<el-table-column label="操作" width="220"> <el-table-column label="操作" width="300">
<template #default="{ row }"> <template #default="{ row }">
<el-button size="small" @click="openEditGroup(row)">編輯</el-button> <el-button size="small" @click="openEditGroup(row)">編輯</el-button>
<el-button size="small" @click="openBindingDialog(row)">設定關聯</el-button> <el-button size="small" @click="openBindingDialog(row)">設定關聯</el-button>
<el-button size="small" type="danger" @click="handleDeleteGroup(row)">刪除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -128,12 +129,13 @@
<script setup> <script setup>
import { ref, reactive, onMounted, computed } from 'vue' import { ref, reactive, onMounted, computed } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus } from '@element-plus/icons-vue' import { Plus } from '@element-plus/icons-vue'
import { import {
getPermissionGroups, getPermissionGroups,
createPermissionGroup, createPermissionGroup,
updatePermissionGroup, updatePermissionGroup,
deletePermissionGroup,
getPermissionGroupPermissions, getPermissionGroupPermissions,
getPermissionGroupBindings, getPermissionGroupBindings,
updatePermissionGroupBindings updatePermissionGroupBindings
@@ -283,6 +285,18 @@ async function handleEditGroup() {
} }
} }
async function handleDeleteGroup(row) {
try {
await ElMessageBox.confirm(`確認刪除群組 ${row.name}${row.group_key}`, '刪除確認', { type: 'warning' })
await deletePermissionGroup(row.group_key)
ElMessage.success('刪除成功')
await loadGroups()
} catch (err) {
if (err === 'cancel') return
ElMessage.error(err.response?.data?.detail || '刪除失敗')
}
}
async function openBindingDialog(row) { async function openBindingDialog(row) {
bindingGroupKey.value = row.group_key bindingGroupKey.value = row.group_key
showBindingDialog.value = true showBindingDialog.value = true

View File

@@ -16,9 +16,10 @@
</el-table-column> </el-table-column>
<el-table-column prop="name" label="名稱" min-width="180" /> <el-table-column prop="name" label="名稱" min-width="180" />
<el-table-column prop="status" label="狀態" width="120" /> <el-table-column prop="status" label="狀態" width="120" />
<el-table-column label="操作" width="120"> <el-table-column label="操作" width="200">
<template #default="{ row }"> <template #default="{ row }">
<el-button size="small" @click="openEdit(row)">編輯</el-button> <el-button size="small" @click="openEdit(row)">編輯</el-button>
<el-button size="small" type="danger" @click="handleDelete(row)">刪除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -64,9 +65,9 @@
<script setup> <script setup>
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus } from '@element-plus/icons-vue' import { Plus } from '@element-plus/icons-vue'
import { getSites, createSite, updateSite } from '@/api/sites' import { getSites, createSite, updateSite, deleteSite } from '@/api/sites'
import { getCompanies } from '@/api/companies' import { getCompanies } from '@/api/companies'
const sites = ref([]) const sites = ref([])
@@ -166,5 +167,21 @@ async function handleEdit() {
} }
} }
async function handleDelete(row) {
try {
await ElMessageBox.confirm(
`確認刪除站台 ${row.name}${row.site_key})?此操作會清理相關授權。`,
'刪除確認',
{ type: 'warning' }
)
await deleteSite(row.site_key)
ElMessage.success('刪除成功')
await load()
} catch (err) {
if (err === 'cancel') return
ElMessage.error(err.response?.data?.detail || '刪除失敗')
}
}
onMounted(load) onMounted(load)
</script> </script>

View File

@@ -26,6 +26,7 @@
<el-button size="small" @click="openEdit(row)">編輯</el-button> <el-button size="small" @click="openEdit(row)">編輯</el-button>
<el-button size="small" @click="openRelations(row, 'groups')">群組</el-button> <el-button size="small" @click="openRelations(row, 'groups')">群組</el-button>
<el-button size="small" @click="openRelations(row, 'members')">會員</el-button> <el-button size="small" @click="openRelations(row, 'members')">會員</el-button>
<el-button size="small" type="danger" @click="handleDelete(row)">刪除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -94,9 +95,9 @@
<script setup> <script setup>
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus } from '@element-plus/icons-vue' import { Plus } from '@element-plus/icons-vue'
import { getSystems, createSystem, updateSystem, getSystemGroups, getSystemMembers } from '@/api/systems' import { getSystems, createSystem, updateSystem, deleteSystem, getSystemGroups, getSystemMembers } from '@/api/systems'
const systems = ref([]) const systems = ref([])
const loading = ref(false) const loading = ref(false)
@@ -188,6 +189,18 @@ async function handleEdit() {
} }
} }
async function handleDelete(row) {
try {
await ElMessageBox.confirm(`確認刪除系統 ${row.name}${row.system_key}`, '刪除確認', { type: 'warning' })
await deleteSystem(row.system_key)
ElMessage.success('刪除成功')
await load()
} catch (err) {
if (err === 'cancel') return
ElMessage.error(err.response?.data?.detail || '刪除失敗')
}
}
async function openRelations(row, tab) { async function openRelations(row, tab) {
relationSystemKey.value = row.system_key relationSystemKey.value = row.system_key
relationTab.value = tab relationTab.value = tab