v3
veypi 11 months ago
parent 5004d00afe
commit be16fc1c5c

@ -72,32 +72,38 @@ func accessGet(x *rest.X) (any, error) {
return data, err return data, err
} }
func accessPatch(x *rest.X) (any, error) { func accessDelete(x *rest.X) (any, error) {
opts := &M.AccessPatch{} opts := &M.AccessDelete{}
err := x.Parse(opts) err := x.Parse(opts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
data := &M.Access{} data := &M.Access{}
err = cfg.DB().Where("id = ?", opts.ID).First(data).Error err = cfg.DB().Where("id = ?", opts.ID).Delete(data).Error
if err != nil {
return nil, err
}
optsMap := make(map[string]interface{})
err = cfg.DB().Model(data).Updates(optsMap).Error
return data, err return data, err
} }
func accessDelete(x *rest.X) (any, error) { func accessPatch(x *rest.X) (any, error) {
opts := &M.AccessDelete{} opts := &M.AccessPatch{}
err := x.Parse(opts) err := x.Parse(opts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
data := &M.Access{} data := &M.Access{}
err = cfg.DB().Where("id = ?", opts.ID).Delete(data).Error err = cfg.DB().Where("id = ?", opts.ID).First(data).Error
if err != nil {
return nil, err
}
optsMap := make(map[string]interface{})
if opts.TID != nil {
optsMap["tid"] = opts.TID
}
if opts.Level != nil {
optsMap["level"] = opts.Level
}
err = cfg.DB().Model(data).Updates(optsMap).Error
return data, err return data, err
} }

@ -1,11 +1,9 @@
package user package user
import ( import (
"github.com/google/uuid"
"github.com/veypi/OneBD/rest" "github.com/veypi/OneBD/rest"
"oa/cfg" "oa/cfg"
M "oa/models" M "oa/models"
"strings"
) )
func useUserRole(r rest.Router) { func useUserRole(r rest.Router) {
@ -15,43 +13,65 @@ func useUserRole(r rest.Router) {
r.Patch("/:user_role_id", userRolePatch) r.Patch("/:user_role_id", userRolePatch)
r.Post("/", userRolePost) r.Post("/", userRolePost)
} }
func userRoleDelete(x *rest.X) (any, error) { func userRoleList(x *rest.X) (any, error) {
opts := &M.UserRoleDelete{} opts := &M.UserRoleList{}
err := x.Parse(opts) err := x.Parse(opts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
data := &M.UserRole{} data := make([]*struct {
M.UserRole
Username string `json:"username"`
Nickname string `json:"nickname"`
Icon string `json:"icon"`
RoleName string `json:"role_name"`
}, 0, 10)
// data := make([]*M.UserRole, 0, 10)
err = cfg.DB().Where("id = ?", opts.ID).Delete(data).Error query := cfg.DB().Debug().Table("user_roles").Select("user_roles.*,users.username,users.nickname,users.icon,roles.name as role_name").
Joins("JOIN users ON users.id = user_roles.user_id").
Joins("JOIN roles ON roles.id = user_roles.role_id")
if opts.UserID != nil && *opts.UserID != "-" {
query = query.Where("user_id LIKE ?", opts.UserID)
}
if opts.RoleID != nil {
query = query.Where("role_id LIKE ?", opts.RoleID)
}
if opts.AppID != nil {
query = query.Where("app_id LIKE ?", opts.AppID)
}
if opts.Status != nil {
query = query.Where("status LIKE ?", opts.Status)
}
err = query.Scan(&data).Error
return data, err return data, err
} }
func userRoleGet(x *rest.X) (any, error) { func userRolePost(x *rest.X) (any, error) {
opts := &M.UserRoleGet{} opts := &M.UserRolePost{}
err := x.Parse(opts) err := x.Parse(opts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
data := &M.UserRole{} data := &M.UserRole{}
err = cfg.DB().Where("id = ?", opts.ID).First(data).Error data.UserID = opts.UserID
data.RoleID = opts.RoleID
data.AppID = opts.AppID
data.Status = opts.Status
err = cfg.DB().Create(data).Error
return data, err return data, err
} }
func userRoleList(x *rest.X) (any, error) { func userRoleGet(x *rest.X) (any, error) {
opts := &M.UserRoleList{} opts := &M.UserRoleGet{}
err := x.Parse(opts) err := x.Parse(opts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
data := make([]*M.UserRole, 0, 10) data := &M.UserRole{}
query := cfg.DB() err = cfg.DB().Where("id = ?", opts.ID).First(data).Error
if opts.Status != nil {
query = query.Where("status LIKE ?", opts.Status)
}
err = query.Find(&data).Error
return data, err return data, err
} }
@ -75,19 +95,15 @@ func userRolePatch(x *rest.X) (any, error) {
return data, err return data, err
} }
func userRolePost(x *rest.X) (any, error) { func userRoleDelete(x *rest.X) (any, error) {
opts := &M.UserRolePost{} opts := &M.UserRoleDelete{}
err := x.Parse(opts) err := x.Parse(opts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
data := &M.UserRole{} data := &M.UserRole{}
data.ID = strings.ReplaceAll(uuid.New().String(), "-", "") err = cfg.DB().Where("id = ?", opts.ID).Delete(data).Error
data.UserID = opts.UserID
data.RoleID = opts.RoleID
data.Status = opts.Status
err = cfg.DB().Create(data).Error
return data, err return data, err
} }

@ -28,6 +28,8 @@ type AccessGet struct {
type AccessPatch struct { type AccessPatch struct {
ID string `json:"id" gorm:"primaryKey;type:varchar(32)" parse:"path@access_id"` ID string `json:"id" gorm:"primaryKey;type:varchar(32)" parse:"path@access_id"`
TID *string `json:"tid" parse:"json"`
Level *uint `json:"level" parse:"json"`
} }
type AccessDelete struct { type AccessDelete struct {

@ -23,6 +23,6 @@ type Access struct {
Name string `json:"name" methods:"post,*list" parse:"json"` Name string `json:"name" methods:"post,*list" parse:"json"`
TID string `json:"tid" methods:"post" parse:"json"` TID string `json:"tid" methods:"post,*patch" parse:"json"`
Level uint `json:"level" methods:"post" parse:"json"` Level uint `json:"level" methods:"post,*patch" parse:"json"`
} }

@ -40,30 +40,27 @@ type UserList struct {
type UserRoleGet struct { type UserRoleGet struct {
ID string `json:"id" gorm:"primaryKey;type:varchar(32)" parse:"path@user_role_id"` ID string `json:"id" gorm:"primaryKey;type:varchar(32)" parse:"path@user_role_id"`
UserID string `json:"user_id" parse:"path"`
} }
type UserRolePatch struct { type UserRolePatch struct {
ID string `json:"id" gorm:"primaryKey;type:varchar(32)" parse:"path@user_role_id"` ID string `json:"id" gorm:"primaryKey;type:varchar(32)" parse:"path@user_role_id"`
Status *string `json:"status" parse:"json"` Status *string `json:"status" parse:"json"`
UserID string `json:"user_id" parse:"path"`
} }
type UserRoleDelete struct { type UserRoleDelete struct {
ID string `json:"id" gorm:"primaryKey;type:varchar(32)" parse:"path@user_role_id"` ID string `json:"id" gorm:"primaryKey;type:varchar(32)" parse:"path@user_role_id"`
UserID string `json:"user_id" parse:"path"`
RoleID string `json:"role_id" parse:"json"`
AppID string `json:"app_id" parse:"json"`
} }
type UserRolePost struct { type UserRoleList struct {
Status string `json:"status" parse:"json"` UserID *string `json:"user_id" parse:"path"`
RoleID string `json:"role_id" parse:"json"` RoleID *string `json:"role_id" parse:"query"`
AppID string `json:"app_id" parse:"json"` AppID *string `json:"app_id" parse:"query"`
UserID string `json:"user_id" parse:"path"` Status *string `json:"status" parse:"query"`
} }
type UserRoleList struct { type UserRolePost struct {
UserID string `json:"user_id" parse:"path"` UserID string `json:"user_id" parse:"path"`
Status *string `json:"status" parse:"query"` RoleID string `json:"role_id" parse:"json"`
AppID string `json:"app_id" parse:"json"`
Status string `json:"status" parse:"json"`
} }

@ -24,13 +24,13 @@ type User struct {
type UserRole struct { type UserRole struct {
BaseModel BaseModel
UserID string `json:"user_id" methods:"get,list,post,patch,delete" parse:"path"` UserID string `json:"user_id" methods:"*list,post" parse:"path"`
User *User `json:"-" gorm:"foreignKey:UserID;references:ID"` User *User `json:"-" gorm:"foreignKey:UserID;references:ID"`
RoleID string `json:"role_id" methods:"post,delete" parse:"json"` RoleID string `json:"role_id" methods:"*list,post" parse:"json"`
Role *Role `json:"-" gorm:"foreignKey:RoleID;references:ID"` Role *Role `json:"-" gorm:"foreignKey:RoleID;references:ID"`
AppID string `json:"app_id" methods:"post,delete" parse:"json"` AppID string `json:"app_id" methods:"*list,post" parse:"json"`
App *App `json:"-" gorm:"foreignKey:AppID;references:ID"` App *App `json:"-" gorm:"foreignKey:AppID;references:ID"`
Status string `json:"status" methods:"post,*patch,*list" parse:"json"` Status string `json:"status" methods:"post,*patch,*list" parse:"json"`

2
oaer/index.d.ts vendored

@ -1,3 +1,3 @@
export default interface OAER { export default {
logout() logout()
} }

@ -35,7 +35,7 @@ const dirTree = (root: string | FileStat, depth = 0): HTMLElement => {
style: `padding-left: ${depth * 1 + 1}rem`, style: `padding-left: ${depth * 1 + 1}rem`,
children: [v('div', '🔤'), v('div', item.basename)], children: [v('div', '🔤'), v('div', item.basename)],
onclick: () => { onclick: () => {
api.token.Post({ refresh: logic.token.refresh.raw(), typ: 'ufs' }).then(e => { api.token.Post({ refresh: logic.token.refresh.raw(), typ: 'ufs' }).then(() => {
logic.goto(fs.user.urlwrap(item.filename), undefined, true) logic.goto(fs.user.urlwrap(item.filename), undefined, true)
}) })

@ -133,42 +133,42 @@ const rename = (o: string, n?: string) => {
// }) // })
// } // }
const get_dav = (client: webdav.WebDAVClient, base_url: string) => { // const get_dav = (client: webdav.WebDAVClient, base_url: string) => {
return { // return {
client: client, // client: client,
stat: client.stat, // stat: client.stat,
dir: client.getDirectoryContents, // dir: client.getDirectoryContents,
uploadstr: (dir: string, name: string, data: string) => { // uploadstr: (dir: string, name: string, data: string) => {
if (dir.startsWith('/')) { // if (dir.startsWith('/')) {
dir = dir.slice(1) // dir = dir.slice(1)
} // }
return new Promise((resolve, reject) => { // return new Promise((resolve, reject) => {
let temp = () => { // let temp = () => {
let reader = new FileReader() // let reader = new FileReader()
reader.onload = function (event) { // reader.onload = function (event) {
var res = event.target?.result // var res = event.target?.result
// let data = new Blob([res]) // // let data = new Blob([res])
client.putFileContents(dir + name, res).then(e => { // client.putFileContents(dir + name, res).then(e => {
if (e) { // if (e) {
resolve(base_url + dir + name) // resolve(base_url + dir + name)
} // }
}).catch(reject) // }).catch(reject)
} // }
reader.readAsArrayBuffer(new Blob([data], { type: 'plain/text' })) // reader.readAsArrayBuffer(new Blob([data], { type: 'plain/text' }))
} // }
client.stat(dir).then(() => { // client.stat(dir).then(() => {
temp() // temp()
}).catch((_) => { // }).catch((_) => {
client.createDirectory(dir, { recursive: true }).then(() => { // client.createDirectory(dir, { recursive: true }).then(() => {
temp() // temp()
}).catch(e => { // }).catch(e => {
console.warn(e) // console.warn(e)
}) // })
}) // })
}); // });
} // }
} // }
} // }
const download = (url: string) => { const download = (url: string) => {
return new Promise<string>((resolve, reject) => { return new Promise<string>((resolve, reject) => {

@ -4,8 +4,6 @@ import './style.css'
// let a = proxy.Ref(1) // let a = proxy.Ref(1)
// a.value++ // a.value++
import oaer from '../lib/main' import oaer from '../lib/main'
let code =
`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiI3ODA5YTZlNjhmNDE0OWM5ODFhMmFhNTI0NTE0MzIyZiIsImFpZCI6InRNUnhXejc3UDlBQk5aQTNaSXVvTlFJTGpWQkJJVWRmIiwibmFtZSI6ImFkbWluIiwiaWNvbiI6Imh0dHBzOi8vcHVibGljLnZleXBpLmNvbS9pbWcvYXZhdGFyLzAwNTEuanBnIiwiYWNjZXNzIjpudWxsLCJpc3MiOiJvYSIsImV4cCI6MTczMDk4NDY4MiwiaWF0IjoxNzMwNzI1NDgyLCJqdGkiOiIxOWZlZTc4YjQwN2M0ZTQ5OWI1Yjg2YmJjNTNjMTA2YyJ9.vIriE-L2AZLtigWXAXHrTG2_XIELqp0bAnFEBX0Hw8w`
let token = '' let token = ''
let querys = window.location.search.match(/token=([^&]*)/) let querys = window.location.search.match(/token=([^&]*)/)
if (querys && querys.length > 1) { if (querys && querys.length > 1) {

@ -42,11 +42,11 @@
</template> </template>
<template v-else-if="type === 'radio'"> <template v-else-if="type === 'radio'">
<div class="flex px-8 justify-between gap-4"> <div class="flex px-8 justify-between gap-4">
<template :key="ok" v-for="(ov, ok) of transDic"> <template :key="ov.key" v-for="ov of transOptions">
<div :class="[value === ok ? 'radio-btn-active' : <div :class="[value === ov.key ? 'radio-btn-active' :
'div-btn']" @click="setSelect(ok)" style="color:white;" 'div-btn']" @click="setSelect(ov.key)" style="color:white;"
class="div-center font-bold grow truncate radio-btn rounded-md transition duration-500"> class="div-center font-bold grow truncate radio-btn rounded-md transition duration-500">
{{ ov || ok }} {{ ov.label }}
</div> </div>
</template> </template>
</div> </div>
@ -54,23 +54,32 @@
</template> </template>
<template v-else-if="type === 'select'"> <template v-else-if="type === 'select'">
<div class="noborder cursor-pointer w-full overflow-x-auto whitespace-nowrap" @click="showSelect" <div class="noborder cursor-pointer w-full overflow-x-auto whitespace-nowrap" @click="showSelect">
:title="title"> <span v-if="!value">
<span v-if="value === undefined || value === null"></span> <div v-if="typeof selectNone === 'string'">{{ selectNone }}</div>
<span v-else-if="!Array.isArray(value)">{{ transDic[value] || value }}</span> <div v-else></div>
</span>
<div v-else-if="!Array.isArray(value)">
<slot name="select-item" :value="transDic[value]">
{{ transDic[value].label }}
</slot>
</div>
<template v-else> <template v-else>
<span class="mx-2" v-for=" iv in value " :key="iv">{{ transDic[iv] || iv }}</span> <div class="mx-2" v-for=" iv in value " :key="iv">
<slot name="select-item" :value="transDic[iv]">
{{ transDic[iv].label }}
</slot>
</div>
</template> </template>
</div> </div>
<div @mouseleave="showSelectOpt = false" <div @mouseleave="showSelectOpt = false"
:style="{ left: selectPos[0] + 'px', top: selectPos[1] + 'px', height: showSelectOpt ? '20rem' : '0rem' }" :style="{ left: selectPos[0] + 'px', top: selectPos[1] + 'px', height: showSelectOpt ? '20rem' : '0rem' }"
class="select-opt text-base text-white rounded-md overflow-y-auto" style="min-width: 10rem;" :title="title"> class="select-opt text-base text-white rounded-md overflow-y-auto" style="min-width: 10rem;">
<div class="m-2 p-2" v-if="!options"></div> <div class="m-2 p-2" v-if="!transOptions.length"></div>
<div :class="[ov.key === value ? 'bg-gray-500' : 'bg-gray-800']" class="cursor-pointer m-2 p-2 rounded-md <div :class="[ov.key === value ? 'bg-gray-500' : 'bg-gray-800']" class="cursor-pointer m-2 p-2 rounded-md
hover:bg-gray-500" @click="setSelect(ov.key)" v-for="ov of options" :key="ov.key"> hover:bg-gray-500" @click="setSelect(ov.key)" v-for="ov of transOptions" :key="ov.key">
{{ ov.name }} {{ ov.label }}
</div> </div>
<div class="w-full h-32"></div>
</div> </div>
</template> </template>
<template v-else-if="type === 'region'"> <template v-else-if="type === 'region'">
@ -122,6 +131,7 @@
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { OneIcon } from '@veypi/one-icon' import { OneIcon } from '@veypi/one-icon'
@ -144,10 +154,16 @@ type ArgType = 'text' | 'password' | 'bool' | 'select' | 'radio' | 'number' | 'r
// [ArgType.Bool]: '', // [ArgType.Bool]: '',
// } // }
type NumberOptions = {
max?: number
min?: number
}
type SelectBaseOption = { key: string, label: string, [key: string]: any }
type SelectOptions = SelectBaseOption[] | string[] | { [key: string]: SelectBaseOption | string }
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
modelValue?: any modelValue?: any
type?: ArgType, type?: ArgType,
options?: any,
disabled?: boolean disabled?: boolean
hideBorder?: boolean hideBorder?: boolean
tidy?: boolean tidy?: boolean
@ -158,7 +174,16 @@ const props = withDefaults(defineProps<{
require?: boolean require?: boolean
clicktoedit?: boolean, clicktoedit?: boolean,
normalwrap?: (s: string) => string normalwrap?: (s: string) => string
// /^reg for value &/
validate?: any validate?: any
// work for select radio number
// number: { max: 100, min: 0 }
// select: [{ key: 'a', label: 'A' }]
options?: NumberOptions | SelectOptions
selectNone?: any
selectSearch?: any
// //
title?: string title?: string
}>(), { }>(), {
@ -187,7 +212,8 @@ const dy_style = computed(() => `text-align:${props.align}`)
let inputRef = ref<HTMLInputElement>() let inputRef = ref<HTMLInputElement>()
let all = ref<HTMLElement>() let all = ref<HTMLElement>()
const transDic = ref({} as { [key: string]: string }) const transOptions = ref<SelectBaseOption[]>([])
const transDic = ref<{ [key: string]: SelectBaseOption }>({})
const change = (s: string) => { const change = (s: string) => {
if (props.disabled) { if (props.disabled) {
@ -219,20 +245,31 @@ const sync = () => {
} }
if (props.type === 'radio' || props.type === 'select') { if (props.type === 'radio' || props.type === 'select') {
transDic.value = {} transDic.value = {}
if (Array.isArray(props.options)) { transOptions.value = []
for (let i of props.options) { let options = props.options as SelectOptions
if (Array.isArray(options)) {
for (let i of options) {
if (typeof i === 'string') { if (typeof i === 'string') {
transDic.value[i] = i transOptions.value.push({ key: i, label: i })
} else { } else {
transDic.value[i.key] = i.name transOptions.value.push({ key: i.key, label: i.label })
} }
} }
// console.log(transDic.value) // console.log(transDic.value)
} else { } else {
for (let i in props.options) { for (let i in options) {
transDic.value[i] = props.options[i] let v = options[i]
if (typeof v === 'string') {
transOptions.value.push({ key: i, label: v })
} else {
transOptions.value.push(v)
}
}
} }
for (let i of transOptions.value) {
transDic.value[i.key] = i
} }
console.log(transDic.value)
} }
} }
watch(computed(() => props), sync) watch(computed(() => props), sync)
@ -242,10 +279,11 @@ const check = (e?: InputEvent) => {
if (v !== 0 && !v) { if (v !== 0 && !v) {
return false return false
} }
if (typeof props.options?.max === 'number' && v > props.options.max) { let options = props.options as NumberOptions
if (typeof options?.max === 'number' && v > options.max) {
return false return false
} }
if (typeof props.options?.min === 'number' && v < props.options.min) { if (typeof options?.min === 'number' && v < options.min) {
return false return false
} }
value.value = v value.value = v
@ -422,7 +460,7 @@ select {
top: 0; top: 0;
background: #333; background: #333;
transform-origin: top; transform-origin: top;
transition: height 0.3s linear; transition: height 3s linear;
} }
.radio-btn { .radio-btn {

@ -36,11 +36,16 @@ export function Get(access_id: string) {
return webapi.Get<models.Access>(`/access/${access_id}`, {}) return webapi.Get<models.Access>(`/access/${access_id}`, {})
} }
export function Patch(access_id: string) { export function Patch(access_id: string, json: PatchOpts) {
return webapi.Patch<models.Access>(`/access/${access_id}`, {}) return webapi.Patch<models.Access>(`/access/${access_id}`, { json })
} }
export function Delete(access_id: string) { export function Delete(access_id: string) {
return webapi.Delete<models.Access>(`/access/${access_id}`, {}) return webapi.Delete<models.Access>(`/access/${access_id}`, {})
} }
// keep
export interface PatchOpts {
tid?: string
level?: number
}

@ -111,3 +111,34 @@ export function ResourcePost(app_id: string, json: ResourcePostOpts) {
return webapi.Post<models.Resource>(`/app/${app_id}/resource`, { json }) return webapi.Post<models.Resource>(`/app/${app_id}/resource`, { json })
} }
export function RoleGet(app_id: string, role_id: string) {
return webapi.Get<models.Role>(`/app/${app_id}/role/${role_id}`, {})
}
export interface RolePatchOpts {
name?: string
des?: string
}
export function RolePatch(app_id: string, role_id: string, json: RolePatchOpts) {
return webapi.Patch<models.Role>(`/app/${app_id}/role/${role_id}`, { json })
}
export function RoleDelete(app_id: string, role_id: string) {
return webapi.Delete<models.Role>(`/app/${app_id}/role/${role_id}`, {})
}
export interface RoleListQuery {
name?: string
}
export function RoleList(app_id: string, query: RoleListQuery) {
return webapi.Get<models.Role[]>(`/app/${app_id}/role`, { query })
}
export interface RolePostOpts {
name: string
des: string
}
export function RolePost(app_id: string, json: RolePostOpts) {
return webapi.Post<models.Role>(`/app/${app_id}/role`, { json })
}

@ -1,7 +1,6 @@
import * as user from "./user" import * as user from "./user"
import * as token from "./token" import * as token from "./token"
import * as role from "./role"
import * as app from "./app" import * as app from "./app"
import * as access from "./access" import * as access from "./access"
import { token as apitoken } from './webapi' import { token as apitoken } from './webapi'
@ -10,7 +9,6 @@ export default {
apitoken, apitoken,
user, user,
token, token,
role,
app, app,
access access
} }

@ -85,5 +85,9 @@ export interface UserRole {
role_id: string role_id: string
app_id: string app_id: string
status: string status: string
username?: string
nickname?: string
icon?: string
role_name?: string
} }

@ -1,42 +0,0 @@
//
// Copyright (C) 2024 veypi <i@veypi.com>
// 2024-10-11 14:36:07
// Distributed under terms of the MIT license.
//
import webapi from "./webapi"
import * as models from "./models"
export function Get(role_id: string) {
return webapi.Get<models.Role>(`/role/${role_id}`, {})
}
export interface PatchOpts {
name?: string
des?: string
app_id?: string
}
export function Patch(role_id: string, json: PatchOpts) {
return webapi.Patch<models.Role>(`/role/${role_id}`, { json })
}
export function Delete(role_id: string) {
return webapi.Delete<models.Role>(`/role/${role_id}`, {})
}
export interface PostOpts {
name: string
des: string
app_id: string
}
export function Post(json: PostOpts) {
return webapi.Post<models.Role>(`/role`, { json })
}
export interface ListQuery {
name?: string
}
export function List(query: ListQuery) {
return webapi.Get<models.Role[]>(`/role`, { query })
}

@ -65,8 +65,8 @@ export interface UserRoleDeleteOpts {
role_id: string role_id: string
app_id: string app_id: string
} }
export function UserRoleDelete(user_id: string, user_role_id: string, json: UserRoleDeleteOpts) { export function UserRoleDelete(user_id: string, user_role_id: string) {
return webapi.Delete<models.UserRole>(`/user/${user_id}/user_role/${user_role_id}`, { json }) return webapi.Delete<models.UserRole>(`/user/${user_id}/user_role/${user_role_id}`, {})
} }
export interface UserRolePostOpts { export interface UserRolePostOpts {
@ -80,6 +80,8 @@ export function UserRolePost(user_id: string, json: UserRolePostOpts) {
export interface UserRoleListQuery { export interface UserRoleListQuery {
status?: string status?: string
role_id?: string
app_id?: string
} }
export function UserRoleList(user_id: string, query: UserRoleListQuery) { export function UserRoleList(user_id: string, query: UserRoleListQuery) {
return webapi.Get<models.UserRole[]>(`/user/${user_id}/user_role`, { query }) return webapi.Get<models.UserRole[]>(`/user/${user_id}/user_role`, { query })

@ -19,9 +19,6 @@ const util = {
} }
}, },
datetostr(d: string) { datetostr(d: string) {
if (!d.endsWith('Z')) {
d = d + 'Z'
}
let r = new Date(d) let r = new Date(d)
let delta = (new Date().getTime() - r.getTime()) / 1000 let delta = (new Date().getTime() - r.getTime()) / 1000
if (delta < 0) { if (delta < 0) {

@ -6,13 +6,13 @@
--> -->
<template> <template>
<div> <div>
<div class="flex justify-start items-center gap-4"> <div class="flex justify-start items-center gap-4 mb-8">
<img class="w-12 h-12 rounded-full" :src="core.icon"> <img class="w-12 h-12 rounded-full" :src="core.icon">
<div class="text-3xl"> <div class="text-3xl">
{{ core.name }} {{ core.name }}
</div> </div>
</div> </div>
<NuxtPage keepalive :core='core' :page-key="route => route.fullPath"></NuxtPage> <NuxtPage keepalive :page-key="route => route.fullPath"></NuxtPage>
</div> </div>
</template> </template>
@ -29,6 +29,8 @@ const router = useRouter()
let menu = useMenuStore() let menu = useMenuStore()
let core = ref({} as models.App) let core = ref({} as models.App)
provide('app', core)
const set_menu = () => { const set_menu = () => {
let p = '/app/' + core.value.id let p = '/app/' + core.value.id
menu.set([ menu.set([

@ -11,23 +11,22 @@
<div @click="created(0)"></div> <div @click="created(0)"></div>
</div> </div>
<div class="w-full"> <div class="w-full">
<crud vertical :keys="role_keys" :data="role" @update="update(0, <crud vertical :keys="role_keys" :data="role" @update="update($event, role, api.app.RolePatch, app.id)">
$event)"> <template #k_created_at="{ value }">{{ util.datetostr(value) }}</template>
<template #k_created="{ value }">{{ util.datetostr(value) }}</template>
<template #k__="{ row }"> <template #k__="{ row }">
<div color="primary" size="sm" @click="show_dialog(0, <div color="primary" size="sm" @click="show_dialog(0,
row.id)">权限</div> row.id)">权限</div>
<div color="secondary" size="sm" @click="show_dialog(1, <div color="secondary" size="sm" @click="show_dialog(1,
row.id)">用户</div> row.id)">用户</div>
<div color="negative" size="sm" @click="del(0, row.id)">删除</div> <div color="negative" size="sm" @click="del(0, row.id)">删除</div>
<template v-if="row.id === app.role_id"> <template v-if="row.id === app.init_role_id">
<div color="positive" disable size="sm">初始角色</div> <div color="positive" disable size="sm">初始角色</div>
</template> </template>
<template v-else> <template v-else>
<div size="sm" @click="api.app.update(app.id, { <div size="sm" @click="api.app.Patch(app.id, {
role_id: init_role_id:
row.id row.id
}).then(_ => app.role_id = row.id)">设置为初始角色</div> }).then(_ => app.init_role_id = row.id)">设置为初始角色</div>
</template> </template>
</template> </template>
</crud> </crud>
@ -38,52 +37,49 @@
<div @click="created(1)"></div> <div @click="created(1)"></div>
</div> </div>
<div class="w-full"> <div class="w-full">
<crud vertical :keys="resource_keys" :data="resource" @update="update(1, <crud vertical :keys="resource_keys" :data="resource"
$event)"> @update="update($event, resource, api.app.ResourcePatch, app.id)">
<template #k_created="{ value }">{{ util.datetostr(value) }}</template> <template #k_created_at="{ value }">{{ util.datetostr(value) }}</template>
<template #k__="{ row }"> <template #k__="{ row }">
<div color="negative" size="sm" @click="del(1, row.name)">删除</div> <div color="negative" size="sm" @click="del(1, row.name)">删除</div>
</template> </template>
</crud> </crud>
</div> </div>
<q-dialog v-model="dialog"> <UModal v-model="dialog" :ui="{ 'width': '' }">
<q-card v-if="dialog_obj" class="mx-4 mt-4" style="width: 1000px;max-width: 90vw;"> <UCard v-if="dialog_obj" style="width: 1000px;max-width: 90vw;">
<q-card-section class="row items-center q-pb-none"> <template #header>
<div class="text-h6">{{ dialog_obj.name }} <div class="items-center flex">
<div class="text-2xl">{{ dialog_obj.name }}
</div> </div>
<q-space /> <div class="flex-grow"></div>
<div v-if="dialog_mode"> <div v-if="dialog_mode">
<q-select filled :model-value="''" @update:model-value="roleuser.add" use-input hide-selected fill-input <vinput :model-value="''" type="select" :options="['1', '2']" select-none=""></vinput>
input-debounce="0" label="添加用户" :options="users_cache" @filter="filterFn" style="width: 20rem"> <!-- <USelect filled :model-value="''" @update:model-value="roleuser.add" use-input hide-selected fill-input -->
<template v-slot:no-option> <!-- input-debounce="0" label="添加用户" :options="users_cache" @filter="filterFn" style="width: 20rem"> -->
<q-item> <!-- </USelect> -->
<q-item-section class="text-grey">
无结果
</q-item-section>
</q-item>
</template>
</q-select>
</div> </div>
<div v-else @click="created(2, { role_id: dialog_obj.id })">添加权限</div> <div v-else @click="created(2, { role_id: dialog_obj.id })">添加权限</div>
</q-card-section> </div>
</template>
<q-card-section v-if="dialog_mode"> <div v-if="dialog_mode">
<crud vertical :keys="users_keys" :data="users"> <crud vertical :keys="users_keys" :data="users">
<template #k__="{ row }"> <template #k__="{ row }">
<div color="negative" size="sm" @click="roleuser.drop(row)"></div> <div color="negative" size="sm" @click="roleuser.drop(row)"></div>
</template> </template>
</crud> </crud>
</q-card-section> </div>
<q-card-section v-else> <div v-else>
<crud vertical :keys="access_keys" :data="access" @update="update(2, $event)"> <crud vertical :keys="access_keys" :data="access" @update="update($event, access, api.access.Patch)">
<template #k__="{ row }"> <template #k__="{ row }">
<div color="negative" size="sm" @click="del(2, row.id)">删除</div> <div color="negative" size="sm" @click="del(2, row.id)">删除</div>
</template> </template>
</crud> </crud>
</q-card-section> </div>
</q-card>
</q-dialog> </UCard>
</UModal>
</div> </div>
</template> </template>
@ -101,7 +97,7 @@ const role_keys = ref<any>([
}, },
{ name: 'name', label: '标识符' }, { name: 'name', label: '标识符' },
{ name: 'des', label: '描述', editable: true }, { name: 'des', label: '描述', editable: true },
{ name: 'created', label: '创建时间' }, { name: 'created_at', label: '创建时间' },
{ name: 'user_count', label: '绑定用户数' }, { name: 'user_count', label: '绑定用户数' },
{ {
name: '_', label: '操作', style: { 'justify-content': 'start' }, name: '_', label: '操作', style: { 'justify-content': 'start' },
@ -111,7 +107,7 @@ const role_keys = ref<any>([
const resource_keys = ref<any>([ const resource_keys = ref<any>([
{ name: 'name', label: '标识符' }, { name: 'name', label: '标识符' },
{ name: 'des', label: '描述', editable: true }, { name: 'des', label: '描述', editable: true },
{ name: 'created', label: '创建时间' }, { name: 'created_at', label: '创建时间' },
{ {
name: '_', label: '操作', style: { 'justify-content': 'start' }, name: '_', label: '操作', style: { 'justify-content': 'start' },
width: 40 width: 40
@ -141,11 +137,12 @@ const users_keys = ref<any>([
]) ])
let access = ref<models.Access[]>([]) let access = ref<models.Access[]>([])
let users = ref<models.User[]>([]) let users = ref<models.UserRole[]>([])
let users_cache = ref<any[]>([]) let users_cache = ref<any[]>([])
const resource = ref<any[]>([]) const resource = ref<any[]>([])
const role = ref<any[]>([]) const role = ref<any[]>([])
const app = inject('app') as Ref<models.App> const app = inject('app') as Ref<models.App>
watch(computed(() => app.value.id), (v) => { watch(computed(() => app.value.id), (v) => {
sync(v) sync(v)
@ -155,13 +152,10 @@ const sync = (id: any) => {
if (!id) { if (!id) {
return return
} }
api.role.List(id).then(e => { api.app.RoleList(id, {}).then(e => {
role.value = e
})
api.role(id).list().then(e => {
role.value = e role.value = e
}) })
api.resource(id).list().then(e => { api.app.ResourceList(id).then(e => {
resource.value = e resource.value = e
}) })
} }
@ -171,11 +165,12 @@ const show_dialog = (mode: number, idx: string) => {
dialog.value = true dialog.value = true
dialog_mode.value = mode dialog_mode.value = mode
if (mode) { if (mode) {
api.user.list({ role_id: idx }).then(e => { api.user.UserRoleList('-', { role_id: idx }).then(e => {
console.log(e)
users.value = e users.value = e
}) })
} else { } else {
api.access(app.value.id).list({ role_id: idx }).then(e => { api.access.List({ app_id: app.value.id, role_id: idx }).then(e => {
access.value = e access.value = e
}) })
} }
@ -184,7 +179,7 @@ const show_dialog = (mode: number, idx: string) => {
// 0: role 1: resource 2: access 3: users // 0: role 1: resource 2: access 3: users
const crud_option = (mode: number) => { const crud_option = (mode: number) => {
let res = { let res = {
api: api.role(app.value.id), api: api.app.RolePatch(app.value.id),
lable: '角色', lable: '角色',
obj: role, obj: role,
} }
@ -220,32 +215,36 @@ const created = (k: number, props?: any) => {
}) })
} }
} }
$q.dialog({ // $q.dialog({
title: '创建' + opt.lable, // title: '' + opt.lable,
message: '请输入标识码', // message: '',
prompt: prompt as any, // prompt: prompt as any,
options: options as any, // options: options as any,
cancel: true, // cancel: true,
persistent: true // persistent: true
}).onOk(data => { // }).onOk(data => {
opt.api.create(data, props).then(e => { // opt.api.create(data, props).then(e => {
msg.Info('创建成功') // msg.Info('')
opt.obj.value.push(e) // opt.obj.value.push(e)
}).catch(e => { // }).catch(e => {
msg.Warn('创建失败: ' + e) // msg.Warn(' ' + e)
}) // })
}) // })
} }
const update = (k: number, props: any[]) => { const update = (props: any[], objs: any, cb: any, topid?: string) => {
let opt = crud_option(k) // let opt = crud_option(k)
console.log(props) let patch = cb
if (topid) {
patch = (id: string, props: any) => {
return cb(topid, id, props)
}
}
for (let i in props) { for (let i in props) {
let id = opt.obj.value[i][k === 1 ? 'name' : 'id'] let id = objs[i].id
console.log(id) patch(id, props[i]).then(() => {
opt.api.update(id, props[i]).then(() => { Object.assign(objs[i], props[i])
Object.assign(opt.obj.value[i], props[i]) }).catch((e: any) => {
}).catch(e => {
msg.Warn('更新失败: ' + e) msg.Warn('更新失败: ' + e)
}) })
} }
@ -294,7 +293,7 @@ const filterFn = (val: string, cb: any) => {
cb(() => { cb(() => {
users_cache.value = e.map(i => { users_cache.value = e.map(i => {
return Object.assign({ return Object.assign({
label: i.username, name: i.username,
value: i.id, value: i.id,
}, i) }, i)
}) })

@ -56,19 +56,15 @@ const keys = ref<any>([
{ name: 'init_url', label: '项目首页', validate: /^(https?:\/\/)?(([\da-z\.-]+)|(\d{1,3}(\.\d{1,3}){3}))(:\d+)?([\/\w \.-]*)*\/?$/ }, { name: 'init_url', label: '项目首页', validate: /^(https?:\/\/)?(([\da-z\.-]+)|(\d{1,3}(\.\d{1,3}){3}))(:\d+)?([\/\w \.-]*)*\/?$/ },
{ name: 'redirect', label: '跳转地址' }, { name: 'redirect', label: '跳转地址' },
]) ])
let props = withDefaults(defineProps<{
core: models.App,
}>(),
{}
)
const core = inject('app') as Ref<models.App>
const newApp = ref({}) const newApp = ref({})
const table = ref() const table = ref()
const save = () => { const save = () => {
api.app.Patch(props.core.id, newApp.value).then(e => { api.app.Patch(core.value.id, newApp.value).then(e => {
msg.Info('更新成功') msg.Info('更新成功')
Object.assign(props.core, newApp.value) Object.assign(core, newApp.value)
newApp.value = {} newApp.value = {}
}).catch(e => { }).catch(e => {
msg.Warn('更新失败 ' + e) msg.Warn('更新失败 ' + e)
@ -78,8 +74,8 @@ const save = () => {
const tmpkey = ref('') const tmpkey = ref('')
const getkey = () => { const getkey = () => {
msg.Prompt('请输入应用名确认', '').then((s) => { msg.Prompt('请输入应用名确认', '').then((s) => {
if (s === props.core.name) { if (s === core.value.name) {
api.app.Key(props.core.id).then(e => { api.app.Key(core.value.id).then(e => {
tmpkey.value = e tmpkey.value = e
navigator.clipboard.writeText(e) navigator.clipboard.writeText(e)
.then(() => { .then(() => {

@ -6,7 +6,7 @@
--> -->
<template> <template>
<div> <div>
<Editor style="" v-if="core.id" :eid="core.id + '.des'" v-model="preview_mode" :content="content" @save="save"> <Editor style="" v-if="app.id" :eid="app.id + '.des'" v-model="preview_mode" :content="content" @save="save">
</Editor> </Editor>
</div> </div>
</template> </template>
@ -16,36 +16,31 @@ import type { models } from '#imports';
import { fs } from '@veypi/oaer' import { fs } from '@veypi/oaer'
let props = withDefaults(defineProps<{ const app = inject('app') as Ref<models.App>
core: models.App,
}>(),
{}
)
let preview_mode = ref(true) let preview_mode = ref(true)
let content = ref('') let content = ref('')
const sync = () => { const sync = () => {
if (props.core.des) { if (app.value.des) {
fs.app.getFileContents(fs.app.urlunwrap(props.core.des), { format: 'text' }).then((e) => { fs.app.getFileContents(fs.app.urlunwrap(app.value.des), { format: 'text' }).then((e) => {
content.value = e as string content.value = e as string
}) })
} }
} }
watch(computed(() => props.core.id), () => { watch(computed(() => app.value.id), () => {
sync() sync()
}, { immediate: true }) }, { immediate: true })
const save = (des: string) => { const save = (des: string) => {
let furl = `/info/appdes/${props.core.id}.md` let furl = `/info/appdes/${app.value.id}.md`
fs.app.putFileContents(furl, des).then((e) => { fs.app.putFileContents(furl, des).then((e) => {
furl = fs.app.urlwrap(furl) furl = fs.app.urlwrap(furl)
if (props.core.des !== furl) { if (app.value.des !== furl) {
api.app.Patch(props.core.id, { des: furl }).then((e) => { api.app.Patch(app.value.id, { des: furl }).then((e) => {
preview_mode.value = true preview_mode.value = true
props.core.des = furl app.value.des = furl
}) })
} else { } else {
preview_mode.value = true preview_mode.value = true

Loading…
Cancel
Save