feat: 增强用户、角色和权限管理功能
- 在用户、角色和权限的API中添加模糊查询支持 - 更新CRUD选项以支持查询参数的转换 - 优化前端管理页面,提升用户体验 - 删除不再使用的旧资产文件以减小包体积
This commit is contained in:
parent
5a3fdfe295
commit
83d84254f5
Binary file not shown.
@ -1,6 +1,6 @@
|
|||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select, and_
|
||||||
from fastcrud import FastCRUD
|
from fastcrud import FastCRUD
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ from schemas import (
|
|||||||
)
|
)
|
||||||
from middleware import get_current_user
|
from middleware import get_current_user
|
||||||
from models import User
|
from models import User
|
||||||
|
from utils.like_filter import ilike_contains
|
||||||
|
|
||||||
router = APIRouter(prefix="/permissions", tags=["permissions"])
|
router = APIRouter(prefix="/permissions", tags=["permissions"])
|
||||||
|
|
||||||
@ -54,11 +55,21 @@ async def create_permission(
|
|||||||
async def get_permissions(
|
async def get_permissions(
|
||||||
db: AsyncSession = Depends(get_db),
|
db: AsyncSession = Depends(get_db),
|
||||||
current_user: User = Depends(get_current_user),
|
current_user: User = Depends(get_current_user),
|
||||||
|
name: str | None = Query(None, description="权限名称,模糊匹配"),
|
||||||
|
code: str | None = Query(None, description="权限编码,模糊匹配"),
|
||||||
):
|
):
|
||||||
"""获取所有权限"""
|
"""获取权限列表;支持按名称、编码的模糊查询与组合查询(AND)"""
|
||||||
result = await db.execute(select(Permission))
|
stmt = select(Permission)
|
||||||
|
filters = []
|
||||||
|
for col, raw in ((Permission.name, name), (Permission.code, code)):
|
||||||
|
cond = ilike_contains(col, raw)
|
||||||
|
if cond is not None:
|
||||||
|
filters.append(cond)
|
||||||
|
if filters:
|
||||||
|
stmt = stmt.where(and_(*filters))
|
||||||
|
result = await db.execute(stmt)
|
||||||
permissions = result.scalars().all()
|
permissions = result.scalars().all()
|
||||||
return permissions
|
return [PermissionResponse.model_validate(p) for p in permissions]
|
||||||
|
|
||||||
|
|
||||||
@router.get("/tree", response_model=list[PermissionTreeResponse])
|
@router.get("/tree", response_model=list[PermissionTreeResponse])
|
||||||
|
|||||||
Binary file not shown.
@ -1,11 +1,13 @@
|
|||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
from sqlalchemy import select
|
||||||
from fastcrud import FastCRUD
|
from fastcrud import FastCRUD
|
||||||
|
|
||||||
from database import get_db
|
from database import get_db
|
||||||
from models import Role, User
|
from models import Role, User
|
||||||
from schemas import RoleCreate, RoleResponse
|
from schemas import RoleCreate, RoleResponse
|
||||||
from middleware import get_current_user
|
from middleware import get_current_user
|
||||||
|
from utils.like_filter import ilike_contains
|
||||||
|
|
||||||
router = APIRouter(prefix="/roles", tags=["roles"])
|
router = APIRouter(prefix="/roles", tags=["roles"])
|
||||||
|
|
||||||
@ -38,11 +40,18 @@ async def create_role(
|
|||||||
|
|
||||||
@router.get("/", response_model=list[RoleResponse])
|
@router.get("/", response_model=list[RoleResponse])
|
||||||
async def get_roles(
|
async def get_roles(
|
||||||
db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user)
|
db: AsyncSession = Depends(get_db),
|
||||||
|
current_user: User = Depends(get_current_user),
|
||||||
|
name: str | None = Query(None, description="角色名称,模糊匹配"),
|
||||||
):
|
):
|
||||||
"""获取所有角色"""
|
"""获取角色列表;支持按名称模糊查询"""
|
||||||
result = await role_crud.get_multi(db)
|
stmt = select(Role)
|
||||||
return result["data"]
|
cond = ilike_contains(Role.name, name)
|
||||||
|
if cond is not None:
|
||||||
|
stmt = stmt.where(cond)
|
||||||
|
result = await db.execute(stmt)
|
||||||
|
rows = result.scalars().all()
|
||||||
|
return [RoleResponse.model_validate(r) for r in rows]
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{role_id}", response_model=RoleResponse)
|
@router.get("/{role_id}", response_model=RoleResponse)
|
||||||
|
|||||||
Binary file not shown.
@ -1,7 +1,7 @@
|
|||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||||
from fastapi.responses import Response
|
from fastapi.responses import Response
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select, and_
|
||||||
from fastcrud import FastCRUD
|
from fastcrud import FastCRUD
|
||||||
from datetime import timedelta, datetime
|
from datetime import timedelta, datetime
|
||||||
import redis.asyncio as redis
|
import redis.asyncio as redis
|
||||||
@ -22,6 +22,7 @@ from config import app_settings
|
|||||||
from utils.redis_client import get_redis, set_token_in_redis
|
from utils.redis_client import get_redis, set_token_in_redis
|
||||||
from utils.captcha import create_captcha, validate_captcha
|
from utils.captcha import create_captcha, validate_captcha
|
||||||
from middleware import get_current_user
|
from middleware import get_current_user
|
||||||
|
from utils.like_filter import ilike_contains
|
||||||
|
|
||||||
router = APIRouter(prefix="/users", tags=["users"])
|
router = APIRouter(prefix="/users", tags=["users"])
|
||||||
|
|
||||||
@ -118,11 +119,30 @@ async def create_user(user_data: UserCreateRequest, db: AsyncSession = Depends(g
|
|||||||
|
|
||||||
@router.get("/", response_model=list[UserResponse])
|
@router.get("/", response_model=list[UserResponse])
|
||||||
async def get_users(
|
async def get_users(
|
||||||
db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user)
|
db: AsyncSession = Depends(get_db),
|
||||||
|
current_user: User = Depends(get_current_user),
|
||||||
|
username: str | None = Query(None, description="登录名,模糊匹配"),
|
||||||
|
nickname: str | None = Query(None, description="昵称,模糊匹配"),
|
||||||
|
email: str | None = Query(None, description="邮箱,模糊匹配"),
|
||||||
|
phone: str | None = Query(None, description="手机,模糊匹配"),
|
||||||
):
|
):
|
||||||
"""获取所有用户"""
|
"""分页列表用户;支持登录名、昵称、邮箱、手机的模糊查询与组合查询(AND)"""
|
||||||
result = await user_crud.get_multi(db)
|
stmt = select(User)
|
||||||
return result["data"]
|
filters = []
|
||||||
|
for col, raw in (
|
||||||
|
(User.username, username),
|
||||||
|
(User.nickname, nickname),
|
||||||
|
(User.email, email),
|
||||||
|
(User.phone, phone),
|
||||||
|
):
|
||||||
|
cond = ilike_contains(col, raw)
|
||||||
|
if cond is not None:
|
||||||
|
filters.append(cond)
|
||||||
|
if filters:
|
||||||
|
stmt = stmt.where(and_(*filters))
|
||||||
|
result = await db.execute(stmt)
|
||||||
|
rows = result.scalars().all()
|
||||||
|
return [UserResponse.model_validate(u) for u in rows]
|
||||||
|
|
||||||
|
|
||||||
@router.get("/me", response_model=UserResponse)
|
@router.get("/me", response_model=UserResponse)
|
||||||
|
|||||||
BIN
backend/auth/utils/__pycache__/like_filter.cpython-313.pyc
Normal file
BIN
backend/auth/utils/__pycache__/like_filter.cpython-313.pyc
Normal file
Binary file not shown.
20
backend/auth/utils/like_filter.py
Normal file
20
backend/auth/utils/like_filter.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
"""LIKE / ILIKE 模糊查询:转义 %、_、\\,避免通配符注入。"""
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from sqlalchemy import ColumnElement
|
||||||
|
|
||||||
|
|
||||||
|
def ilike_contains(column: ColumnElement[Any], value: str | None) -> ColumnElement[bool] | None:
|
||||||
|
"""非空时对 column 做 ILIKE '%value%'(PostgreSQL 下不区分大小写)。"""
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
s = value.strip()
|
||||||
|
if not s:
|
||||||
|
return None
|
||||||
|
escaped = (
|
||||||
|
s.replace("\\", "\\\\")
|
||||||
|
.replace("%", "\\%")
|
||||||
|
.replace("_", "\\_")
|
||||||
|
)
|
||||||
|
return column.ilike(f"%{escaped}%", escape="\\")
|
||||||
@ -1 +1 @@
|
|||||||
import{f as C,u as A,g as L,E as N,p as S,w as t,k as V,l as q,r as a,d as e,a as o,q as l,s as E,t as M,v as P,x as p,b as R,y as f,o as T,_ as D}from"./index-CG0WJko8.js";const I={class:"right"},j={class:"nick"},z={class:"page-title-bar"},F={class:"page-title"},G={class:"page-content"},H=C({__name:"AdminLayout",setup(J){const u=V(),d=q(),s=A(),v=f(()=>u.path),g=f(()=>u.meta.title||"");L(async()=>{try{s.user||await s.fetchProfile()}catch{N.error("获取用户信息失败,请重新登录"),await s.logout(),d.replace({name:"admin-login",query:{redirect:u.fullPath}})}});async function x(){await s.logout(),d.push({name:"admin-login"})}return(K,n)=>{const i=a("el-icon"),r=a("el-menu-item"),y=a("el-menu"),h=a("el-aside"),k=a("el-button"),b=a("el-header"),w=a("router-view"),B=a("el-main"),c=a("el-container");return T(),S(c,{class:"admin-root"},{default:t(()=>[e(h,{width:"220px",class:"aside"},{default:t(()=>[n[3]||(n[3]=o("div",{class:"brand"},"BookSystem 管理",-1)),e(y,{"default-active":v.value,router:"","background-color":"#0f172a","text-color":"#e2e8f0","active-text-color":"#38bdf8"},{default:t(()=>[e(r,{index:"/admin/users",route:{name:"admin-users"}},{default:t(()=>[e(i,null,{default:t(()=>[e(l(E))]),_:1}),n[0]||(n[0]=o("span",null,"用户",-1))]),_:1}),e(r,{index:"/admin/roles",route:{name:"admin-roles"}},{default:t(()=>[e(i,null,{default:t(()=>[e(l(M))]),_:1}),n[1]||(n[1]=o("span",null,"角色",-1))]),_:1}),e(r,{index:"/admin/permissions",route:{name:"admin-permissions"}},{default:t(()=>[e(i,null,{default:t(()=>[e(l(P))]),_:1}),n[2]||(n[2]=o("span",null,"权限",-1))]),_:1})]),_:1},8,["default-active"])]),_:1}),e(c,null,{default:t(()=>[e(b,{class:"header"},{default:t(()=>{var _,m;return[o("div",I,[o("span",j,p(((_=l(s).user)==null?void 0:_.nickname)||((m=l(s).user)==null?void 0:m.username)),1),e(k,{type:"primary",link:"",onClick:x},{default:t(()=>[...n[4]||(n[4]=[R("退出",-1)])]),_:1})])]}),_:1}),e(B,{class:"main"},{default:t(()=>[o("div",z,[o("h1",F,p(g.value),1)]),o("div",G,[e(w)])]),_:1})]),_:1})]),_:1})}}}),Q=D(H,[["__scopeId","data-v-c0bd569a"]]);export{Q as default};
|
import{f as C,u as A,g as L,E as N,p as S,w as t,k as V,l as q,r as a,d as e,a as o,q as l,s as E,t as M,v as P,x as p,b as R,y as f,o as T,_ as D}from"./index-B340d_U7.js";const I={class:"right"},j={class:"nick"},z={class:"page-title-bar"},F={class:"page-title"},G={class:"page-content"},H=C({__name:"AdminLayout",setup(J){const u=V(),d=q(),s=A(),v=f(()=>u.path),g=f(()=>u.meta.title||"");L(async()=>{try{s.user||await s.fetchProfile()}catch{N.error("获取用户信息失败,请重新登录"),await s.logout(),d.replace({name:"admin-login",query:{redirect:u.fullPath}})}});async function x(){await s.logout(),d.push({name:"admin-login"})}return(K,n)=>{const i=a("el-icon"),r=a("el-menu-item"),y=a("el-menu"),h=a("el-aside"),k=a("el-button"),b=a("el-header"),w=a("router-view"),B=a("el-main"),c=a("el-container");return T(),S(c,{class:"admin-root"},{default:t(()=>[e(h,{width:"220px",class:"aside"},{default:t(()=>[n[3]||(n[3]=o("div",{class:"brand"},"BookSystem 管理",-1)),e(y,{"default-active":v.value,router:"","background-color":"#0f172a","text-color":"#e2e8f0","active-text-color":"#38bdf8"},{default:t(()=>[e(r,{index:"/admin/users",route:{name:"admin-users"}},{default:t(()=>[e(i,null,{default:t(()=>[e(l(E))]),_:1}),n[0]||(n[0]=o("span",null,"用户",-1))]),_:1}),e(r,{index:"/admin/roles",route:{name:"admin-roles"}},{default:t(()=>[e(i,null,{default:t(()=>[e(l(M))]),_:1}),n[1]||(n[1]=o("span",null,"角色",-1))]),_:1}),e(r,{index:"/admin/permissions",route:{name:"admin-permissions"}},{default:t(()=>[e(i,null,{default:t(()=>[e(l(P))]),_:1}),n[2]||(n[2]=o("span",null,"权限",-1))]),_:1})]),_:1},8,["default-active"])]),_:1}),e(c,null,{default:t(()=>[e(b,{class:"header"},{default:t(()=>{var _,m;return[o("div",I,[o("span",j,p(((_=l(s).user)==null?void 0:_.nickname)||((m=l(s).user)==null?void 0:m.username)),1),e(k,{type:"primary",link:"",onClick:x},{default:t(()=>[...n[4]||(n[4]=[R("退出",-1)])]),_:1})])]}),_:1}),e(B,{class:"main"},{default:t(()=>[o("div",z,[o("h1",F,p(g.value),1)]),o("div",G,[e(w)])]),_:1})]),_:1})]),_:1})}}}),Q=D(H,[["__scopeId","data-v-c0bd569a"]]);export{Q as default};
|
||||||
@ -1 +1 @@
|
|||||||
import{f as L,u as q,g as I,c as f,d as o,w as s,h as S,E as h,r as u,i,j,k as B,l as E,o as w,m as M,a as g,b as N,n as O,_ as T}from"./index-CG0WJko8.js";const D={class:"login-wrap"},A={class:"captcha-row"},P=["src"],W={key:1,class:"captcha-placeholder"},X=L({__name:"LoginView",setup($){const y=B(),V=E(),v=q(),b=i(),m=i(!1),l=i(""),p=i(""),k="/api",a=j({username:"",password:"",captcha_code:""}),x={username:[{required:!0,message:"请输入用户名",trigger:"blur"}],password:[{required:!0,message:"请输入密码",trigger:"blur"}],captcha_code:[{required:!0,message:"请输入验证码",trigger:"blur"}]};async function _(){l.value.startsWith("blob:")&&URL.revokeObjectURL(l.value),l.value="",p.value="";const r=await S.get(`${k}/users/captcha`,{responseType:"blob",headers:{}}),e=r.headers["x-captcha-id"]||r.headers["X-Captcha-ID"];if(!e){h.error("未获取到验证码 ID,请检查后端 CORS expose_headers");return}p.value=e,l.value=URL.createObjectURL(r.data),a.captcha_code=""}async function R(){var e,n;if(!p.value){h.warning("请先加载验证码");return}const r=b.value;if(r){try{await r.validate()}catch{return}m.value=!0;try{const{data:t}=await O.post("/users/login",{username:a.username,password:a.password,captcha_id:p.value,captcha_code:a.captcha_code});v.setToken(t.access_token),await v.fetchProfile();const c=y.query.redirect||"/admin";await V.replace(c)}catch(t){const c=(n=(e=t==null?void 0:t.response)==null?void 0:e.data)==null?void 0:n.detail;h.error(typeof c=="string"?c:"登录失败"),await _()}finally{m.value=!1}}}return I(()=>{_()}),(r,e)=>{const n=u("el-input"),t=u("el-form-item"),c=u("el-button"),U=u("el-form"),C=u("el-card");return w(),f("div",D,[o(C,{class:"card",shadow:"hover"},{header:s(()=>[...e[4]||(e[4]=[g("span",{class:"card-title"},"管理后台登录",-1)])]),default:s(()=>[o(U,{ref_key:"formRef",ref:b,model:a,rules:x,"label-width":"88px",onSubmit:e[3]||(e[3]=M(()=>{},["prevent"]))},{default:s(()=>[o(t,{label:"用户名",prop:"username"},{default:s(()=>[o(n,{modelValue:a.username,"onUpdate:modelValue":e[0]||(e[0]=d=>a.username=d),autocomplete:"username"},null,8,["modelValue"])]),_:1}),o(t,{label:"密码",prop:"password"},{default:s(()=>[o(n,{modelValue:a.password,"onUpdate:modelValue":e[1]||(e[1]=d=>a.password=d),type:"password","show-password":"",autocomplete:"current-password"},null,8,["modelValue"])]),_:1}),o(t,{label:"验证码",prop:"captcha_code"},{default:s(()=>[g("div",A,[o(n,{modelValue:a.captcha_code,"onUpdate:modelValue":e[2]||(e[2]=d=>a.captcha_code=d),maxlength:"8",placeholder:"请输入验证码"},null,8,["modelValue"]),g("div",{class:"captcha-img",onClick:_},[l.value?(w(),f("img",{key:0,src:l.value,alt:"captcha"},null,8,P)):(w(),f("span",W,"点击获取"))])])]),_:1}),o(t,null,{default:s(()=>[o(c,{type:"primary",loading:m.value,style:{width:"100%"},onClick:R},{default:s(()=>[...e[5]||(e[5]=[N(" 登录 ",-1)])]),_:1},8,["loading"])]),_:1})]),_:1},8,["model"])]),_:1})])}}}),F=T(X,[["__scopeId","data-v-24c39918"]]);export{F as default};
|
import{f as L,u as q,g as I,c as f,d as o,w as s,h as S,E as h,r as u,i,j,k as B,l as E,o as w,m as M,a as g,b as N,n as O,_ as T}from"./index-B340d_U7.js";const D={class:"login-wrap"},A={class:"captcha-row"},P=["src"],W={key:1,class:"captcha-placeholder"},X=L({__name:"LoginView",setup($){const y=B(),V=E(),v=q(),b=i(),m=i(!1),l=i(""),p=i(""),k="/api",a=j({username:"",password:"",captcha_code:""}),x={username:[{required:!0,message:"请输入用户名",trigger:"blur"}],password:[{required:!0,message:"请输入密码",trigger:"blur"}],captcha_code:[{required:!0,message:"请输入验证码",trigger:"blur"}]};async function _(){l.value.startsWith("blob:")&&URL.revokeObjectURL(l.value),l.value="",p.value="";const r=await S.get(`${k}/users/captcha`,{responseType:"blob",headers:{}}),e=r.headers["x-captcha-id"]||r.headers["X-Captcha-ID"];if(!e){h.error("未获取到验证码 ID,请检查后端 CORS expose_headers");return}p.value=e,l.value=URL.createObjectURL(r.data),a.captcha_code=""}async function R(){var e,n;if(!p.value){h.warning("请先加载验证码");return}const r=b.value;if(r){try{await r.validate()}catch{return}m.value=!0;try{const{data:t}=await O.post("/users/login",{username:a.username,password:a.password,captcha_id:p.value,captcha_code:a.captcha_code});v.setToken(t.access_token),await v.fetchProfile();const c=y.query.redirect||"/admin";await V.replace(c)}catch(t){const c=(n=(e=t==null?void 0:t.response)==null?void 0:e.data)==null?void 0:n.detail;h.error(typeof c=="string"?c:"登录失败"),await _()}finally{m.value=!1}}}return I(()=>{_()}),(r,e)=>{const n=u("el-input"),t=u("el-form-item"),c=u("el-button"),U=u("el-form"),C=u("el-card");return w(),f("div",D,[o(C,{class:"card",shadow:"hover"},{header:s(()=>[...e[4]||(e[4]=[g("span",{class:"card-title"},"管理后台登录",-1)])]),default:s(()=>[o(U,{ref_key:"formRef",ref:b,model:a,rules:x,"label-width":"88px",onSubmit:e[3]||(e[3]=M(()=>{},["prevent"]))},{default:s(()=>[o(t,{label:"用户名",prop:"username"},{default:s(()=>[o(n,{modelValue:a.username,"onUpdate:modelValue":e[0]||(e[0]=d=>a.username=d),autocomplete:"username"},null,8,["modelValue"])]),_:1}),o(t,{label:"密码",prop:"password"},{default:s(()=>[o(n,{modelValue:a.password,"onUpdate:modelValue":e[1]||(e[1]=d=>a.password=d),type:"password","show-password":"",autocomplete:"current-password"},null,8,["modelValue"])]),_:1}),o(t,{label:"验证码",prop:"captcha_code"},{default:s(()=>[g("div",A,[o(n,{modelValue:a.captcha_code,"onUpdate:modelValue":e[2]||(e[2]=d=>a.captcha_code=d),maxlength:"8",placeholder:"请输入验证码"},null,8,["modelValue"]),g("div",{class:"captcha-img",onClick:_},[l.value?(w(),f("img",{key:0,src:l.value,alt:"captcha"},null,8,P)):(w(),f("span",W,"点击获取"))])])]),_:1}),o(t,null,{default:s(()=>[o(c,{type:"primary",loading:m.value,style:{width:"100%"},onClick:R},{default:s(()=>[...e[5]||(e[5]=[N(" 登录 ",-1)])]),_:1},8,["loading"])]),_:1})]),_:1},8,["model"])]),_:1})])}}}),F=T(X,[["__scopeId","data-v-24c39918"]]);export{F as default};
|
||||||
1
frontend/dist/assets/PermissionManage-C7NwWP_j.js
vendored
Normal file
1
frontend/dist/assets/PermissionManage-C7NwWP_j.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
import{z as a,n as s,f as m,A as d,g as c,p,w as l,r as n,d as f,B as y,q as h,o as g}from"./index-B340d_U7.js";import{t as _}from"./list-query-4ZZvVtjn.js";const w=()=>({crudOptions:{pagination:{show:!1},request:{transformQuery:({form:e})=>_(e,["name","code"]),pageRequest:async e=>{const{data:t}=await s.get("/permissions/",{params:e});return t},transformRes:({res:e})=>{const t=Array.isArray(e)?e:[];return{currentPage:1,pageSize:Math.max(t.length,1),total:t.length,records:t}},addRequest:async({form:e})=>{await s.post("/permissions/",e)},editRequest:async({form:e,row:t})=>{t&&await s.put(`/permissions/${t.id}`,e)},delRequest:async({row:e})=>{e&&await s.delete(`/permissions/${e.id}`)}},rowHandle:{buttons:{edit:{text:"编辑"},remove:{text:"删除"}}},columns:{id:{title:"ID",key:"id",type:"number",column:{width:70},form:{show:!1}},name:{title:"名称",key:"name",type:"text",search:{show:!0},form:{rules:[{required:!0,message:"必填"}]}},code:{title:"编码",key:"code",type:"text",search:{show:!0},form:{rules:[{required:!0,message:"必填"}]}},parentid:{title:"父级ID",key:"parentid",type:"number",form:{value:0,rules:[{required:!0,message:"必填"}]}},description:{title:"描述",key:"description",type:"textarea",column:{show:!1}},createtime:{title:"创建时间",key:"createtime",type:"text",form:{show:!1},column:{width:170},valueBuilder({value:e,row:t,key:r}){!t||e==null||(t[r]=a(e).format("YYYY-MM-DD HH:mm:ss"))}},updatetime:{title:"更新时间",key:"updatetime",type:"text",form:{show:!1},column:{width:170},valueBuilder({value:e,row:t,key:r}){!t||e==null||(t[r]=a(e).format("YYYY-MM-DD HH:mm:ss"))}}}}}),Y=m({__name:"PermissionManage",setup(e){const{crudBinding:t,crudRef:r,crudExpose:o}=d({createCrudOptions:w});return c(()=>{o.doRefresh()}),(x,k)=>{const i=n("fs-crud"),u=n("fs-page");return g(),p(u,null,{default:l(()=>[f(i,y({ref_key:"crudRef",ref:r},h(t)),null,16)]),_:1})}}});export{Y as default};
|
||||||
@ -1 +0,0 @@
|
|||||||
import{z as a,n as r,f as d,A as m,g as c,p as l,w as p,r as n,d as f,B as y,q as h,o as g}from"./index-CG0WJko8.js";const _=()=>({crudOptions:{pagination:{show:!1},request:{pageRequest:async()=>{const{data:e}=await r.get("/permissions/");return e},transformRes:({res:e})=>{const t=Array.isArray(e)?e:[];return{currentPage:1,pageSize:Math.max(t.length,1),total:t.length,records:t}},addRequest:async({form:e})=>{await r.post("/permissions/",e)},editRequest:async({form:e,row:t})=>{t&&await r.put(`/permissions/${t.id}`,e)},delRequest:async({row:e})=>{e&&await r.delete(`/permissions/${e.id}`)}},rowHandle:{buttons:{edit:{text:"编辑"},remove:{text:"删除"}}},columns:{id:{title:"ID",key:"id",type:"number",column:{width:70},form:{show:!1}},name:{title:"名称",key:"name",type:"text",search:{show:!0},form:{rules:[{required:!0,message:"必填"}]}},code:{title:"编码",key:"code",type:"text",search:{show:!0},form:{rules:[{required:!0,message:"必填"}]}},parentid:{title:"父级ID",key:"parentid",type:"number",form:{value:0,rules:[{required:!0,message:"必填"}]}},description:{title:"描述",key:"description",type:"textarea",column:{show:!1}},createtime:{title:"创建时间",key:"createtime",type:"text",form:{show:!1},column:{width:170},valueBuilder({value:e,row:t,key:s}){!t||e==null||(t[s]=a(e).format("YYYY-MM-DD HH:mm:ss"))}},updatetime:{title:"更新时间",key:"updatetime",type:"text",form:{show:!1},column:{width:170},valueBuilder({value:e,row:t,key:s}){!t||e==null||(t[s]=a(e).format("YYYY-MM-DD HH:mm:ss"))}}}}}),q=d({__name:"PermissionManage",setup(e){const{crudBinding:t,crudRef:s,crudExpose:o}=m({createCrudOptions:_});return c(()=>{o.doRefresh()}),(w,x)=>{const i=n("fs-crud"),u=n("fs-page");return g(),l(u,null,{default:p(()=>[f(i,y({ref_key:"crudRef",ref:s},h(t)),null,16)]),_:1})}}});export{q as default};
|
|
||||||
@ -1 +1 @@
|
|||||||
import{_ as n,c as r,a as s,b as t,d as l,w as d,r as i,o as a}from"./index-CG0WJko8.js";const p={},u={class:"public-wrap"},m={class:"hint"};function f(_,o){const e=i("router-link");return a(),r("div",u,[o[5]||(o[5]=s("h1",null,"BookSystem",-1)),s("p",m,[o[1]||(o[1]=t(" 此处预留为",-1)),o[2]||(o[2]=s("strong",null,"无需登录",-1)),o[3]||(o[3]=t("的站点首页(图书浏览、门户等)。管理功能请前往 ",-1)),l(e,{to:"/admin/login"},{default:d(()=>[...o[0]||(o[0]=[t("管理后台",-1)])]),_:1}),o[4]||(o[4]=t("。 ",-1))])])}const x=n(p,[["render",f],["__scopeId","data-v-f5876933"]]);export{x as default};
|
import{_ as n,c as r,a as s,b as t,d as l,w as d,r as i,o as a}from"./index-B340d_U7.js";const p={},u={class:"public-wrap"},m={class:"hint"};function f(_,o){const e=i("router-link");return a(),r("div",u,[o[5]||(o[5]=s("h1",null,"BookSystem",-1)),s("p",m,[o[1]||(o[1]=t(" 此处预留为",-1)),o[2]||(o[2]=s("strong",null,"无需登录",-1)),o[3]||(o[3]=t("的站点首页(图书浏览、门户等)。管理功能请前往 ",-1)),l(e,{to:"/admin/login"},{default:d(()=>[...o[0]||(o[0]=[t("管理后台",-1)])]),_:1}),o[4]||(o[4]=t("。 ",-1))])])}const x=n(p,[["render",f],["__scopeId","data-v-f5876933"]]);export{x as default};
|
||||||
1
frontend/dist/assets/RoleManage-CvRyPV5W.js
vendored
1
frontend/dist/assets/RoleManage-CvRyPV5W.js
vendored
@ -1 +0,0 @@
|
|||||||
import{z as B,n as u,f as F,A as I,g as U,p as v,w as m,r as p,i,d as _,B as j,q as G,c as h,b as k,a as P,x,C as Y,F as L,D as J,E as R,o as r,G as Q,_ as W}from"./index-CG0WJko8.js";function X(C){return()=>({crudOptions:{pagination:{show:!1},request:{pageRequest:async()=>{const{data:t}=await u.get("/roles/");return t},transformRes:({res:t})=>{const s=Array.isArray(t)?t:[];return{currentPage:1,pageSize:Math.max(s.length,1),total:s.length,records:s}},addRequest:async({form:t})=>{await u.post("/roles/",t)},editRequest:async({form:t,row:s})=>{s&&await u.put(`/roles/${s.id}`,t)},delRequest:async({row:t})=>{t&&await u.delete(`/roles/${t.id}`)}},rowHandle:{width:300,buttons:{viewPerm:{text:"查看",order:1,click:({row:t})=>{t&&C.onView(t)}},assignPerm:{text:"分配权限",order:2,click:({row:t})=>{t&&C.onAssign(t)}},edit:{text:"编辑",order:10},remove:{text:"删除",order:20}}},columns:{id:{title:"ID",key:"id",type:"number",column:{width:70},form:{show:!1}},name:{title:"角色名称",key:"name",type:"text",search:{show:!0},form:{rules:[{required:!0,message:"必填"}]}},creator:{title:"创建人",key:"creator",type:"text"},createtime:{title:"创建时间",key:"createtime",type:"text",form:{show:!1},column:{width:170},valueBuilder({value:t,row:s,key:d}){!s||t==null||(s[d]=B(t).format("YYYY-MM-DD HH:mm:ss"))}},updatetime:{title:"更新时间",key:"updatetime",type:"text",form:{show:!1},column:{width:170},valueBuilder({value:t,row:s,key:d}){!s||t==null||(s[d]=B(t).format("YYYY-MM-DD HH:mm:ss"))}}}}})}const Z={key:0,class:"dlg-role"},ee={key:1,class:"perm-tags"},te={key:0,class:"dlg-role"},se=F({__name:"RoleManage",setup(C){const t=i(!1),s=i(!1),d=i(null),g=i([]),y=i(null),V=i([]),w=i(),b=i(!1),$={label:"label",children:"children"};function D(a){return a.map(e=>{var n;return{id:e.id,label:`${e.name}(${e.code})`,children:(n=e.children)!=null&&n.length?D(e.children):void 0}})}async function q(){const{data:a}=await u.get("/permissions/tree");V.value=D(a??[])}async function T(a){d.value=a,t.value=!0;try{const{data:e}=await u.get(`/permissions/role/${a.id}`);g.value=e.permissions??[]}catch{R.error("加载权限失败"),g.value=[]}}function H(a){y.value=a,s.value=!0}async function O(){var e,n;const a=y.value;if(a)try{const{data:f}=await u.get(`/permissions/role/${a.id}`),o=(f.permissions??[]).map(c=>c.id);await Q(),(e=w.value)==null||e.setCheckedKeys(o)}catch{R.error("加载角色权限失败"),(n=w.value)==null||n.setCheckedKeys([])}}async function z(){var e,n,f;const a=y.value;if(a){b.value=!0;try{const o=((e=w.value)==null?void 0:e.getCheckedKeys(!1))??[];await u.post("/permissions/assign",{role_id:a.id,permission_ids:o}),R.success("保存成功"),s.value=!1,M.doRefresh()}catch(o){const c=(f=(n=o==null?void 0:o.response)==null?void 0:n.data)==null?void 0:f.detail;R.error(typeof c=="string"?c:"保存失败")}finally{b.value=!1}}}const{crudBinding:E,crudRef:N,crudExpose:M}=I({createCrudOptions:X({onView:T,onAssign:H})});return U(async()=>{await q(),M.doRefresh()}),(a,e)=>{const n=p("fs-crud"),f=p("el-tag"),o=p("el-empty"),c=p("el-dialog"),K=p("el-tree"),A=p("el-button"),S=p("fs-page");return r(),v(S,null,{default:m(()=>[_(n,j({ref_key:"crudRef",ref:N},G(E)),null,16),_(c,{modelValue:t.value,"onUpdate:modelValue":e[0]||(e[0]=l=>t.value=l),title:"查看角色权限",width:"560px","destroy-on-close":"",onClosed:e[1]||(e[1]=l=>g.value=[])},{default:m(()=>[d.value?(r(),h("p",Z,[e[4]||(e[4]=k(" 角色:",-1)),P("strong",null,x(d.value.name),1)])):Y("",!0),g.value.length?(r(),h("div",ee,[(r(!0),h(L,null,J(g.value,l=>(r(),v(f,{key:l.id,class:"perm-tag",type:"info"},{default:m(()=>[k(x(l.name)+"("+x(l.code)+") ",1)]),_:2},1024))),128))])):(r(),v(o,{key:2,description:"暂未分配权限","image-size":72}))]),_:1},8,["modelValue"]),_(c,{modelValue:s.value,"onUpdate:modelValue":e[3]||(e[3]=l=>s.value=l),title:"分配权限",width:"640px","destroy-on-close":"",onOpen:O},{footer:m(()=>[_(A,{onClick:e[2]||(e[2]=l=>s.value=!1)},{default:m(()=>[...e[6]||(e[6]=[k("取消",-1)])]),_:1}),_(A,{type:"primary",loading:b.value,onClick:z},{default:m(()=>[...e[7]||(e[7]=[k(" 保存 ",-1)])]),_:1},8,["loading"])]),default:m(()=>[y.value?(r(),h("p",te,[e[5]||(e[5]=k(" 角色:",-1)),P("strong",null,x(y.value.name),1)])):Y("",!0),V.value.length?(r(),v(K,{key:1,ref_key:"permTreeRef",ref:w,class:"perm-tree",data:V.value,"show-checkbox":"","node-key":"id",props:$,"default-expand-all":"","check-strictly":!1},null,8,["data"])):(r(),v(o,{key:2,description:"暂无权限数据","image-size":64}))]),_:1},8,["modelValue"])]),_:1})}}}),ne=W(se,[["__scopeId","data-v-714ba56c"]]);export{ne as default};
|
|
||||||
1
frontend/dist/assets/RoleManage-DlOeFBnz.js
vendored
Normal file
1
frontend/dist/assets/RoleManage-DlOeFBnz.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
import{z as A,n as u,f as F,A as I,g as U,p as v,w as m,r as p,i,d as _,B as j,q as G,c as h,b as k,a as B,x,C as Y,F as L,D as Q,E as R,o as l,G as J,_ as W}from"./index-B340d_U7.js";import{t as X}from"./list-query-4ZZvVtjn.js";function Z(C){return()=>({crudOptions:{pagination:{show:!1},request:{transformQuery:({form:t})=>X(t,["name"]),pageRequest:async t=>{const{data:s}=await u.get("/roles/",{params:t});return s},transformRes:({res:t})=>{const s=Array.isArray(t)?t:[];return{currentPage:1,pageSize:Math.max(s.length,1),total:s.length,records:s}},addRequest:async({form:t})=>{await u.post("/roles/",t)},editRequest:async({form:t,row:s})=>{s&&await u.put(`/roles/${s.id}`,t)},delRequest:async({row:t})=>{t&&await u.delete(`/roles/${t.id}`)}},rowHandle:{width:300,buttons:{viewPerm:{text:"查看",order:1,click:({row:t})=>{t&&C.onView(t)}},assignPerm:{text:"分配权限",order:2,click:({row:t})=>{t&&C.onAssign(t)}},edit:{text:"编辑",order:10},remove:{text:"删除",order:20}}},columns:{id:{title:"ID",key:"id",type:"number",column:{width:70},form:{show:!1}},name:{title:"角色名称",key:"name",type:"text",search:{show:!0},form:{rules:[{required:!0,message:"必填"}]}},creator:{title:"创建人",key:"creator",type:"text"},createtime:{title:"创建时间",key:"createtime",type:"text",form:{show:!1},column:{width:170},valueBuilder({value:t,row:s,key:d}){!s||t==null||(s[d]=A(t).format("YYYY-MM-DD HH:mm:ss"))}},updatetime:{title:"更新时间",key:"updatetime",type:"text",form:{show:!1},column:{width:170},valueBuilder({value:t,row:s,key:d}){!s||t==null||(s[d]=A(t).format("YYYY-MM-DD HH:mm:ss"))}}}}})}const ee={key:0,class:"dlg-role"},te={key:1,class:"perm-tags"},se={key:0,class:"dlg-role"},ae=F({__name:"RoleManage",setup(C){const t=i(!1),s=i(!1),d=i(null),g=i([]),y=i(null),V=i([]),w=i(),b=i(!1),$={label:"label",children:"children"};function D(a){return a.map(e=>{var n;return{id:e.id,label:`${e.name}(${e.code})`,children:(n=e.children)!=null&&n.length?D(e.children):void 0}})}async function q(){const{data:a}=await u.get("/permissions/tree");V.value=D(a??[])}async function T(a){d.value=a,t.value=!0;try{const{data:e}=await u.get(`/permissions/role/${a.id}`);g.value=e.permissions??[]}catch{R.error("加载权限失败"),g.value=[]}}function H(a){y.value=a,s.value=!0}async function O(){var e,n;const a=y.value;if(a)try{const{data:f}=await u.get(`/permissions/role/${a.id}`),o=(f.permissions??[]).map(c=>c.id);await J(),(e=w.value)==null||e.setCheckedKeys(o)}catch{R.error("加载角色权限失败"),(n=w.value)==null||n.setCheckedKeys([])}}async function z(){var e,n,f;const a=y.value;if(a){b.value=!0;try{const o=((e=w.value)==null?void 0:e.getCheckedKeys(!1))??[];await u.post("/permissions/assign",{role_id:a.id,permission_ids:o}),R.success("保存成功"),s.value=!1,M.doRefresh()}catch(o){const c=(f=(n=o==null?void 0:o.response)==null?void 0:n.data)==null?void 0:f.detail;R.error(typeof c=="string"?c:"保存失败")}finally{b.value=!1}}}const{crudBinding:E,crudRef:N,crudExpose:M}=I({createCrudOptions:Z({onView:T,onAssign:H})});return U(async()=>{await q(),M.doRefresh()}),(a,e)=>{const n=p("fs-crud"),f=p("el-tag"),o=p("el-empty"),c=p("el-dialog"),S=p("el-tree"),P=p("el-button"),K=p("fs-page");return l(),v(K,null,{default:m(()=>[_(n,j({ref_key:"crudRef",ref:N},G(E)),null,16),_(c,{modelValue:t.value,"onUpdate:modelValue":e[0]||(e[0]=r=>t.value=r),title:"查看角色权限",width:"560px","destroy-on-close":"",onClosed:e[1]||(e[1]=r=>g.value=[])},{default:m(()=>[d.value?(l(),h("p",ee,[e[4]||(e[4]=k(" 角色:",-1)),B("strong",null,x(d.value.name),1)])):Y("",!0),g.value.length?(l(),h("div",te,[(l(!0),h(L,null,Q(g.value,r=>(l(),v(f,{key:r.id,class:"perm-tag",type:"info"},{default:m(()=>[k(x(r.name)+"("+x(r.code)+") ",1)]),_:2},1024))),128))])):(l(),v(o,{key:2,description:"暂未分配权限","image-size":72}))]),_:1},8,["modelValue"]),_(c,{modelValue:s.value,"onUpdate:modelValue":e[3]||(e[3]=r=>s.value=r),title:"分配权限",width:"640px","destroy-on-close":"",onOpen:O},{footer:m(()=>[_(P,{onClick:e[2]||(e[2]=r=>s.value=!1)},{default:m(()=>[...e[6]||(e[6]=[k("取消",-1)])]),_:1}),_(P,{type:"primary",loading:b.value,onClick:z},{default:m(()=>[...e[7]||(e[7]=[k(" 保存 ",-1)])]),_:1},8,["loading"])]),default:m(()=>[y.value?(l(),h("p",se,[e[5]||(e[5]=k(" 角色:",-1)),B("strong",null,x(y.value.name),1)])):Y("",!0),V.value.length?(l(),v(S,{key:1,ref_key:"permTreeRef",ref:w,class:"perm-tree",data:V.value,"show-checkbox":"","node-key":"id",props:$,"default-expand-all":"","check-strictly":!1},null,8,["data"])):(l(),v(o,{key:2,description:"暂无权限数据","image-size":64}))]),_:1},8,["modelValue"])]),_:1})}}}),re=W(ae,[["__scopeId","data-v-714ba56c"]]);export{re as default};
|
||||||
1
frontend/dist/assets/UserManage-Bvgn1DBa.js
vendored
Normal file
1
frontend/dist/assets/UserManage-Bvgn1DBa.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
import{z as a,O as l,n as r,f as m,A as d,g as c,p,w as f,r as o,d as h,B as y,q as w,o as g}from"./index-B340d_U7.js";import{t as _}from"./list-query-4ZZvVtjn.js";const k=()=>({crudOptions:{pagination:{show:!1},request:{transformQuery:({form:e})=>_(e,["username","nickname","email","phone"]),pageRequest:async e=>{const{data:t}=await r.get("/users/",{params:e});return t},transformRes:({res:e})=>{const t=Array.isArray(e)?e:[];return{currentPage:1,pageSize:Math.max(t.length,1),total:t.length,records:t}},addRequest:async({form:e})=>{await r.post("/users/",e)},editRequest:async({form:e,row:t})=>{if(!t)return;const s={...e};s.password||delete s.password,await r.put(`/users/${t.id}`,s)},delRequest:async({row:e})=>{e&&await r.delete(`/users/${e.id}`)}},rowHandle:{buttons:{edit:{text:"编辑"},remove:{text:"删除"}}},columns:{id:{title:"ID",key:"id",type:"number",column:{width:70},form:{show:!1}},username:{title:"登录名",key:"username",type:"text",search:{show:!0},form:{rules:[{required:!0,message:"必填"}]}},nickname:{title:"昵称",key:"nickname",type:"text",search:{show:!0},form:{rules:[{required:!0,message:"必填"}]}},password:{title:"密码",key:"password",type:"password",column:{show:!1},form:{show:!1},addForm:{show:!0,rules:[{required:!0,message:"新建用户需设置密码"}]},editForm:{show:!0,component:{placeholder:"不修改请留空"}}},email:{title:"邮箱",key:"email",type:"text",search:{show:!0}},phone:{title:"手机",key:"phone",type:"text",search:{show:!0}},wx_openid:{title:"微信OpenID",key:"wx_openid",type:"text",column:{show:!1}},avatar:{title:"头像",key:"avatar",type:"text",column:{show:!1}},role_id:{title:"角色",key:"role_id",type:"dict-select",dict:l({getData:async()=>{const{data:e}=await r.get("/roles/");return e},value:"id",label:"name"}),form:{rules:[{required:!0,message:"必选"}],value:2}},createtime:{title:"创建时间",key:"createtime",type:"text",form:{show:!1},column:{width:170},valueBuilder({value:e,row:t,key:s}){!t||e==null||(t[s]=a(e).format("YYYY-MM-DD HH:mm:ss"))}},updatetime:{title:"更新时间",key:"updatetime",type:"text",form:{show:!1},column:{width:170},valueBuilder({value:e,row:t,key:s}){!t||e==null||(t[s]=a(e).format("YYYY-MM-DD HH:mm:ss"))}},lastlogintime:{title:"最后登录",key:"lastlogintime",type:"text",form:{show:!1},column:{width:170},valueBuilder({value:e,row:t,key:s}){!t||e==null||(t[s]=a(e).format("YYYY-MM-DD HH:mm:ss"))}}}}}),M=m({__name:"UserManage",setup(e){const{crudBinding:t,crudRef:s,crudExpose:n}=d({createCrudOptions:k});return c(()=>{n.doRefresh()}),(x,Y)=>{const u=o("fs-crud"),i=o("fs-page");return g(),p(i,null,{default:f(()=>[h(u,y({ref_key:"crudRef",ref:s},w(t)),null,16)]),_:1})}}});export{M as default};
|
||||||
1
frontend/dist/assets/UserManage-D2hZze1V.js
vendored
1
frontend/dist/assets/UserManage-D2hZze1V.js
vendored
@ -1 +0,0 @@
|
|||||||
import{z as a,O as i,n as r,f as d,A as m,g as c,p,w as f,r as o,d as y,B as h,q as w,o as g}from"./index-CG0WJko8.js";const _=()=>({crudOptions:{pagination:{show:!1},request:{pageRequest:async()=>{const{data:e}=await r.get("/users/");return e},transformRes:({res:e})=>{const t=Array.isArray(e)?e:[];return{currentPage:1,pageSize:Math.max(t.length,1),total:t.length,records:t}},addRequest:async({form:e})=>{await r.post("/users/",e)},editRequest:async({form:e,row:t})=>{if(!t)return;const s={...e};s.password||delete s.password,await r.put(`/users/${t.id}`,s)},delRequest:async({row:e})=>{e&&await r.delete(`/users/${e.id}`)}},rowHandle:{buttons:{edit:{text:"编辑"},remove:{text:"删除"}}},columns:{id:{title:"ID",key:"id",type:"number",column:{width:70},form:{show:!1}},username:{title:"登录名",key:"username",type:"text",search:{show:!0},form:{rules:[{required:!0,message:"必填"}]}},nickname:{title:"昵称",key:"nickname",type:"text",form:{rules:[{required:!0,message:"必填"}]}},password:{title:"密码",key:"password",type:"password",column:{show:!1},form:{show:!1},addForm:{show:!0,rules:[{required:!0,message:"新建用户需设置密码"}]},editForm:{show:!0,component:{placeholder:"不修改请留空"}}},email:{title:"邮箱",key:"email",type:"text"},phone:{title:"手机",key:"phone",type:"text"},wx_openid:{title:"微信OpenID",key:"wx_openid",type:"text",column:{show:!1}},avatar:{title:"头像",key:"avatar",type:"text",column:{show:!1}},role_id:{title:"角色",key:"role_id",type:"dict-select",dict:i({getData:async()=>{const{data:e}=await r.get("/roles/");return e},value:"id",label:"name"}),form:{rules:[{required:!0,message:"必选"}],value:2}},createtime:{title:"创建时间",key:"createtime",type:"text",form:{show:!1},column:{width:170},valueBuilder({value:e,row:t,key:s}){!t||e==null||(t[s]=a(e).format("YYYY-MM-DD HH:mm:ss"))}},updatetime:{title:"更新时间",key:"updatetime",type:"text",form:{show:!1},column:{width:170},valueBuilder({value:e,row:t,key:s}){!t||e==null||(t[s]=a(e).format("YYYY-MM-DD HH:mm:ss"))}},lastlogintime:{title:"最后登录",key:"lastlogintime",type:"text",form:{show:!1},column:{width:170},valueBuilder({value:e,row:t,key:s}){!t||e==null||(t[s]=a(e).format("YYYY-MM-DD HH:mm:ss"))}}}}}),q=d({__name:"UserManage",setup(e){const{crudBinding:t,crudRef:s,crudExpose:n}=m({createCrudOptions:_});return c(()=>{n.doRefresh()}),(x,k)=>{const u=o("fs-crud"),l=o("fs-page");return g(),p(l,null,{default:f(()=>[y(u,h({ref_key:"crudRef",ref:s},w(t)),null,16)]),_:1})}}});export{q as default};
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
frontend/dist/assets/list-query-4ZZvVtjn.js
vendored
Normal file
1
frontend/dist/assets/list-query-4ZZvVtjn.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
function c(o,i){const r={};for(const n of i){const t=o[n];t!=null&&String(t).trim()!==""&&(r[n]=String(t).trim())}return r}export{c as t};
|
||||||
2
frontend/dist/index.html
vendored
2
frontend/dist/index.html
vendored
@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>BookSystem</title>
|
<title>BookSystem</title>
|
||||||
<script type="module" crossorigin src="/assets/index-CG0WJko8.js"></script>
|
<script type="module" crossorigin src="/assets/index-B340d_U7.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-ciY4YJQ7.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-ciY4YJQ7.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
14
frontend/src/modules/admin/utils/list-query.ts
Normal file
14
frontend/src/modules/admin/utils/list-query.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/** 将 CRUD 查询表单中非空字段转为 GET query(组合查询) */
|
||||||
|
export function toSearchParams(
|
||||||
|
form: Record<string, unknown>,
|
||||||
|
keys: readonly string[],
|
||||||
|
): Record<string, string> {
|
||||||
|
const q: Record<string, string> = {};
|
||||||
|
for (const k of keys) {
|
||||||
|
const v = form[k];
|
||||||
|
if (v !== undefined && v !== null && String(v).trim() !== "") {
|
||||||
|
q[k] = String(v).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return q;
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
import type { CreateCrudOptions } from "@fast-crud/fast-crud";
|
import type { CreateCrudOptions } from "@fast-crud/fast-crud";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { http } from "@/modules/admin/api/http";
|
import { http } from "@/modules/admin/api/http";
|
||||||
|
import { toSearchParams } from "@/modules/admin/utils/list-query";
|
||||||
|
|
||||||
export type PermissionRow = {
|
export type PermissionRow = {
|
||||||
id: number;
|
id: number;
|
||||||
@ -17,8 +18,12 @@ export const createPermissionCrudOptions: CreateCrudOptions<PermissionRow> = ()
|
|||||||
crudOptions: {
|
crudOptions: {
|
||||||
pagination: { show: false },
|
pagination: { show: false },
|
||||||
request: {
|
request: {
|
||||||
pageRequest: async () => {
|
transformQuery: ({ form }) =>
|
||||||
const { data } = await http.get<PermissionRow[]>("/permissions/");
|
toSearchParams(form as Record<string, unknown>, ["name", "code"]),
|
||||||
|
pageRequest: async (query) => {
|
||||||
|
const { data } = await http.get<PermissionRow[]>("/permissions/", {
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
transformRes: ({ res }) => {
|
transformRes: ({ res }) => {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type { CreateCrudOptions } from "@fast-crud/fast-crud";
|
import type { CreateCrudOptions } from "@fast-crud/fast-crud";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { http } from "@/modules/admin/api/http";
|
import { http } from "@/modules/admin/api/http";
|
||||||
|
import { toSearchParams } from "@/modules/admin/utils/list-query";
|
||||||
|
|
||||||
export type RoleRow = {
|
export type RoleRow = {
|
||||||
id: number;
|
id: number;
|
||||||
@ -20,8 +21,12 @@ export function buildRoleCrudOptions(h: RoleCrudHandlers): CreateCrudOptions<Rol
|
|||||||
crudOptions: {
|
crudOptions: {
|
||||||
pagination: { show: false },
|
pagination: { show: false },
|
||||||
request: {
|
request: {
|
||||||
pageRequest: async () => {
|
transformQuery: ({ form }) =>
|
||||||
const { data } = await http.get<RoleRow[]>("/roles/");
|
toSearchParams(form as Record<string, unknown>, ["name"]),
|
||||||
|
pageRequest: async (query) => {
|
||||||
|
const { data } = await http.get<RoleRow[]>("/roles/", {
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
transformRes: ({ res }) => {
|
transformRes: ({ res }) => {
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import type { CreateCrudOptions } from "@fast-crud/fast-crud";
|
|||||||
import { dict } from "@fast-crud/fast-crud";
|
import { dict } from "@fast-crud/fast-crud";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { http } from "@/modules/admin/api/http";
|
import { http } from "@/modules/admin/api/http";
|
||||||
|
import { toSearchParams } from "@/modules/admin/utils/list-query";
|
||||||
|
|
||||||
export type UserRow = {
|
export type UserRow = {
|
||||||
id: number;
|
id: number;
|
||||||
@ -22,8 +23,17 @@ export const createUserCrudOptions: CreateCrudOptions<UserRow> = () => {
|
|||||||
crudOptions: {
|
crudOptions: {
|
||||||
pagination: { show: false },
|
pagination: { show: false },
|
||||||
request: {
|
request: {
|
||||||
pageRequest: async () => {
|
transformQuery: ({ form }) =>
|
||||||
const { data } = await http.get<UserRow[]>("/users/");
|
toSearchParams(form as Record<string, unknown>, [
|
||||||
|
"username",
|
||||||
|
"nickname",
|
||||||
|
"email",
|
||||||
|
"phone",
|
||||||
|
]),
|
||||||
|
pageRequest: async (query) => {
|
||||||
|
const { data } = await http.get<UserRow[]>("/users/", {
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
transformRes: ({ res }) => {
|
transformRes: ({ res }) => {
|
||||||
@ -76,6 +86,7 @@ export const createUserCrudOptions: CreateCrudOptions<UserRow> = () => {
|
|||||||
title: "昵称",
|
title: "昵称",
|
||||||
key: "nickname",
|
key: "nickname",
|
||||||
type: "text",
|
type: "text",
|
||||||
|
search: { show: true },
|
||||||
form: {
|
form: {
|
||||||
rules: [{ required: true, message: "必填" }],
|
rules: [{ required: true, message: "必填" }],
|
||||||
},
|
},
|
||||||
@ -99,11 +110,13 @@ export const createUserCrudOptions: CreateCrudOptions<UserRow> = () => {
|
|||||||
title: "邮箱",
|
title: "邮箱",
|
||||||
key: "email",
|
key: "email",
|
||||||
type: "text",
|
type: "text",
|
||||||
|
search: { show: true },
|
||||||
},
|
},
|
||||||
phone: {
|
phone: {
|
||||||
title: "手机",
|
title: "手机",
|
||||||
key: "phone",
|
key: "phone",
|
||||||
type: "text",
|
type: "text",
|
||||||
|
search: { show: true },
|
||||||
},
|
},
|
||||||
wx_openid: {
|
wx_openid: {
|
||||||
title: "微信OpenID",
|
title: "微信OpenID",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user