feat: user list
This commit is contained in:
parent
65b3a7074b
commit
918ab92940
@ -21,30 +21,42 @@
|
|||||||
<div class="c-title">用户添加</div>
|
<div class="c-title">用户添加</div>
|
||||||
<el-form v-if="m">
|
<el-form v-if="m">
|
||||||
<div class="c-item br">
|
<div class="c-item br">
|
||||||
<label class="c-label">昵称:</label>
|
<label class="c-label">*用户名:</label>
|
||||||
<el-input v-model="m.username"></el-input>
|
<el-input v-model="m.username"></el-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-item br">
|
<div class="c-item br">
|
||||||
<label class="c-label">密码:</label>
|
<label class="c-label">昵称:</label>
|
||||||
|
<el-input v-model="m.nickname"></el-input>
|
||||||
|
</div>
|
||||||
|
<div class="c-item br">
|
||||||
|
<label class="c-label">真实姓名:</label>
|
||||||
|
<el-input v-model="m.real_name"></el-input>
|
||||||
|
</div>
|
||||||
|
<div class="c-item br">
|
||||||
|
<label class="c-label">*密码:</label>
|
||||||
<el-input v-model="m.password"></el-input>
|
<el-input v-model="m.password"></el-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-item br">
|
<div class="c-item br">
|
||||||
<label class="c-label">手机:</label>
|
<label class="c-label">*学号:</label>
|
||||||
|
<el-input v-model="m.student_id"></el-input>
|
||||||
|
</div>
|
||||||
|
<div class="c-item br">
|
||||||
|
<label class="c-label">*手机:</label>
|
||||||
<el-input v-model="m.phone"></el-input>
|
<el-input v-model="m.phone"></el-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-item br">
|
<div class="c-item br">
|
||||||
<label class="c-label">角色:</label>
|
<label class="c-label">*角色:</label>
|
||||||
<el-select v-model="m.role_id">
|
<el-select v-model="m.role_id">
|
||||||
<el-option label="管理员" :value="1"></el-option>
|
<el-option label="管理员" :value="'admin'"></el-option>
|
||||||
<el-option label="公告管理员" :value="2"></el-option>
|
<el-option label="普通用户" :value="'student'"></el-option>
|
||||||
<el-option label="普通用户" :value="3"></el-option>
|
<el-option label="校外人员" :value="'out'"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-item br">
|
<div class="c-item br">
|
||||||
<label class="c-label">性别:</label>
|
<label class="c-label">性别:</label>
|
||||||
<el-radio-group v-model="m.sex">
|
<el-radio-group v-model="m.sex">
|
||||||
<el-radio :label="1">男</el-radio>
|
<el-radio :label="'男'">男</el-radio>
|
||||||
<el-radio :label="2">女</el-radio>
|
<el-radio :label="'女'">女</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-item br">
|
<div class="c-item br">
|
||||||
@ -58,11 +70,15 @@
|
|||||||
new Vue({
|
new Vue({
|
||||||
el: '.vue-box',
|
el: '.vue-box',
|
||||||
data: {
|
data: {
|
||||||
m: { // 查询参数
|
m: { // 查询参数
|
||||||
username: '',
|
student_id: '',
|
||||||
password: '',
|
|
||||||
phone: '',
|
phone: '',
|
||||||
sex: 1,
|
username: '',
|
||||||
|
nickname: '',
|
||||||
|
real_name: '',
|
||||||
|
password: '',
|
||||||
|
sex: '男',
|
||||||
|
role_id: 'admin',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -21,12 +21,20 @@
|
|||||||
<div class="c-title">用户列表</div>
|
<div class="c-title">用户列表</div>
|
||||||
<el-form>
|
<el-form>
|
||||||
<div class="c-item">
|
<div class="c-item">
|
||||||
<label class="c-label">用户昵称:</label>
|
<label class="c-label">用户名:</label>
|
||||||
<el-input v-model="p.username" placeholder="模糊查询"></el-input>
|
<el-input v-model="p.username" placeholder="模糊查询"></el-input>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="c-item">
|
||||||
|
<label class="c-label">用户昵称:</label>
|
||||||
|
<el-input v-model="p.nickname" placeholder="模糊查询"></el-input>
|
||||||
|
</div>
|
||||||
|
<div class="c-item">
|
||||||
|
<label class="c-label">真实姓名:</label>
|
||||||
|
<el-input v-model="p.real_name" placeholder="模糊查询"></el-input>
|
||||||
|
</div>
|
||||||
<div class="c-item">
|
<div class="c-item">
|
||||||
<label class="c-label">注册日期:</label>
|
<label class="c-label">注册日期:</label>
|
||||||
<el-date-picker v-model="p.start_time" type="date" value-format="yyyy-MM-dd" placeholder="开始日期"></el-date-picker> -
|
<el-date-picker v-model="p.start_time" type="date" value-format="yyyy-MM-dd" placeholder="开始日期"></el-date-picker> -
|
||||||
<el-date-picker v-model="p.end_time" type="date" value-format="yyyy-MM-dd" placeholder="结束日期"></el-date-picker>
|
<el-date-picker v-model="p.end_time" type="date" value-format="yyyy-MM-dd" placeholder="结束日期"></el-date-picker>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-item" style="min-width: 0px;">
|
<div class="c-item" style="min-width: 0px;">
|
||||||
@ -49,7 +57,7 @@
|
|||||||
<div class="fast-btn">
|
<div class="fast-btn">
|
||||||
<el-button type="primary" icon="el-icon-plus" @click="add()">新增</el-button>
|
<el-button type="primary" icon="el-icon-plus" @click="add()">新增</el-button>
|
||||||
<el-button type="success" icon="el-icon-view" @click="getBySelect()">查看</el-button>
|
<el-button type="success" icon="el-icon-view" @click="getBySelect()">查看</el-button>
|
||||||
<el-button type="danger" icon="el-icon-delete" @click="deleteByIds()">删除</el-button>
|
<el-button type="danger" icon="el-icon-delete" @click="deleteByIds()">禁用</el-button>
|
||||||
<el-button type="warning" icon="el-icon-download" @click="sa.exportExcel()">导出</el-button>
|
<el-button type="warning" icon="el-icon-download" @click="sa.exportExcel()">导出</el-button>
|
||||||
<el-button type="info" icon="el-icon-refresh" @click="sa.f5()">重置</el-button>
|
<el-button type="info" icon="el-icon-refresh" @click="sa.f5()">重置</el-button>
|
||||||
</div>
|
</div>
|
||||||
@ -57,36 +65,31 @@
|
|||||||
<el-table class="data-table" ref="data-table" :data="dataList" size="small">
|
<el-table class="data-table" ref="data-table" :data="dataList" size="small">
|
||||||
<el-table-column type="selection" width="45px"></el-table-column>
|
<el-table-column type="selection" width="45px"></el-table-column>
|
||||||
<el-table-column label="编号" prop="id" width="80px" > </el-table-column>
|
<el-table-column label="编号" prop="id" width="80px" > </el-table-column>
|
||||||
<el-table-column label="昵称" prop="username" width="220px">
|
<el-table-column label="用户名" prop="username" width="220px">
|
||||||
<template slot-scope="s">
|
<template slot-scope="s">
|
||||||
<img :src="s.row.avatar" @click="sa.showImage(s.row.avatar, '400px', '400px')"
|
<img :src="sa.cfg.api_url + s.row.avatar" @click="sa.showImage(sa.cfg.api_url + s.row.avatar, '400px', '400px')"
|
||||||
style="width: 3em; height: 3em; float: left; margin-right: 1em; border-radius: 50%; cursor: pointer;" >
|
style="width: 3em; height: 3em; float: left; margin-right: 1em; border-radius: 50%; cursor: pointer;" >
|
||||||
<div style="float: left; width: 130px; line-height: 20px;">
|
<div style="float: left; width: 130px; line-height: 20px;">
|
||||||
<b>{{s.row.username}}</b>
|
<b>{{s.row.username}} / {{s.row.nickname}}</b>
|
||||||
<p>{{s.row.tell}}</p>
|
<p>{{s.row.student_id}} / {{s.row.phone}}</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="个人相册">
|
<el-table-column label="真实姓名" prop="real_name"> </el-table-column>
|
||||||
<template slot-scope="s">
|
<el-table-column label="邮箱" prop="email"> </el-table-column>
|
||||||
<img :src="s.row.photo_list[0]" style="width: 40px; height: 40px; cursor: pointer;" @click="sa.showImageList(s.row.photo_list)" >
|
|
||||||
共{{s.row.photo_list.length}}张, 点击预览
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="性别" prop="sex"> </el-table-column>
|
<el-table-column label="性别" prop="sex"> </el-table-column>
|
||||||
<el-table-column label="注册方式" prop="create_type"></el-table-column>
|
|
||||||
<el-table-column label="注册于" prop="create_time"></el-table-column>
|
<el-table-column label="注册于" prop="create_time"></el-table-column>
|
||||||
<el-table-column label="状态">
|
<el-table-column label="状态">
|
||||||
<template slot-scope="s">
|
<template slot-scope="s">
|
||||||
<el-switch v-model="s.row.status" :active-value="1" :inactive-value="2" inactive-color="#ff4949"></el-switch>
|
<el-switch v-model="s.row.status" :active-value="1" :inactive-value="0" inactive-color="#ff4949"></el-switch>
|
||||||
<b style="color: green;" v-if="s.row.status == 1">正常</b>
|
<b style="color: green;" v-if="s.row.status === 1">正常</b>
|
||||||
<b style="color: red;" v-if="s.row.status == 2">禁用</b>
|
<b style="color: red;" v-if="s.row.status === 0">禁用</b>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="address" label="操作">
|
<el-table-column prop="address" label="操作">
|
||||||
<template slot-scope="s">
|
<template slot-scope="s">
|
||||||
<el-button class="c-btn" type="success" icon="el-icon-view" @click="get(s.row)">详情</el-button>
|
<el-button class="c-btn" type="success" icon="el-icon-view" @click="get(s.row)">详情</el-button>
|
||||||
<el-button class="c-btn" type="danger" icon="el-icon-delete" @click="del(s.row)">删除</el-button>
|
<el-button class="c-btn" type="danger" icon="el-icon-delete" @click="del(s.row)">禁用</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@ -117,33 +120,42 @@
|
|||||||
data: {
|
data: {
|
||||||
p: { // 查询参数
|
p: { // 查询参数
|
||||||
username: '',
|
username: '',
|
||||||
|
nickname: '',
|
||||||
|
real_name: '',
|
||||||
create_type: 0,
|
create_type: 0,
|
||||||
sortType: 1,
|
sortType: 1,
|
||||||
start_time: new Date().getFullYear() + '-' + (new Date().getMonth() + 1) + '-1', // 本月一号
|
start_time: new Date().getFullYear() + '-' + (new Date().getMonth() + 1) + '-1', // 本月一号
|
||||||
end_time: new Date().getFullYear() + '-' + (new Date().getMonth() + 1) + '-' + new Date().getDate(), // 本月当日
|
end_time: new Date().getFullYear() + '-' + (new Date().getMonth() + 1) + '-' + new Date().getDate(), // 本月当日
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
},
|
},
|
||||||
dataCount: 1422,
|
dataCount: 0,
|
||||||
dataList: []
|
dataList: []
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 数据刷新
|
// 数据刷新
|
||||||
f5: function() {
|
f5: function() {
|
||||||
sa.ajax2('/user/getList', this.p, function(res){
|
sa.ajax('/user/admin/list', this.p, function(res){
|
||||||
this.dataList = res.data; // 数据
|
this.dataList = res.data.data; // 数据
|
||||||
this.dataCount = res.dataCount; // 分页
|
for (let i = 0; i < this.dataList.length; i++) {
|
||||||
|
this.dataList[i].status = this.dataList[i].is_active ? 1 : 0;
|
||||||
|
}
|
||||||
|
this.dataCount = res.data.count; // 分页
|
||||||
sa.f5TableHeight(); // 刷新表格高度
|
sa.f5TableHeight(); // 刷新表格高度
|
||||||
}.bind(this), {res: mockData});
|
}.bind(this), {});
|
||||||
},
|
},
|
||||||
// 查看
|
// 查看
|
||||||
get: function(data) {
|
get: function(data) {
|
||||||
var str = '<div>';
|
var str = '<div>';
|
||||||
str += '<p>编号:' + data.id + '</p>';
|
str += '<p>编号:' + data.id + '</p>';
|
||||||
str += '<p>昵称:' + data.username + '</p>';
|
str += '<p>用户名:' + data.username + '</p>';
|
||||||
|
str += '<p>昵称:' + data.nickname + '</p>';
|
||||||
|
str += '<p>真实姓名:' + data.real_name + '</p>';
|
||||||
|
str += '<p>学号:' + data.student_id + '</p>';
|
||||||
|
str += '<p>电话:' + data.phone + '</p>';
|
||||||
|
str += '<p>邮箱:' + data.email + '</p>';
|
||||||
str += '<p>性别:' + data.sex + '</p>';
|
str += '<p>性别:' + data.sex + '</p>';
|
||||||
str += '<p>当前状态:<b>' + (data.status == 1 ? '正常' : '禁用') + '</b></p>';
|
str += '<p>当前状态:<b>' + (data.status == 1 ? '正常' : '禁用') + '</b></p>';
|
||||||
str += '<p>注册方式:' + data.create_type + '</p>';
|
|
||||||
str += '<p>注册时间:' + data.create_time + '</p>';
|
str += '<p>注册时间:' + data.create_time + '</p>';
|
||||||
str += '</div>';
|
str += '</div>';
|
||||||
sa.alert(str);
|
sa.alert(str);
|
||||||
|
46
src/route/users_admin.py
Normal file
46
src/route/users_admin.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
from fastapi import HTTPException
|
||||||
|
from fastapi_amis_admin.crud import BaseApiOut
|
||||||
|
from starlette import status
|
||||||
|
|
||||||
|
from src.plugin import handler
|
||||||
|
from src.plugin.plugin import Plugin
|
||||||
|
from src.services.users.schemas import UserList
|
||||||
|
from src.services.users.services import UserServices
|
||||||
|
|
||||||
|
|
||||||
|
class UserRoutes(Plugin):
|
||||||
|
_prefix = "/user/admin"
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
user_services: UserServices,
|
||||||
|
):
|
||||||
|
self.user_services = user_services
|
||||||
|
|
||||||
|
@handler.post("/list", student=True)
|
||||||
|
async def get_user_list(self, data: UserList):
|
||||||
|
username, nickname, real_name = data.username, data.nickname, data.real_name
|
||||||
|
start_time, end_time = data.start, data.end
|
||||||
|
page_no, page_size = data.pageNo, data.pageSize
|
||||||
|
if page_no < 1:
|
||||||
|
page_no = 1
|
||||||
|
if page_size < 0 or page_size > 100:
|
||||||
|
page_size = 10
|
||||||
|
try:
|
||||||
|
data, count = await self.user_services.get_user_list(
|
||||||
|
username,
|
||||||
|
nickname,
|
||||||
|
real_name,
|
||||||
|
start_time,
|
||||||
|
end_time,
|
||||||
|
page_no,
|
||||||
|
page_size,
|
||||||
|
)
|
||||||
|
return BaseApiOut(
|
||||||
|
code=0, msg="请求成功", data={"data": data, "count": count}
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail=f"Error Execute SQL:{e}",
|
||||||
|
) from e
|
@ -1,11 +1,13 @@
|
|||||||
from typing import Optional, Sequence
|
from datetime import datetime
|
||||||
|
from typing import Optional, Sequence, Tuple
|
||||||
|
|
||||||
from fastapi_user_auth.auth import Auth
|
from fastapi_user_auth.auth import Auth
|
||||||
from fastapi_user_auth.auth.backends.redis import RedisTokenStore
|
from fastapi_user_auth.auth.backends.redis import RedisTokenStore
|
||||||
from fastapi_user_auth.auth.models import CasbinRule, LoginHistory
|
from fastapi_user_auth.auth.models import CasbinRule, LoginHistory
|
||||||
from persica.factory.component import AsyncInitializingComponent
|
from persica.factory.component import AsyncInitializingComponent
|
||||||
from pydantic import SecretStr
|
from pydantic import SecretStr
|
||||||
from sqlmodel import select
|
from sqlalchemy import func
|
||||||
|
from sqlmodel import select, col
|
||||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||||
|
|
||||||
from src.core.database import Database
|
from src.core.database import Database
|
||||||
@ -133,3 +135,49 @@ class UserRepo(AsyncInitializingComponent):
|
|||||||
await session.commit()
|
await session.commit()
|
||||||
await session.refresh(user)
|
await session.refresh(user)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def get_count(session, q) -> int:
|
||||||
|
count_q = (
|
||||||
|
q.with_only_columns(func.count())
|
||||||
|
.order_by(None)
|
||||||
|
.select_from(q.get_final_froms()[0])
|
||||||
|
)
|
||||||
|
iterator = await session.exec(count_q)
|
||||||
|
for count in iterator:
|
||||||
|
return count
|
||||||
|
return 0
|
||||||
|
|
||||||
|
async def get_user_list(
|
||||||
|
self,
|
||||||
|
username: str,
|
||||||
|
nickname: str,
|
||||||
|
real_name: str,
|
||||||
|
start_time: datetime,
|
||||||
|
end_time: datetime,
|
||||||
|
page_no: int,
|
||||||
|
page_size: int,
|
||||||
|
) -> Tuple[Sequence[UserModel], int]:
|
||||||
|
async with AsyncSession(self.engine) as session:
|
||||||
|
statement = select(self.user_model)
|
||||||
|
if username:
|
||||||
|
statement = statement.where(
|
||||||
|
col(self.user_model.username).like(f"%{username}%")
|
||||||
|
)
|
||||||
|
if nickname:
|
||||||
|
statement = statement.where(
|
||||||
|
col(self.user_model.nickname).like(f"%{nickname}%")
|
||||||
|
)
|
||||||
|
if real_name:
|
||||||
|
statement = statement.where(
|
||||||
|
col(self.user_model.real_name).like(f"%{real_name}%")
|
||||||
|
)
|
||||||
|
if start_time:
|
||||||
|
statement = statement.where(self.user_model.create_time >= start_time)
|
||||||
|
if end_time:
|
||||||
|
statement = statement.where(self.user_model.create_time <= end_time)
|
||||||
|
all_count = await self.get_count(session, statement)
|
||||||
|
offset = (page_no - 1) * page_size
|
||||||
|
statement = statement.offset(offset).limit(page_size)
|
||||||
|
r = await session.exec(statement)
|
||||||
|
return r.all(), all_count
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from datetime import date, datetime
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
@ -107,3 +108,27 @@ class UserRoleEnum(str, Enum):
|
|||||||
ADMIN = "admin" # 管理员
|
ADMIN = "admin" # 管理员
|
||||||
STUDENT = "student" # 教职工
|
STUDENT = "student" # 教职工
|
||||||
OUT = "out" # 校外人员
|
OUT = "out" # 校外人员
|
||||||
|
|
||||||
|
|
||||||
|
class UserList(BaseModel):
|
||||||
|
username: str = ""
|
||||||
|
nickname: str = ""
|
||||||
|
real_name: str = ""
|
||||||
|
|
||||||
|
sortType: int = 1
|
||||||
|
start_time: str
|
||||||
|
end_time: str
|
||||||
|
pageNo: int = 1
|
||||||
|
pageSize: int = 10
|
||||||
|
|
||||||
|
@property
|
||||||
|
def start(self):
|
||||||
|
y, m, d = self.start_time.split("-")
|
||||||
|
start_time = date(int(y), int(m), int(d))
|
||||||
|
return datetime.combine(start_time, datetime.min.time())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def end(self):
|
||||||
|
y, m, d = self.end_time.split("-")
|
||||||
|
end_time = date(int(y), int(m), int(d))
|
||||||
|
return datetime.combine(end_time, datetime.max.time())
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from typing import Optional, List, Union, Sequence
|
from datetime import datetime
|
||||||
|
from typing import Optional, List, Union, Sequence, Tuple
|
||||||
|
|
||||||
from fastapi_user_auth.auth.models import CasbinRule, LoginHistory
|
from fastapi_user_auth.auth.models import CasbinRule, LoginHistory
|
||||||
from fastapi_user_auth.utils.casbin import update_subject_roles
|
from fastapi_user_auth.utils.casbin import update_subject_roles
|
||||||
@ -96,6 +97,20 @@ class UserServices(AsyncInitializingComponent):
|
|||||||
user.avatar = avatar
|
user.avatar = avatar
|
||||||
return await self.repo.update_user(user)
|
return await self.repo.update_user(user)
|
||||||
|
|
||||||
|
async def get_user_list(
|
||||||
|
self,
|
||||||
|
username: str,
|
||||||
|
nickname: str,
|
||||||
|
real_name: str,
|
||||||
|
start_time: datetime,
|
||||||
|
end_time: datetime,
|
||||||
|
page_no: int,
|
||||||
|
page_size: int,
|
||||||
|
) -> Tuple[Sequence[UserModel], int]:
|
||||||
|
return await self.repo.get_user_list(
|
||||||
|
username, nickname, real_name, start_time, end_time, page_no, page_size
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class UserRoleServices(AsyncInitializingComponent):
|
class UserRoleServices(AsyncInitializingComponent):
|
||||||
__order__ = 1
|
__order__ = 1
|
||||||
|
Loading…
Reference in New Issue
Block a user