Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/0.3.2 #710

Merged
merged 2 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/backend/bisheng/api/services/role_group_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from uuid import UUID

from fastapi.encoders import jsonable_encoder
from fastapi import Request
from fastapi import Request, HTTPException

from bisheng.api.services.assistant import AssistantService
from bisheng.api.services.audit_log import AuditLogService
Expand All @@ -16,7 +16,9 @@
from bisheng.database.models.group import Group, GroupCreate, GroupDao, GroupRead, DefaultGroup
from bisheng.database.models.group_resource import GroupResourceDao, ResourceTypeEnum
from bisheng.database.models.knowledge import KnowledgeDao
from bisheng.database.models.role import AdminRole
from bisheng.database.models.user import User, UserDao
from bisheng.database.models.user_role import UserRoleDao
from bisheng.database.models.user_group import UserGroupCreate, UserGroupDao, UserGroupRead
from loguru import logger

Expand Down Expand Up @@ -141,7 +143,7 @@ def insert_user_group(self, user_group: UserGroupCreate) -> UserGroupRead:
def replace_user_groups(self, request: Request, login_user: UserPayload, user_id: int, group_ids: List[int]):
""" 覆盖用户的所在的用户组 """
# 判断下被操作用户是否是超级管理员
user_role_list = UserRoleDao.get_user_role(user_id)
user_role_list = UserRoleDao.get_user_roles(user_id)
if any(one.role_id == AdminRole for one in user_role_list):
raise HTTPException(status_code=500, detail='系统管理员不允许编辑')

Expand Down
2 changes: 2 additions & 0 deletions src/backend/bisheng/api/v1/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,8 @@ async def user_addrole(*,
user_role_list = UserRoleDao.get_user_roles(user_role.user_id)
if any(one.role_id == AdminRole for one in user_role_list):
raise HTTPException(status_code=500, detail='系统管理员不允许编辑')
if any(one == AdminRole for one in user_role.role_id):
raise HTTPException(status_code=500, detail='不允许设置为系统管理员')

if not login_user.is_admin():
# 判断拥有哪些用户组的管理权限
Expand Down
6 changes: 3 additions & 3 deletions src/backend/bisheng/database/models/role.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ class RoleDao(RoleBase):
@classmethod
def get_role_by_groups(cls, group: List[int], keyword: str = None, page: int = 0, limit: int = 0) -> List[Role]:
"""
获取用户组内的角色列表
获取用户组内的角色列表, 不包含系统管理员角色
params:
group: 用户组ID列表
page: 页数
limit: 每页条数
return: 角色列表
"""
statement = select(Role)
statement = select(Role).where(Role.id > AdminRole)
if group:
statement = statement.where(Role.group_id.in_(group))
if keyword:
Expand All @@ -69,7 +69,7 @@ def count_role_by_groups(cls, group: List[int], keyword: str = None) -> int:
"""
统计用户组内的角色数量,参数如上
"""
statement = select(func.count(Role.id))
statement = select(func.count(Role.id)).where(Role.id > AdminRole)
if group:
statement = statement.where(Role.group_id.in_(group))
if keyword:
Expand Down
47 changes: 47 additions & 0 deletions src/frontend/src/components/bs-comp/sheets/TaggingSheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {
Sheet,
SheetContent,
SheetTitle,
SheetTrigger,
} from "../../bs-ui/sheet";
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';

export default function TaggingSheet({children}) {
const buttons = [
{id:'01',name:'Button01'},
{id:'02',name:'Button02'},
{id:'03',name:'Button03'},
]

return <Sheet>
<SheetTrigger asChild>{children}</SheetTrigger>
<SheetContent className="bg-gray-100 sm:min-w-[800px]">
<SheetTitle>给助手打标签</SheetTitle>
<div className="w-full h-full grid grid-cols-[80%,20%]">
<div className="bg-slate-500">

</div>
<div className="bg-slate-300">
<DragDropContext onDragEnd={() => console.log('-------------')}>
<Droppable droppableId={'list'}>
{(provided) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{buttons.map((b,index) => (
<Draggable key={'drag' + b.id} draggableId={'drag' + b.id} index={index}>
{(provided) => (
<div ref={provided.innerRef} {...provided.draggableProps}
{...provided.dragHandleProps}>
{b.name}
</div>
)}
</Draggable>
))}
</div>
)}
</Droppable>
</DragDropContext>
</div>
</div>
</SheetContent>
</Sheet>
}
44 changes: 44 additions & 0 deletions src/frontend/src/components/bs-ui/select/select.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React, { ChangeEvent } from "react"
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/bs-ui/select";
import { SearchInput } from "../input";

