You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/oaweb/pages/app/[id]/auth.vue

310 lines
8.6 KiB
Vue

<!--
* auth.vue
* Copyright (C) 2024 veypi <i@veypi.com>
* 2024-06-12 18:38
* Distributed under terms of the MIT license.
-->
<template>
<div class="">
<div class="flex justify-between">
<div class="text-3xl">角色管理</div>
<div @click="created(0)"></div>
</div>
<div class="w-full">
<crud vertical :keys="role_keys" :data="role" @update="update($event, role, api.app.RolePatch, app.id)">
<template #k_created_at="{ value }">{{ util.datetostr(value) }}</template>
<template #k__="{ row }">
<div color="primary" size="sm" @click="show_dialog(0,
row.id)">权限</div>
<div color="secondary" size="sm" @click="show_dialog(1,
row.id)">用户</div>
<div color="negative" size="sm" @click="del(0, row.id)">删除</div>
<template v-if="row.id === app.init_role_id">
<div color="positive" disable size="sm">初始角色</div>
</template>
<template v-else>
<div size="sm" @click="api.app.Patch(app.id, {
init_role_id:
row.id
}).then(_ => app.init_role_id = row.id)">设置为初始角色</div>
</template>
</template>
</crud>
</div>
<div class="flex mt-16 justify-between">
<div class="text-3xl">资源管理</div>
<div @click="created(1)">创建资源</div>
</div>
<div class="w-full">
<crud vertical :keys="resource_keys" :data="resource"
@update="update($event, resource, api.app.ResourcePatch, app.id)">
<template #k_created_at="{ value }">{{ util.datetostr(value) }}</template>
<template #k__="{ row }">
<div color="negative" size="sm" @click="del(1, row.name)">删除</div>
</template>
</crud>
</div>
<UModal v-model="dialog" :ui="{ 'width': '' }">
<UCard v-if="dialog_obj" style="width: 1000px;max-width: 90vw;">
<template #header>
<div class="items-center flex">
<div class="text-2xl">{{ dialog_obj.name }}
</div>
<div class="flex-grow"></div>
<div v-if="dialog_mode">
<vinput :model-value="''" type="select" :options="['1', '2']" select-none="添加用户"></vinput>
<!-- <USelect filled :model-value="''" @update:model-value="roleuser.add" use-input hide-selected fill-input -->
<!-- input-debounce="0" label="添加用户" :options="users_cache" @filter="filterFn" style="width: 20rem"> -->
<!-- </USelect> -->
</div>
<div v-else @click="created(2, { role_id: dialog_obj.id })">添加权限</div>
</div>
</template>
<div v-if="dialog_mode">
<crud vertical :keys="users_keys" :data="users">
<template #k__="{ row }">
<div color="negative" size="sm" @click="roleuser.drop(row)"></div>
</template>
</crud>
</div>
<div v-else>
<crud vertical :keys="access_keys" :data="access" @update="update($event, access, api.access.Patch)">
<template #k__="{ row }">
<div color="negative" size="sm" @click="del(2, row.id)">删除</div>
</template>
</crud>
</div>
</UCard>
</UModal>
</div>
</template>
<script lang="ts" setup>
import type { models } from '#imports';
import msg from '@veypi/msg';
let dialog = ref(false)
let dialog_mode = ref(0)
let dialog_obj = ref<models.Role>({} as any)
const role_keys = ref<any>([
{
name: 'id',
label: 'ID',
},
{ name: 'name', label: '标识符' },
{ name: 'des', label: '描述', editable: true },
{ name: 'created_at', label: '创建时间' },
{ name: 'user_count', label: '绑定用户数' },
{
name: '_', label: '操作', style: { 'justify-content': 'start' },
width: 40
},
])
const resource_keys = ref<any>([
{ name: 'name', label: '标识符' },
{ name: 'des', label: '描述', editable: true },
{ name: 'created_at', label: '创建时间' },
{
name: '_', label: '操作', style: { 'justify-content': 'start' },
width: 40
},
])
const access_keys = ref<any>([
{ name: 'id', label: 'ID' },
{ name: 'name', label: '标识符' },
{ name: 'rid', label: '特定子资源id', editable: true },
{
name: 'level', label: '权限等级', editable: true, typ:
ArgType.Select, options: LevelOptions,
},
{
name: '_', label: '操作', style: { 'justify-content': 'start' },
width: 40
},
])
const users_keys = ref<any>([
{ name: 'id', label: 'ID' },
{ name: 'username', label: '用户名' },
{ name: 'nickname', label: '昵称' },
{
name: '_', label: '操作', style: { 'justify-content': 'start' },
width: 40
},
])
let access = ref<models.Access[]>([])
let users = ref<models.UserRole[]>([])
let users_cache = ref<any[]>([])
const resource = ref<any[]>([])
const role = ref<any[]>([])
const app = inject('app') as Ref<models.App>
watch(computed(() => app.value.id), (v) => {
sync(v)
})
const sync = (id: any) => {
if (!id) {
return
}
api.app.RoleList(id, {}).then(e => {
role.value = e
})
api.app.ResourceList(id).then(e => {
resource.value = e
})
}
const show_dialog = (mode: number, idx: string) => {
dialog_obj.value = role.value.find(e => e.id === idx)
dialog.value = true
dialog_mode.value = mode
if (mode) {
api.user.UserRoleList('-', { role_id: idx }).then(e => {
console.log(e)
users.value = e
})
} else {
api.access.List({ app_id: app.value.id, role_id: idx }).then(e => {
access.value = e
})
}
}
// 0: role 1: resource 2: access 3: users
const crud_option = (mode: number) => {
let res = {
api: api.app.RolePatch(app.value.id),
lable: '角色',
obj: role,
}
if (mode === 1) {
res.api = api.resource(app.value.id) as any
res.lable = '资源'
res.obj = resource
} else if (mode === 2) {
res.api = api.access(app.value.id) as any
res.lable = '权限'
res.obj = access
} else if (mode === 3) {
res.api = api.user as any
res.lable = '用户'
res.obj = users
}
return res
}
const created = (k: number, props?: any) => {
let opt = crud_option(k)
let options;
let prompt;
if (k !== 2) {
prompt = { model: '', type: 'text' }
} else {
console.log(k)
options = {
model: '', type: 'radio', items: resource.value.map(e => {
return {
label: e.name,
value: e.name
}
})
}
}
// $q.dialog({
// title: '创建' + opt.lable,
// message: '请输入标识码',
// prompt: prompt as any,
// options: options as any,
// cancel: true,
// persistent: true
// }).onOk(data => {
// opt.api.create(data, props).then(e => {
// msg.Info('创建成功')
// opt.obj.value.push(e)
// }).catch(e => {
// msg.Warn('创建失败: ' + e)
// })
// })
}
const update = (props: any[], objs: any, cb: any, topid?: string) => {
// let opt = crud_option(k)
let patch = cb
if (topid) {
patch = (id: string, props: any) => {
return cb(topid, id, props)
}
}
for (let i in props) {
let id = objs[i].id
patch(id, props[i]).then(() => {
Object.assign(objs[i], props[i])
}).catch((e: any) => {
msg.Warn('更新失败: ' + e)
})
}
}
const del = (k: number, id: string) => {
let opt = crud_option(k)
$q.dialog({
title: '是否确定删除',
message: '',
cancel: true,
persistent: true
}).onOk(() => {
opt.api.del(id).then(e => {
msg.Info('删除成功')
opt.obj.value.splice(opt.obj.value.findIndex(e => e.name === id
|| e.id === id), 1)
}).catch(e => {
msg.Warn('删除失败: ' + e)
})
})
}
const roleuser = {
add: (u: any) => {
let idx = users.value.findIndex(e => e.id == u.id)
if (idx >= 0) {
return
}
api.role(app.value.id).add(dialog_obj.value?.id || '', u.id).then(e => {
users.value.push(u)
dialog_obj.value.user_count = dialog_obj.value?.user_count + 1
})
},
drop: (u: any) => {
api.role(app.value.id).drop(dialog_obj.value?.id || '', u.id).then(e => {
users.value.splice(users.value.findIndex(e => e.id === u.id), 1)
dialog_obj.value.user_count = dialog_obj.value?.user_count - 1
})
}
}
const filterFn = (val: string, cb: any) => {
if (val && val.length > 1) {
api.user.list({ name: val }).then((e: modelsUser[]) => {
cb(() => {
users_cache.value = e.map(i => {
return Object.assign({
name: i.username,
value: i.id,
}, i)
})
})
})
}
}
onMounted(() => {
sync(app.value.id)
})
</script>
<style scoped></style>