Files
sino-mci/admin-web/src/views/UserManagement.vue

476 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<PageCard>
<template #toolbar-left>
<el-select
v-model="selectedTenant"
placeholder="选择租户"
clearable
style="width: 200px"
:disabled="!isPlatformAdmin"
@change="loadUsers"
>
<el-option
v-for="t in tenants"
:key="t.id"
:label="`${t.tenantName}${t.tenantCode}`"
:value="t.tenantCode"
/>
</el-select>
<el-input
v-model="searchText"
placeholder="搜索用户名 / 姓名 / 手机号"
clearable
style="width: 240px"
@keyup.enter="handleSearch"
/>
</template>
<template #toolbar-right>
<el-button type="primary" @click="openCreateDialog">新增用户</el-button>
</template>
<el-table :data="pagedUsers" @row-click="openEditDrawer" class="sino-table">
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="tenantCode" label="租户编码" min-width="140" />
<el-table-column prop="username" label="用户名" min-width="140" />
<el-table-column prop="realName" label="真实姓名" min-width="120" />
<el-table-column prop="phone" label="手机号" min-width="140" />
<el-table-column prop="roleCodes" label="角色" min-width="180">
<template #default="{ row }">
{{ formatRoles(row.roleCodes) }}
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="120">
<template #default="{ row }">
<div class="status-cell" @click.stop>
<el-switch
v-model="row.status"
:active-value="1"
:inactive-value="0"
inline-prompt
active-text="启用"
inactive-text="禁用"
@change="(val: any) => handleStatusChange(row, val)"
/>
</div>
</template>
</el-table-column>
</el-table>
<template #pagination>
<el-pagination
v-model:current-page="page"
v-model:page-size="pageSize"
:total="totalCount"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
/>
</template>
<!-- 新增用户弹窗 -->
<el-dialog v-model="dialogVisible" title="新增用户" width="600px">
<el-form :model="dialogForm" label-width="100px">
<el-form-item label="租户">
<el-select v-model="dialogForm.tenantCode" style="width: 100%">
<el-option
v-for="t in tenants"
:key="t.id"
:label="`${t.tenantName}${t.tenantCode}`"
:value="t.tenantCode"
/>
</el-select>
</el-form-item>
<el-form-item label="用户名">
<el-input v-model="dialogForm.username" placeholder="用户名" />
</el-form-item>
<el-form-item label="密码">
<el-input v-model="dialogForm.password" type="password" placeholder="密码" />
</el-form-item>
<el-form-item label="真实姓名">
<el-input v-model="dialogForm.realName" placeholder="真实姓名" />
</el-form-item>
<el-form-item label="手机号">
<el-input v-model="dialogForm.phone" placeholder="手机号" />
</el-form-item>
<el-form-item label="角色">
<el-select v-model="dialogForm.roleCodes" multiple placeholder="选择角色" style="width: 100%">
<el-option
v-for="r in roles"
:key="r.id"
:label="r.roleName"
:value="r.roleCode"
/>
</el-select>
</el-form-item>
<el-form-item label="状态">
<el-switch
v-model="dialogForm.status"
:active-value="1"
:inactive-value="0"
active-text="启用"
inactive-text="禁用"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSave">保存</el-button>
</template>
</el-dialog>
<!-- 用户详情 / 编辑抽屉 -->
<el-drawer v-model="drawerVisible" :title="drawerTitle" size="480px">
<el-form :model="drawerForm" label-width="100px" v-if="currentRow">
<el-form-item label="租户">
<el-select v-model="drawerForm.tenantCode" disabled style="width: 100%">
<el-option
v-for="t in tenants"
:key="t.id"
:label="`${t.tenantName}${t.tenantCode}`"
:value="t.tenantCode"
/>
</el-select>
</el-form-item>
<el-form-item label="用户名">
<el-input v-model="drawerForm.username" disabled />
</el-form-item>
<el-form-item label="真实姓名">
<el-input v-model="drawerForm.realName" placeholder="真实姓名" />
</el-form-item>
<el-form-item label="手机号">
<el-input v-model="drawerForm.phone" placeholder="手机号" />
</el-form-item>
<el-form-item label="角色">
<el-select v-model="drawerForm.roleCodes" multiple placeholder="选择角色" style="width: 100%">
<el-option
v-for="r in roles"
:key="r.id"
:label="r.roleName"
:value="r.roleCode"
/>
</el-select>
</el-form-item>
<el-form-item label="状态">
<el-switch
v-model="drawerForm.status"
:active-value="1"
:inactive-value="0"
active-text="启用"
inactive-text="禁用"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="drawer-footer">
<el-button type="danger" plain @click="handleDrawerDelete">删除</el-button>
<div class="drawer-actions">
<el-button @click="drawerVisible = false">取消</el-button>
<el-button type="primary" @click="handleDrawerSave">保存</el-button>
</div>
</div>
</template>
</el-drawer>
</PageCard>
</template>
<script setup lang="ts">
import { computed, onMounted, reactive, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useAuthStore } from '@/stores/auth'
import {
createUser,
deleteUser,
listRoles,
listTenants,
listUsers,
updateUser,
} from '@/api/account'
import PageCard from '@/components/PageCard.vue'
const authStore = useAuthStore()
const isPlatformAdmin = computed(() => authStore.roleCodes.includes('platform_admin'))
const users = ref<any[]>([])
const tenants = ref<any[]>([])
const roles = ref<any[]>([])
const selectedTenant = ref(authStore.tenantCode || '')
const dialogVisible = ref(false)
const isEdit = ref(false)
const editingId = ref<number | null>(null)
const dialogForm = reactive({
tenantCode: '',
username: '',
password: '',
realName: '',
phone: '',
roleCodes: [] as string[],
status: 1,
})
const searchText = ref('')
const drawerVisible = ref(false)
const currentRow = ref<any>(null)
const page = ref(1)
const pageSize = ref(10)
const drawerForm = reactive({
tenantCode: '',
username: '',
realName: '',
phone: '',
roleCodes: [] as string[],
status: 1,
})
const filteredUsers = computed(() => {
let list = users.value
if (searchText.value) {
const kw = searchText.value.toLowerCase()
list = list.filter(
(u) =>
(u.username || '').toLowerCase().includes(kw) ||
(u.realName || '').toLowerCase().includes(kw) ||
(u.phone || '').toLowerCase().includes(kw)
)
}
return list
})
const totalCount = computed(() => filteredUsers.value.length)
const pagedUsers = computed(() => {
const start = (page.value - 1) * pageSize.value
return filteredUsers.value.slice(start, start + pageSize.value)
})
const drawerTitle = computed(() =>
currentRow.value ? `编辑用户 - ${currentRow.value.username}` : '用户详情'
)
function handleSearch() {
page.value = 1
}
function handlePageChange(val: number) {
page.value = val
}
function handleSizeChange(val: number) {
pageSize.value = val
page.value = 1
}
function resetDialogForm() {
dialogForm.tenantCode = isPlatformAdmin.value ? '' : (authStore.tenantCode || '')
dialogForm.username = ''
dialogForm.password = ''
dialogForm.realName = ''
dialogForm.phone = ''
dialogForm.roleCodes = []
dialogForm.status = 1
}
function fillDialogForm(row: any) {
dialogForm.tenantCode = row.tenantCode ?? ''
dialogForm.username = row.username ?? ''
dialogForm.password = ''
dialogForm.realName = row.realName ?? ''
dialogForm.phone = row.phone ?? ''
dialogForm.roleCodes = row.roleCodes || []
dialogForm.status = row.status ?? 1
}
function resetDrawerForm() {
drawerForm.tenantCode = ''
drawerForm.username = ''
drawerForm.realName = ''
drawerForm.phone = ''
drawerForm.roleCodes = []
drawerForm.status = 1
}
function fillDrawerForm(row: any) {
drawerForm.tenantCode = row.tenantCode ?? ''
drawerForm.username = row.username ?? ''
drawerForm.realName = row.realName ?? ''
drawerForm.phone = row.phone ?? ''
drawerForm.roleCodes = row.roleCodes || []
drawerForm.status = row.status ?? 1
}
function openCreateDialog() {
isEdit.value = false
editingId.value = null
resetDialogForm()
dialogVisible.value = true
}
function openEditDialog(row: any) {
isEdit.value = true
editingId.value = row.id
fillDialogForm(row)
dialogVisible.value = true
}
function openEditDrawer(row: any) {
currentRow.value = row
fillDrawerForm(row)
drawerVisible.value = true
}
function formatRoles(codes: string[] | undefined) {
if (!codes || codes.length === 0) return '-'
return codes
.map((code) => {
const role = roles.value.find((r) => r.roleCode === code)
return role ? role.roleName : code
})
.join('')
}
async function loadTenants() {
try {
const res: any = await listTenants()
tenants.value = res.data || []
} catch (e: any) {
ElMessage.error(e.message)
}
}
async function loadRoles() {
try {
const res: any = await listRoles()
roles.value = res.data || []
} catch (e: any) {
ElMessage.error(e.message)
}
}
async function loadUsers() {
try {
const res: any = await listUsers(selectedTenant.value || undefined)
users.value = res.data || []
page.value = 1
} catch (e: any) {
ElMessage.error(e.message)
}
}
async function handleSave() {
if (!dialogForm.username) {
ElMessage.warning('请填写用户名')
return
}
if (!isEdit.value && !dialogForm.password) {
ElMessage.warning('请填写密码')
return
}
if (!dialogForm.tenantCode) {
ElMessage.warning('请选择租户')
return
}
try {
if (isEdit.value && editingId.value != null) {
await updateUser(editingId.value, {
realName: dialogForm.realName,
phone: dialogForm.phone,
status: dialogForm.status,
roleCodes: dialogForm.roleCodes,
})
ElMessage.success('更新成功')
} else {
await createUser(dialogForm.tenantCode, {
username: dialogForm.username,
password: dialogForm.password,
realName: dialogForm.realName,
phone: dialogForm.phone,
roleCodes: dialogForm.roleCodes,
})
ElMessage.success('创建成功')
}
dialogVisible.value = false
await loadUsers()
} catch (e: any) {
ElMessage.error(e.message)
}
}
async function handleStatusChange(row: any, val: any) {
try {
await updateUser(row.id, {
realName: row.realName,
phone: row.phone,
status: val,
roleCodes: row.roleCodes || [],
})
ElMessage.success('状态更新成功')
await loadUsers()
} catch (e: any) {
ElMessage.error(e.message)
await loadUsers()
}
}
async function handleDelete(row: any) {
try {
await ElMessageBox.confirm(
`确定删除用户「${row.username}${row.tenantCode})」吗?`,
'删除确认',
{ confirmButtonText: '删除', cancelButtonText: '取消', type: 'warning' }
)
await deleteUser(row.id)
ElMessage.success('删除成功')
await loadUsers()
} catch (e: any) {
if (e !== 'cancel') {
ElMessage.error(e.message || '删除失败')
}
}
}
async function handleDrawerSave() {
if (!currentRow.value) return
try {
await updateUser(currentRow.value.id, {
realName: drawerForm.realName,
phone: drawerForm.phone,
status: drawerForm.status,
roleCodes: drawerForm.roleCodes,
})
ElMessage.success('保存成功')
drawerVisible.value = false
await loadUsers()
} catch (e: any) {
ElMessage.error(e.message)
}
}
async function handleDrawerDelete() {
if (!currentRow.value) return
await handleDelete(currentRow.value)
drawerVisible.value = false
}
onMounted(() => {
loadTenants()
loadRoles()
loadUsers()
})
</script>
<style scoped>
.drawer-footer {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.drawer-actions {
display: flex;
gap: 12px;
}
</style>