interface SelectSearchProps {
value: string,
options: {label: string, value: string}[],
selectPlaceholder?:string,
inputPlaceholder?:string,
onOpenChange?: (open:boolean) => void,
onValueChange: (value: string) => void,
onChange: (e:ChangeEvent<HTMLInputElement>) => void,
selectClass?: string,
contentClass?: string
}

const SelectSearch: React.FC<SelectSearchProps> = ({
value,
options,
selectPlaceholder = '',
inputPlaceholder = '',
onOpenChange,
onValueChange,
onChange,
selectClass = '',
contentClass = ''
}) => {
return <Select value={value} onOpenChange={(open) => onOpenChange?.(open)} onValueChange={(v) => onValueChange(v)}>
<SelectTrigger className={selectClass}>
<SelectValue placeholder={selectPlaceholder}/>
</SelectTrigger>
<SelectContent className={contentClass}>
<SearchInput inputClassName="h-8 mb-2" placeholder={inputPlaceholder}
onChange={(e) => onChange(e)} onKeyDown={e => e.stopPropagation()} iconClassName="w-4 h-4" />
<SelectGroup>
{options.map(el => (
<SelectItem key={el.value} value={el.value}>{el.label}</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
}

export default React.memo(SelectSearch)
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import ModelSelect from "./ModelSelect";
import Temperature from "./Temperature";
import { locationContext } from "@/contexts/locationContext";
import { useContext } from "react";
import TaggingSheet from "@/components/bs-comp/sheets/TaggingSheet";

export default function Setting() {
const { t } = useTranslation();
Expand Down Expand Up @@ -303,6 +304,12 @@ export default function Setting() {
</div>
</AccordionContent>
</AccordionItem>
<TaggingSheet>
<PlusIcon
className="mr-2 text-primary hover:text-primary/80"
onClick={(e) => e.stopPropagation()}
></PlusIcon>
</TaggingSheet>
</Accordion>
</div>
);
Expand Down
39 changes: 15 additions & 24 deletions src/frontend/src/pages/SystemPage/components/Roles.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { PlusIcon } from "@/components/bs-icons/plus";
import { bsConfirm } from "@/components/bs-ui/alertDialog/useConfirm";
import { Label } from "@/components/bs-ui/label";
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/bs-ui/select";
import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { Button } from "../../../components/bs-ui/button";
Expand All @@ -19,6 +18,7 @@ import { delRoleApi, getRolesByGroupApi, getUserGroupsApi } from "../../../contr
import { captureAndAlertRequestErrorHoc } from "../../../controllers/request";
import { ROLE } from "../../../types/api/user";
import EditRole from "./EditRole";
import SelectSearch from "@/components/bs-ui/select/select"

interface State {
roles: ROLE[];
Expand Down Expand Up @@ -71,7 +71,7 @@ export default function Roles() {
inputDom.value = '';
}
try {
const data: any = await getRolesByGroupApi('', [state.group]);
const data:any = await getRolesByGroupApi('', [state.group]);
dispatch({ type: 'SET_ROLES', payload: data });
allRolesRef.current = data;
} catch (error) {
Expand All @@ -80,7 +80,7 @@ export default function Roles() {
}, [state.group]);

useEffect(() => {
getUserGroupsApi().then((res: any) => {
getUserGroupsApi().then((res:any) => {
const groups = res.records.map(ug => ({ label: ug.group_name, value: ug.id }))
// 获取最近修改用户组
dispatch({ type: 'SET_GROUP', payload: groups[0].value });
Expand Down Expand Up @@ -142,26 +142,17 @@ export default function Roles() {
<div className="flex justify-between">
<div className="flex items-center">
<Label>{t('system.currentGroup')}</Label>
<Select value={state.group}
onOpenChange={(open) => {
!open && setKeyWord('')
}}
onValueChange={(value) =>
dispatch({ type: 'SET_GROUP', payload: value })
}>
<SelectTrigger className="w-[180px] inline-flex ml-2">
<SelectValue placeholder={t('system.defaultGroup')} />
</SelectTrigger>
<SelectContent className="max-w-[180px] break-all">
<SearchInput inputClassName="h-8 mb-2" placeholder={t('log.selectUserGroup')}
onChange={e => setKeyWord(e.target.value)} onKeyDown={e => e.stopPropagation()} iconClassName="w-4 h-4" />
<SelectGroup>
{options.map(el => (
<SelectItem key={el.value} value={el.value}>{el.label}</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
<SelectSearch value={state.group} options={options} selectPlaceholder={t('system.defaultGroup')}
inputPlaceholder={t('log.selectUserGroup')}
selectClass="w-[180px] inline-flex ml-2" contentClass="max-w-[180px] break-all"
onOpenChange={(open) => {
!open && setKeyWord('')
}}
onValueChange={(value) => {
dispatch({ type: 'SET_GROUP', payload: value})
}}
onChange={e => setKeyWord(e.target.value)}
/>
</div>
<div className="flex gap-6 items-center justify-between">
<div className="w-[180px] relative">
Expand All @@ -187,7 +178,7 @@ export default function Roles() {
<TableCell className="font-medium">{el.role_name}</TableCell>
<TableCell>{el.create_time.replace('T', ' ')}</TableCell>
<TableCell className="text-right">
<Button variant="link" disabled={el.id === 1} onClick={() => dispatch({ type: 'SET_ROLE', payload: el })} className="px-0 pl-6">{t('edit')}</Button>
<Button variant="link" onClick={() => dispatch({ type: 'SET_ROLE', payload: el })} className="px-0 pl-6">{t('edit')}</Button>
<Button variant="link" disabled={[1, 2].includes(el.id)} onClick={() => handleDelete(el)} className="text-red-500 px-0 pl-6">{t('delete')}</Button>
</TableCell>
</TableRow>
Expand Down
43 changes: 28 additions & 15 deletions src/frontend/src/pages/SystemPage/components/UserRoleItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,46 @@ import { DelIcon } from "@/components/bs-icons";
import { Button } from "@/components/bs-ui/button";
import MultiSelect from "@/components/bs-ui/select/multi";
import { getRolesByGroupApi, getUserGroupsApi } from "@/controllers/API/user";
import { useEffect, useState } from "react";
import { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import SelectSearch from "@/components/bs-ui/select/select"

export default function UserRoleItem({ showDel, groupId, selectedRoles, onDelete, onChange }:
{ showDel: boolean, groupId: null | string, selectedRoles: any[], onDelete: any, onChange: any }) {
const { t } = useTranslation()

// 用户组
const [groups, setGroups] = useState([])
const groupsRef = useRef([])
const [userGroupSelected, setUserGroupSelected] = useState(groupId ? [groupId] : [])
useEffect(() => {
// 用户组option列表
const loadGroups = () => {
getUserGroupsApi().then((res: any) => {
setGroups(res.records.map((ug) => {
const groups = res.records.map((ug) => {
return {
label: ug.group_name,
value: ug.id.toString()
}
}))
})
setGroups(groups)
groupsRef.current = groups
})
}
useEffect(() => {
// 用户组option列表
loadGroups()
}, [])

const handleSelectGroup = (values) => {
onChange(values, [])
setUserGroupSelected(values);
const handleSelectGroup = (value) => { //单选之后value要改成数组传出去
onChange([value], [])
setUserGroupSelected([value]);
setSelected([])
}
const handleSearch = (e) => {
const keyword = e.target.value
const newGroups = groupsRef.current.filter(g => g.label.toUpperCase().includes(keyword.toUpperCase())
|| g.value === userGroupSelected[0])
setGroups(newGroups)
}

// 角色
const [roles, setRoles] = useState<any[]>([])
Expand All @@ -53,14 +66,14 @@ export default function UserRoleItem({ showDel, groupId, selectedRoles, onDelete
}

return <div className="grid grid-cols-[44%,44%,5%] gap-4">
<MultiSelect
className="max-w-[260px] break-all"
value={userGroupSelected}
<SelectSearch contentClass="max-w-[260px] break-all" selectPlaceholder={t('system.userGroupsSel')}
selectClass="h-[50px]"
value={userGroupSelected[0]}
options={groups}
placeholder={t('system.userGroupsSel')}
onChange={handleSelectGroup}
>
</MultiSelect>
onOpenChange={() => setGroups(groupsRef.current)}
onValueChange={handleSelectGroup}
onChange={handleSearch}
/>
<MultiSelect
multiple
className="max-w-[260px] break-all"
Expand Down