102 lines
5.7 KiB
Vue
102 lines
5.7 KiB
Vue
<template>
|
||
<section class="page-container">
|
||
<div class="page-heading">
|
||
<div><h1>用户与权限</h1><p>管理谁可以配置设备、视频和区域;权限由后端最终校验。</p></div>
|
||
</div>
|
||
<div class="search-toolbar">
|
||
<el-form :inline="true">
|
||
<el-form-item label="用户名"><el-input v-model="keyword" clearable placeholder="搜索用户名或姓名" /></el-form-item>
|
||
<el-form-item><el-button type="primary" :icon="Search">查询</el-button><el-button @click="keyword = ''">重置</el-button></el-form-item>
|
||
</el-form>
|
||
</div>
|
||
<div class="table-container">
|
||
<div class="table-actions">
|
||
<strong>账户列表</strong>
|
||
<el-button v-if="canWrite" type="primary" :icon="Plus" @click="openCreate">新增用户</el-button>
|
||
</div>
|
||
<el-table v-loading="loading" :data="filteredUsers" border>
|
||
<el-table-column prop="username" label="用户名" min-width="130" />
|
||
<el-table-column prop="display_name" label="姓名" min-width="140" />
|
||
<el-table-column label="角色" width="150"><template #default="scope"><el-tag>{{ roleLabel(scope.row.role) }}</el-tag></template></el-table-column>
|
||
<el-table-column label="状态" width="100"><template #default="scope"><el-tag :type="scope.row.enabled ? 'success' : 'info'">{{ scope.row.enabled ? '启用' : '停用' }}</el-tag></template></el-table-column>
|
||
<el-table-column prop="updated_at" label="更新时间" width="190" />
|
||
<el-table-column v-if="canWrite" label="操作" width="100" fixed="right"><template #default="scope"><el-button link type="primary" @click="openEdit(scope.row)">编辑</el-button></template></el-table-column>
|
||
</el-table>
|
||
<div class="pagination"><el-pagination background layout="total, prev, pager, next" :total="filteredUsers.length" :page-size="20" /></div>
|
||
</div>
|
||
|
||
<el-dialog v-model="dialogVisible" :title="editingID ? '编辑用户' : '新增用户'" width="520px" destroy-on-close>
|
||
<el-form ref="formRef" :model="form" :rules="rules" label-width="92px">
|
||
<el-form-item label="用户名" prop="username"><el-input v-model="form.username" :disabled="Boolean(editingID)" /></el-form-item>
|
||
<el-form-item label="姓名" prop="display_name"><el-input v-model="form.display_name" /></el-form-item>
|
||
<el-form-item label="角色" prop="role"><el-select v-model="form.role" style="width: 100%"><el-option v-for="role in roles" :key="role.value" :label="role.label" :value="role.value" /></el-select></el-form-item>
|
||
<el-form-item v-if="!editingID" label="初始密码" prop="password"><el-input v-model="form.password" type="password" show-password /><div class="field-hint">至少 6 位字符。</div></el-form-item>
|
||
<el-form-item v-else label="状态"><el-switch v-model="form.enabled" active-text="启用" inactive-text="停用" /></el-form-item>
|
||
</el-form>
|
||
<template #footer><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" :loading="saving" @click="save">保存</el-button></template>
|
||
</el-dialog>
|
||
</section>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, onMounted, reactive, ref } from 'vue'
|
||
import { ElMessage } from 'element-plus'
|
||
import { Plus, Search } from '@element-plus/icons-vue'
|
||
import { useStore } from 'vuex'
|
||
import { createUser, listUsers, updateUser } from '../../../api/sense/identity'
|
||
|
||
const store = useStore()
|
||
const loading = ref(false)
|
||
const saving = ref(false)
|
||
const keyword = ref('')
|
||
const users = ref([])
|
||
const dialogVisible = ref(false)
|
||
const editingID = ref('')
|
||
const formRef = ref()
|
||
const roles = [
|
||
{ value: 'administrator', label: '系统管理员' },
|
||
{ value: 'operator', label: '实施/运维' },
|
||
{ value: 'site_admin', label: '站点管理员' },
|
||
{ value: 'viewer', label: '只读用户' }
|
||
]
|
||
const form = reactive({ username: '', display_name: '', role: 'viewer', password: '', enabled: true })
|
||
const rules = {
|
||
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
||
display_name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
||
role: [{ required: true, message: '请选择角色', trigger: 'change' }],
|
||
password: [{ required: true, min: 6, message: '请输入至少 6 位初始密码', trigger: 'blur' }]
|
||
}
|
||
const canWrite = computed(() => store.getters['sense-identity/hasPermission']?.('identity.users.write'))
|
||
const filteredUsers = computed(() => {
|
||
const value = keyword.value.trim().toLowerCase()
|
||
return value ? users.value.filter((item) => `${item.username} ${item.display_name}`.toLowerCase().includes(value)) : users.value
|
||
})
|
||
|
||
function roleLabel(value) { return roles.find((item) => item.value === value)?.label || value }
|
||
function resetForm() { Object.assign(form, { username: '', display_name: '', role: 'viewer', password: '', enabled: true }) }
|
||
function openCreate() { editingID.value = ''; resetForm(); dialogVisible.value = true }
|
||
function openEdit(user) { editingID.value = user.id; Object.assign(form, { ...user, password: '' }); dialogVisible.value = true }
|
||
|
||
async function load() {
|
||
loading.value = true
|
||
try { users.value = (await listUsers()).items || [] } finally { loading.value = false }
|
||
}
|
||
async function save() {
|
||
const valid = await formRef.value?.validate().catch(() => false)
|
||
if (!valid) return
|
||
saving.value = true
|
||
try {
|
||
if (editingID.value) await updateUser(editingID.value, { display_name: form.display_name, role: form.role, enabled: form.enabled })
|
||
else await createUser(form)
|
||
ElMessage.success('保存成功')
|
||
dialogVisible.value = false
|
||
await load()
|
||
} catch (error) { ElMessage.error(error.message || '保存失败') } finally { saving.value = false }
|
||
}
|
||
onMounted(load)
|
||
</script>
|
||
|
||
<style scoped>
|
||
.field-hint { color: #86909c; font-size: 12px; line-height: 1.5; }
|
||
</style>
|