Files
yovision/Sense/ui/src/views/sense/identity/Users.vue
T

102 lines
5.7 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>
<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>