mirror of https://github.com/sunface/rust-course
parent
39b01f4cca
commit
5084bcb80e
@ -0,0 +1,160 @@
|
|||||||
|
import { Text, Box, Heading, Image, Center, Button, Flex, VStack, Divider, useToast, Table, Thead, Tr, Th, Tbody, Td, IconButton, Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, useDisclosure, FormControl, FormLabel, Input, FormErrorMessage, Select} from "@chakra-ui/react"
|
||||||
|
import Card from "components/card"
|
||||||
|
import Sidebar from "layouts/sidebar/sidebar"
|
||||||
|
import React, { useEffect, useState } from "react"
|
||||||
|
import { adminLinks } from "src/data/links"
|
||||||
|
import { requestApi } from "utils/axios/request"
|
||||||
|
import TagCard from "components/tags/tag-card"
|
||||||
|
import { useRouter } from "next/router"
|
||||||
|
import Link from "next/link"
|
||||||
|
import { ReserveUrls } from "src/data/reserve-urls"
|
||||||
|
import { Tag } from "src/types/tag"
|
||||||
|
import { route } from "next/dist/next-server/server/router"
|
||||||
|
import PageContainer1 from "layouts/page-container1"
|
||||||
|
import Empty from "components/empty"
|
||||||
|
import { User } from "src/types/user"
|
||||||
|
import moment from 'moment'
|
||||||
|
import { getSvgIcon } from "components/svg-icon"
|
||||||
|
import { Field, Form, Formik } from "formik"
|
||||||
|
import { validateEmail, validateNickname, validateUsername } from "utils/user"
|
||||||
|
import { Role } from "src/types/role"
|
||||||
|
|
||||||
|
const PostsPage = () => {
|
||||||
|
const { isOpen, onOpen, onClose } = useDisclosure()
|
||||||
|
const [currentUser,setCurrentUser]:[User,any] = useState(null)
|
||||||
|
const [users, setUsers]: [User[], any] = useState([])
|
||||||
|
const router = useRouter()
|
||||||
|
const toast = useToast()
|
||||||
|
const getUsers = async () => {
|
||||||
|
const res = await requestApi.get(`/admin/user/all`)
|
||||||
|
setUsers(res.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getUsers()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const onEditUser = user => {
|
||||||
|
if (!user) {
|
||||||
|
// add user
|
||||||
|
setCurrentUser({role:Role.NORMAL})
|
||||||
|
} else {
|
||||||
|
// edit user
|
||||||
|
setCurrentUser(user)
|
||||||
|
}
|
||||||
|
onOpen()
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitUser = async values => {
|
||||||
|
await requestApi.post("/admin/user",values)
|
||||||
|
getUsers()
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PageContainer1>
|
||||||
|
<Box display="flex">
|
||||||
|
<Sidebar routes={adminLinks} title="管理员" />
|
||||||
|
<Card ml="4" p="6" width="100%">
|
||||||
|
<Flex alignItems="center" justify="space-between">
|
||||||
|
<Heading size="md">用户列表({users.length})</Heading>
|
||||||
|
<Button colorScheme="teal" size="sm" _focus={null} onClick={() => onEditUser(null)}>添加用户</Button>
|
||||||
|
</Flex>
|
||||||
|
<Table variant="simple" mt="4">
|
||||||
|
<Thead>
|
||||||
|
<Tr>
|
||||||
|
<Th>用户名</Th>
|
||||||
|
<Th>邮箱</Th>
|
||||||
|
<Th>角色</Th>
|
||||||
|
<Th>加入时间</Th>
|
||||||
|
<Th></Th>
|
||||||
|
</Tr>
|
||||||
|
</Thead>
|
||||||
|
<Tbody>
|
||||||
|
{
|
||||||
|
users.map((user, i) => <Tr key={i}>
|
||||||
|
<Td>{user.username}</Td>
|
||||||
|
<Td>{user.email}</Td>
|
||||||
|
<Td>{user.role}</Td>
|
||||||
|
<Td>{moment(user.created).fromNow()}</Td>
|
||||||
|
<Td>
|
||||||
|
<IconButton aria-label="edit navbar" variant="ghost" icon={getSvgIcon('edit', ".95rem")} onClick={() => onEditUser(user)} />
|
||||||
|
{/* <IconButton aria-label="delete navbar" variant="ghost" icon={getSvgIcon('close', "1rem")} onClick={() => onDeleteUser(user)} /> */}
|
||||||
|
</Td>
|
||||||
|
</Tr>)
|
||||||
|
}
|
||||||
|
|
||||||
|
</Tbody>
|
||||||
|
</Table>
|
||||||
|
</Card>
|
||||||
|
</Box>
|
||||||
|
</PageContainer1>
|
||||||
|
|
||||||
|
<Modal isOpen={isOpen} onClose={onClose}>
|
||||||
|
<ModalOverlay />
|
||||||
|
{currentUser && <ModalContent>
|
||||||
|
<ModalHeader>{currentUser.id ? '编辑用户' : '新建用户'}</ModalHeader>
|
||||||
|
<ModalBody mb="2">
|
||||||
|
<Formik
|
||||||
|
initialValues={currentUser}
|
||||||
|
onSubmit={submitUser}
|
||||||
|
>
|
||||||
|
{(props) => (
|
||||||
|
<Form>
|
||||||
|
<VStack>
|
||||||
|
<Field name="username" validate={currentUser.id !== undefined ? null : validateUsername}>
|
||||||
|
{({ field, form }) => (
|
||||||
|
<FormControl isInvalid={form.errors.username && form.touched.username} >
|
||||||
|
<FormLabel>Username</FormLabel>
|
||||||
|
<Input {...field} placeholder="name" disabled={currentUser.id !== undefined}/>
|
||||||
|
<FormErrorMessage>{form.errors.username}</FormErrorMessage>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
<Field name="email" validate={currentUser.id !== undefined ? null :validateEmail}>
|
||||||
|
{({ field, form }) => (
|
||||||
|
<FormControl isInvalid={form.errors.email && form.touched.email} >
|
||||||
|
<FormLabel>邮箱地址</FormLabel>
|
||||||
|
<Input {...field} placeholder="" size="lg" disabled={currentUser.id !== undefined}/>
|
||||||
|
<FormErrorMessage>{form.errors.email}</FormErrorMessage>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
<Field name="role">
|
||||||
|
{({ field, form }) => (
|
||||||
|
<FormControl isInvalid={form.errors.role && form.touched.role} >
|
||||||
|
<FormLabel>Role</FormLabel>
|
||||||
|
<Select value={currentUser.role} {...field}>
|
||||||
|
<option value={Role.NORMAL}>{Role.NORMAL}</option>
|
||||||
|
<option value={Role.EDITOR}>{Role.EDITOR}</option>
|
||||||
|
<option value={Role.ADMIN}>{Role.ADMIN}</option>
|
||||||
|
</Select>
|
||||||
|
<FormErrorMessage>{form.errors.role}</FormErrorMessage>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
</VStack>
|
||||||
|
<Box mt={6}>
|
||||||
|
<Button
|
||||||
|
colorScheme="teal"
|
||||||
|
variant="outline"
|
||||||
|
type="submit"
|
||||||
|
_focus={null}
|
||||||
|
>
|
||||||
|
提交
|
||||||
|
</Button>
|
||||||
|
<Button variant="ghost" ml="4" _focus={null} onClick={onClose}>取消</Button>
|
||||||
|
</Box>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
</ModalBody>
|
||||||
|
</ModalContent>}
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default PostsPage
|
||||||
|
|
@ -0,0 +1,27 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
|
||||||
|
"github.com/imdotdev/im.dev/server/pkg/db"
|
||||||
|
"github.com/imdotdev/im.dev/server/pkg/e"
|
||||||
|
"github.com/imdotdev/im.dev/server/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetUsers() ([]*models.User, *e.Error) {
|
||||||
|
users := make([]*models.User, 0)
|
||||||
|
rows, err := db.Conn.Query("SELECT id,username,email,role,created FROM user WHERE type=?", models.IDTypeUser)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("get users error", "error", err)
|
||||||
|
return nil, e.New(http.StatusInternalServerError, e.Internal)
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
user := &models.User{}
|
||||||
|
rows.Scan(&user.ID, &user.Username, &user.Email, &user.Role, &user.Created)
|
||||||
|
users = append(users, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/asaskevich/govalidator"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/imdotdev/im.dev/server/internal/admin"
|
||||||
|
"github.com/imdotdev/im.dev/server/internal/user"
|
||||||
|
"github.com/imdotdev/im.dev/server/pkg/common"
|
||||||
|
"github.com/imdotdev/im.dev/server/pkg/e"
|
||||||
|
"github.com/imdotdev/im.dev/server/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AdminSubmitUser(c *gin.Context) {
|
||||||
|
currentUser := user.CurrentUser(c)
|
||||||
|
if !currentUser.Role.IsAdmin() {
|
||||||
|
c.JSON(http.StatusForbidden, common.RespError(e.NoPermission))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
u := &models.User{}
|
||||||
|
c.Bind(&u)
|
||||||
|
|
||||||
|
if u.Username == "" || u.Email == "" || !govalidator.IsEmail(u.Email) || !u.Role.IsValid() {
|
||||||
|
c.JSON(http.StatusBadRequest, common.RespError(e.ParamInvalid))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := user.SubmitUser(u)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(err.Status, common.RespError(err.Message))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, common.RespSuccess(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func AdminGetUsers(c *gin.Context) {
|
||||||
|
currentUser := user.CurrentUser(c)
|
||||||
|
if !currentUser.Role.IsAdmin() {
|
||||||
|
c.JSON(http.StatusForbidden, common.RespError(e.NoPermission))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
users, err := admin.GetUsers()
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(err.Status, common.RespError(err.Message))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, common.RespSuccess(users))
|
||||||
|
}
|
Loading…
Reference in new issue