mirror of https://github.com/sunface/rust-course
parent
0ee209f273
commit
af8c4e85bf
@ -0,0 +1,147 @@
|
||||
import { Text, Box, Heading, Image, Center, Button, Flex, VStack, Divider, useToast, FormControl, FormLabel, FormHelperText, Input, FormErrorMessage, HStack, Wrap, useMediaQuery, Avatar, Textarea, Table, Thead, Tr, Th, Tbody, Td, IconButton, useDisclosure, Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, ModalFooter, Select, NumberInput, NumberInputField, NumberInputStepper, NumberIncrementStepper, NumberDecrementStepper } from "@chakra-ui/react"
|
||||
import Card from "components/card"
|
||||
import PageContainer from "layouts/page-container"
|
||||
import Sidebar from "layouts/sidebar/sidebar"
|
||||
import React, { useEffect, useState } from "react"
|
||||
import { adminLinks, settingLinks } from "src/data/links"
|
||||
import { requestApi } from "utils/axios/request"
|
||||
import { config } from "configs/config"
|
||||
import { getSvgIcon } from "components/svg-icon"
|
||||
import { Navbar, NavbarType } from "src/types/user"
|
||||
import { cloneDeep } from "lodash"
|
||||
import { IDType } from "src/types/id"
|
||||
import { Story } from "src/types/story"
|
||||
import PageContainer1 from "layouts/page-container1"
|
||||
import { HomeSidebar } from "src/types/misc"
|
||||
import { SearchFilter } from "src/types/search"
|
||||
|
||||
const SidebarsPage = () => {
|
||||
const [sidebars, setSidebars]:[HomeSidebar[],any] = useState([])
|
||||
const [currentSidebar, setCurrentSidebar]: [HomeSidebar, any] = useState(null)
|
||||
const { isOpen, onOpen, onClose } = useDisclosure()
|
||||
const toast = useToast()
|
||||
useEffect(() => {
|
||||
getSidebars()
|
||||
}, [])
|
||||
|
||||
const getSidebars = async () => {
|
||||
const res = await requestApi.get("/sidebars")
|
||||
setSidebars(res.data)
|
||||
}
|
||||
|
||||
|
||||
const submitNavbar = async () => {
|
||||
await requestApi.get(`/tag/info/${currentSidebar.tagName}`)
|
||||
await requestApi.post(`/sidebar`, currentSidebar)
|
||||
setCurrentSidebar(null)
|
||||
onClose()
|
||||
getSidebars()
|
||||
}
|
||||
|
||||
const onAddNavbar = () => {
|
||||
setCurrentSidebar({tagName: "", sort: SearchFilter.Recent , displayCount:5, weight: 0})
|
||||
onOpen()
|
||||
}
|
||||
|
||||
const onEditNavbar = nav => {
|
||||
setCurrentSidebar(nav)
|
||||
onOpen()
|
||||
}
|
||||
|
||||
const onSidebarChange = () => {
|
||||
const nv = cloneDeep(currentSidebar)
|
||||
setCurrentSidebar(nv)
|
||||
}
|
||||
|
||||
const onDeleteNavbar = async id => {
|
||||
requestApi.delete(`/sidebar/${id}`)
|
||||
setTimeout( () => getSidebars(),300)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageContainer1>
|
||||
<Box display="flex">
|
||||
<Sidebar routes={adminLinks} width={["120px", "120px", "250px", "250px"]} height="fit-content" title="管理员" />
|
||||
<Card ml="4" width="100%">
|
||||
<Flex justifyContent="space-between" alignItems="center">
|
||||
<Heading size="sm">侧边栏设置</Heading>
|
||||
<Button colorScheme="teal" size="sm" onClick={onAddNavbar} _focus={null}>新建侧边栏</Button>
|
||||
</Flex>
|
||||
<Table variant="simple" mt="4">
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Tag Name</Th>
|
||||
<Th>Sort</Th>
|
||||
<Th>Display count</Th>
|
||||
<Th>Weight</Th>
|
||||
<Th></Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{
|
||||
sidebars.map((nv,i) => <Tr key={i}>
|
||||
<Td>{nv.tagName}</Td>
|
||||
<Td>{nv.sort}</Td>
|
||||
<Td>{nv.displayCount}</Td>
|
||||
<Td>{nv.weight}</Td>
|
||||
<Td>
|
||||
<IconButton aria-label="edit navbar" variant="ghost" icon={getSvgIcon('edit', ".95rem")} onClick={() => onEditNavbar(nv)}/>
|
||||
<IconButton aria-label="delete navbar" variant="ghost" icon={getSvgIcon('close', "1rem")} onClick={() => onDeleteNavbar(nv.id)} />
|
||||
</Td>
|
||||
</Tr>)
|
||||
}
|
||||
|
||||
</Tbody>
|
||||
</Table>
|
||||
</Card>
|
||||
</Box>
|
||||
</PageContainer1>
|
||||
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
<ModalOverlay />
|
||||
{currentSidebar && <ModalContent>
|
||||
<ModalHeader>{currentSidebar.tagName ? "编辑侧边栏" : "新建侧边栏"}</ModalHeader>
|
||||
<ModalBody mb="2">
|
||||
<VStack spacing="4" alignItems="left">
|
||||
<HStack spacing="4">
|
||||
<Heading size="xs" width="150px">Tag name</Heading>
|
||||
<Input value={currentSidebar.tagName} _focus={null} variant="flushed" onChange={e => { currentSidebar.tagName = e.currentTarget.value; onSidebarChange() }}></Input>
|
||||
</HStack>
|
||||
|
||||
{/* <HStack spacing="4">
|
||||
<Heading size="xs" width="150px">Sort</Heading>
|
||||
<Input value={currentSidebar.sort} _focus={null} variant="flushed" onChange={e => { currentSidebar.value = e.currentTarget.value; onSidebarChange() }} placeholder="enter a url, e.g /search"/>
|
||||
</HStack> */}
|
||||
|
||||
<HStack spacing="4">
|
||||
<Heading size="xs" width="105px">Display count</Heading>
|
||||
<NumberInput min={0} max={10} value={currentSidebar.displayCount} variant="flushed" onChange={e => { currentSidebar.displayCount = parseInt(e); onSidebarChange() }}>
|
||||
<NumberInputField _focus={null} />
|
||||
<NumberInputStepper>
|
||||
<NumberIncrementStepper />
|
||||
<NumberDecrementStepper />
|
||||
</NumberInputStepper>
|
||||
</NumberInput>
|
||||
</HStack>
|
||||
|
||||
<HStack spacing="4">
|
||||
<Heading size="xs" width="105px">Weight</Heading>
|
||||
<NumberInput min={0} max={10} value={currentSidebar.weight} variant="flushed" onChange={e => { currentSidebar.weight = parseInt(e); onSidebarChange() }}>
|
||||
<NumberInputField _focus={null} />
|
||||
<NumberInputStepper>
|
||||
<NumberIncrementStepper />
|
||||
<NumberDecrementStepper />
|
||||
</NumberInputStepper>
|
||||
</NumberInput>
|
||||
</HStack>
|
||||
</VStack>
|
||||
<Button colorScheme="teal" variant="outline" mt="6" onClick={submitNavbar}>提交</Button>
|
||||
</ModalBody>
|
||||
</ModalContent>}
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default SidebarsPage
|
||||
|
@ -0,0 +1,59 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/imdotdev/im.dev/server/internal/user"
|
||||
"github.com/imdotdev/im.dev/server/pkg/common"
|
||||
"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 SubmitSidebar(c *gin.Context) {
|
||||
side := &models.Sidebar{}
|
||||
c.Bind(&side)
|
||||
u := user.CurrentUser(c)
|
||||
if !u.Role.IsAdmin() {
|
||||
c.JSON(http.StatusForbidden, common.RespError(e.NoPermission))
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
if side.ID == 0 {
|
||||
_, err = db.Conn.Exec("INSERT INTO home_sidebar (tag_name,sort,display_count,weight) VALUES (?,?,?,?)",
|
||||
side.TagName, side.Sort, side.DisplayCount, side.Weight)
|
||||
} else {
|
||||
_, err = db.Conn.Exec("UPDATE home_sidebar SET tag_name=?,sort=?,display_count=?,weight=? WHERE id=?", side.TagName, side.Sort, side.DisplayCount, side.Weight, side.ID)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Warn("submit sidebar error", "error", err)
|
||||
c.JSON(http.StatusInternalServerError, common.RespError(e.Internal))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.RespSuccess(nil))
|
||||
}
|
||||
|
||||
func GetSidebars(c *gin.Context) {
|
||||
navbars := make(models.Sidebars, 0)
|
||||
|
||||
rows, err := db.Conn.Query("SELECT id,tag_name,sort,display_count,weight FROM home_sidebar")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, common.RespError(e.Internal))
|
||||
return
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
nv := &models.Sidebar{}
|
||||
rows.Scan(&nv.ID, &nv.TagName, &nv.Sort, &nv.DisplayCount, &nv.Weight)
|
||||
navbars = append(navbars, nv)
|
||||
}
|
||||
|
||||
sort.Sort(navbars)
|
||||
|
||||
c.JSON(http.StatusOK, common.RespSuccess(navbars))
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package models
|
||||
|
||||
type Sidebar struct {
|
||||
ID int `json:"id"`
|
||||
TagName string `json:"tagName"`
|
||||
Sort string `json:"sort"`
|
||||
DisplayCount int `json:"displayCount"`
|
||||
Weight int `json:"weight"`
|
||||
}
|
||||
|
||||
type Sidebars []*Sidebar
|
||||
|
||||
func (t Sidebars) Len() int { return len(t) }
|
||||
func (t Sidebars) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
|
||||
func (t Sidebars) Less(i, j int) bool {
|
||||
return t[i].Weight > t[j].Weight
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
export interface HomeSidebar {
|
||||
id?: number,
|
||||
tagName: string
|
||||
sort: string
|
||||
displayCount: number
|
||||
weight: number
|
||||
}
|
Loading…
Reference in new issue