Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { describe, expect, it, vi } from 'vitest'
import { flushPromises, mount } from '@vue/test-utils'
import { ElMessage, ElMessageBox } from 'element-plus'
import AdminUserDetail from '@/components/admin_next/users/AdminUserDetail.vue'

const { deleteUser, fetchUser, routerPush, useFetchApiMock } = vi.hoisted(() => ({
deleteUser: vi.fn(),
fetchUser: vi.fn(),
routerPush: vi.fn(),
useFetchApiMock: vi.fn()
}))

vi.mock('element-plus', () => ({
ElMessage: Object.assign(vi.fn(), {
error: vi.fn(),
success: vi.fn()
}),
ElMessageBox: {
confirm: vi.fn()
}
}))

vi.mock('vue-router', () => ({
useRoute: () => ({ params: { id: 'user-uuid' } }),
useRouter: () => ({ push: routerPush })
}))

vi.mock('@/components/admin_next/router', () => ({
BASE_URL: '/admin_panel'
}))

vi.mock('@/stores/UserStore', () => ({
default: () => ({ username: 'admin-user' })
}))

vi.mock('@/packs/useFetchApi', () => ({
default: useFetchApiMock
}))

const createWrapper = () => mount(AdminUserDetail, {
global: {
stubs: {
Container: { template: '<section><slot /></section>' },
Card: { template: '<article><slot /><footer><slot name="footer" /></footer></article>' },
CsgButton: {
inheritAttrs: false,
props: ['name'],
template: '<button :class="$attrs.class" @click="$emit(\'click\')">{{ name }}</button>'
}
}
}
})

describe('AdminUserDetail', () => {
it('deletes the user after confirmation and returns to the user list', async () => {
fetchUser.mockResolvedValue({
data: {
value: {
data: {
uuid: 'user-uuid',
username: 'target-user',
roles: []
}
}
}
})
deleteUser.mockResolvedValue({ data: { value: true }, error: { value: null } })
ElMessageBox.confirm.mockResolvedValue()
useFetchApiMock.mockImplementation((url) => {
if (url === '/user/user-uuid') {
return { json: fetchUser }
}

return { delete: () => ({ json: deleteUser }) }
})

const wrapper = createWrapper()
await flushPromises()

await wrapper.find('.btn-danger').trigger('click')
await flushPromises()

expect(ElMessageBox.confirm).toHaveBeenCalledOnce()
expect(useFetchApiMock).toHaveBeenCalledWith('/user/target-user?current_user=admin-user')
expect(deleteUser).toHaveBeenCalledOnce()
expect(ElMessage.success).toHaveBeenCalled()
expect(routerPush).toHaveBeenCalledWith('/admin_panel/users')
})
})
46 changes: 43 additions & 3 deletions frontend/src/components/admin_next/users/AdminUserDetail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
</ul>
<template #footer>
<CsgButton class="btn btn-primary btn-md" name="Edit" @click="dialogFormVisible = true" />
<CsgButton
class="btn btn-danger btn-md"
:name="$t('admin.users.destroyConfirmBtn')"
@click="destroyUser" />
</template>
</Card>

Expand Down Expand Up @@ -81,13 +85,19 @@
<script setup>
import { Container, Pagination, Table, Card } from '../admin_component'
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { useRoute, useRouter } from 'vue-router'
import useFetchApi from '../../../packs/useFetchApi'
import dayjs from 'dayjs'
import { ElMessage } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import { BASE_URL } from '../router'
import useUserStore from '../../../stores/UserStore'
import { useI18n } from 'vue-i18n'

const { t } = useI18n()

const userStore = useUserStore()
const route = useRoute()
const router = useRouter()

const user = ref({})

Expand All @@ -108,6 +118,36 @@
}
}

const destroyUser = () => {
ElMessageBox.confirm(
t('admin.users.deleteUserConfirm', '此操作将永久删除该用户, 是否继续?'),
t('admin.users.deleteUserTitle', '提示'),
{
confirmButtonText: t('admin.users.confirm', '确定'),
cancelButtonText: t('admin.users.cancel', '取消'),
type: 'warning',
}
).then(async () => {
const res = await useFetchApi(`/user/${user.value.username}?current_user=${userStore.username}`).delete().json()

if (res.data?.value) {
ElMessage.success(t('admin.users.destroySuccess', '删除成功!'))
router.push('/admin_panel/users')
} else {
if(res.error?.value?.msg) {
ElMessage.error(res.error.value?.msg)
} else {
ElMessage.error(res.error.value?.msg || t('admin.users.destroyFailed', '删除失败!'))
}
}
}).catch(() => {
ElMessage({
type: 'info',
message: t('admin.users.deleteCanceled', '已取消删除'),
});
});
}

const submitUserForm = async () => {
const { data } = await useFetchApi(`/user/${user.value.uuid}`, {
headers: {
Expand Down Expand Up @@ -139,4 +179,4 @@
:deep(.el-dialog__header) {
padding-bottom: 47px;
}
</style>
</style>
10 changes: 9 additions & 1 deletion frontend/src/locales/en_js/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,15 @@ export const admin = {
userDetailTitle: 'User Detail',
editBtn: 'Edit',
editCancelBtn: 'Cancel',
editConfirmBtn: 'Confirm'
editConfirmBtn: 'Confirm',
destroyConfirmBtn: 'Delete',
deleteUserConfirm: 'This action will permanently delete the user. Continue?',
deleteUserTitle: 'Warning',
confirm: 'Confirm',
cancel: 'Cancel',
destroySuccess: 'User deleted successfully!',
destroyFailed: 'Failed to delete user!',
deleteCanceled: 'User deletion canceled'
},
models: {
title: 'Model',
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/locales/zh_hant_js/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,11 @@ export const admin = {
editCancelBtn: '取消',
editConfirmBtn: '確認',
destroyConfirmBtn: '刪除',
deleteUserConfirm: '此操作將永久刪除該用戶,是否繼續?',
deleteUserTitle: '提示',
destroySuccess: '刪除成功!',
destroyFailed: '刪除失敗!',
deleteCanceled: '已取消刪除',
lastLoginAt: '最後登入',
updateUserTags: '更新用戶標籤',
userTag: '用戶標籤',
Expand Down Expand Up @@ -919,4 +924,4 @@ export const admin = {
formatError: '格式錯誤:請輸入正確的相對路徑格式 (如: "namespace/repo_name")',
addItem: '添加項目'
}
}
}
12 changes: 10 additions & 2 deletions frontend/src/locales/zh_js/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,15 @@ export const admin = {
userDetailTitle: '用户详情',
editBtn: '编辑',
editCancelBtn: '取消',
editConfirmBtn: '确认'
editConfirmBtn: '确认',
destroyConfirmBtn: '删除',
deleteUserConfirm: '此操作将永久删除该用户,是否继续?',
deleteUserTitle: '提示',
confirm: '确定',
cancel: '取消',
destroySuccess: '删除成功!',
destroyFailed: '删除失败!',
deleteCanceled: '已取消删除'
},
models: {
title: '模型管理',
Expand Down Expand Up @@ -538,4 +546,4 @@ export const admin = {
formatError: '格式错误:请输入正确的相对路径格式 (如: "namespace/repo_name")',
addItem: '添加项目'
}
}
}
Loading