From af8c4e85bf5df88bf39f42607541c3b7dd5c3157 Mon Sep 17 00:00:00 2001 From: sunface Date: Fri, 26 Mar 2021 17:41:45 +0800 Subject: [PATCH] update --- pages/admin/navbar.tsx | 2 +- pages/admin/sidebars.tsx | 147 +++++++++++++++++++++ pages/bookmarks.tsx | 4 +- pages/index.tsx | 74 +++++++---- server/internal/server.go | 2 + server/internal/sidebar.go | 59 +++++++++ server/internal/storage/sql_tables.go | 8 ++ server/internal/tags/tags.go | 2 +- server/pkg/models/sidebar.go | 17 +++ src/components/story/simple-story-card.tsx | 16 +-- src/data/links.tsx | 8 +- src/types/misc.ts | 7 + 12 files changed, 308 insertions(+), 38 deletions(-) create mode 100644 pages/admin/sidebars.tsx create mode 100644 server/internal/sidebar.go create mode 100644 server/pkg/models/sidebar.go create mode 100644 src/types/misc.ts diff --git a/pages/admin/navbar.tsx b/pages/admin/navbar.tsx index 90e0f586..a3298309 100644 --- a/pages/admin/navbar.tsx +++ b/pages/admin/navbar.tsx @@ -86,7 +86,7 @@ const UserNavbarPage = () => { <> - + 菜单设置 diff --git a/pages/admin/sidebars.tsx b/pages/admin/sidebars.tsx new file mode 100644 index 00000000..ebd854a4 --- /dev/null +++ b/pages/admin/sidebars.tsx @@ -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 ( + <> + + + + + + 侧边栏设置 + + + + + + + + + + + + + + { + sidebars.map((nv,i) => + + + + + + ) + } + + +
Tag NameSortDisplay countWeight
{nv.tagName}{nv.sort}{nv.displayCount}{nv.weight} + onEditNavbar(nv)}/> + onDeleteNavbar(nv.id)} /> +
+
+
+
+ + + + {currentSidebar && + {currentSidebar.tagName ? "编辑侧边栏" : "新建侧边栏"} + + + + Tag name + { currentSidebar.tagName = e.currentTarget.value; onSidebarChange() }}> + + + {/* + Sort + { currentSidebar.value = e.currentTarget.value; onSidebarChange() }} placeholder="enter a url, e.g /search"/> + */} + + + Display count + { currentSidebar.displayCount = parseInt(e); onSidebarChange() }}> + + + + + + + + + + Weight + { currentSidebar.weight = parseInt(e); onSidebarChange() }}> + + + + + + + + + + + } + + + ) +} +export default SidebarsPage + diff --git a/pages/bookmarks.tsx b/pages/bookmarks.tsx index 8ddfe6d5..3775f47c 100644 --- a/pages/bookmarks.tsx +++ b/pages/bookmarks.tsx @@ -9,7 +9,7 @@ import { import siteConfig from "configs/site-config" import PageContainer1 from "layouts/page-container1" import React, { useEffect, useState } from "react" - import { HomeSidebar } from 'pages/index' + import { IndexSidebar } from 'pages/index' import Card from "components/card" import { config } from "configs/config" import { Tag } from "src/types/tag" @@ -108,7 +108,7 @@ import StoryCard from "components/story/story-card" }
- +
diff --git a/pages/index.tsx b/pages/index.tsx index 1dcd4fe3..f6ac7358 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -19,8 +19,11 @@ import { requestApi } from "utils/axios/request" import StoryFilters from "components/story/story-filter" import { concat } from "lodash" import useInfiniteScroll from 'src/hooks/use-infinite-scroll' +import { HomeSidebar } from "src/types/misc" +import Link from "next/link" +import { ReserveUrls } from "src/data/reserve-urls" const HomePage = () => { - const [filter,setFilter] = useState('Best') + const [filter, setFilter] = useState('Best') const initData = (p) => { return requestApi.get(`/story/posts/home?filter=${filter}&page=${p}&per_page=5`) } @@ -40,7 +43,7 @@ const HomePage = () => { - + { - + - + @@ -75,35 +78,56 @@ const HomePage = () => { export default HomePage -export const HomeSidebar = () => { - const [posts, setPosts] = useState([]) - // const initData = async () => { - // const res = await requestApi.get(`/story/posts/home/aa`) - // setPosts(res.data) - // } +export const IndexSidebar = () => { + const [sidebars, setSidebars]: [HomeSidebar[], any] = useState([]) + const getSidebars = async () => { + const res = await requestApi.get("/sidebars") + setSidebars(res.data) + } - // useEffect(() => { - // initData() - // }, []) + useEffect(() => { + getSidebars() + }, []) return ( - + { + sidebars.map(sb => + + ) + } + + + ) +} + +const IndexSidebarCard = ({ sidebar }) => { + const [posts, setPosts] = useState([]) + useEffect(() => { + initData() + }, []) + + const initData = async () => { + const res = await requestApi.get(`/tag/info/${sidebar.tagName}`) + const res1 = await requestApi.get(`/tag/posts/${res.data.id}?filter=${sidebar.sort}&page=${1}&per_page=${sidebar.displayCount}`) + setPosts(res1.data) + } + + return ( + <> + {posts.length > 0 && - 热榜 - - - - - + #{sidebar.tagName} - {/* - - */} + + { + posts.map(p => ) + } + - - + } + ) } \ No newline at end of file diff --git a/server/internal/server.go b/server/internal/server.go index d1d3d0a4..cfd7ac26 100644 --- a/server/internal/server.go +++ b/server/internal/server.go @@ -142,6 +142,8 @@ func (s *Server) Start() error { r.POST("/navbar", IsLogin(), SubmitNavbar) r.DELETE("/navbar/:id", IsLogin(), DeleteNavbar) + r.GET("/sidebars", GetSidebars) + r.POST("/sidebar", IsLogin(), SubmitSidebar) err := router.Run(config.Data.Server.Addr) if err != nil { logger.Crit("start backend server error", "error", err) diff --git a/server/internal/sidebar.go b/server/internal/sidebar.go new file mode 100644 index 00000000..fc95680c --- /dev/null +++ b/server/internal/sidebar.go @@ -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)) +} diff --git a/server/internal/storage/sql_tables.go b/server/internal/storage/sql_tables.go index a278a065..ad52b2cf 100644 --- a/server/internal/storage/sql_tables.go +++ b/server/internal/storage/sql_tables.go @@ -240,4 +240,12 @@ var sqlTables = map[string]string{ weight TINYINT DEFAULT 0 ); `, + + "home_sidebar": `CREATE TABLE IF NOT EXISTS home_sidebar ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + tag_name VARCHAR(255), + sort VARCHAR(20), + display_count TINYINT, + weight TINYINT DEFAULT 0 + );`, } diff --git a/server/internal/tags/tags.go b/server/internal/tags/tags.go index a3d0dcaa..81c22a26 100644 --- a/server/internal/tags/tags.go +++ b/server/internal/tags/tags.go @@ -100,7 +100,7 @@ func GetTag(id string, name string) (*models.Tag, *e.Error) { ) if err != nil { if err == sql.ErrNoRows { - return nil, e.New(http.StatusNotFound, e.NotFound) + return nil, e.New(http.StatusNotFound, "Tag不存在") } logger.Warn("get post error", "error", err) return nil, e.New(http.StatusInternalServerError, e.Internal) diff --git a/server/pkg/models/sidebar.go b/server/pkg/models/sidebar.go new file mode 100644 index 00000000..a26bd83e --- /dev/null +++ b/server/pkg/models/sidebar.go @@ -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 +} diff --git a/src/components/story/simple-story-card.tsx b/src/components/story/simple-story-card.tsx index e0045c89..c2ee86c0 100644 --- a/src/components/story/simple-story-card.tsx +++ b/src/components/story/simple-story-card.tsx @@ -19,19 +19,19 @@ export const SimpleStoryCard = (props: Props) => { const [isLargeScreen] = useMediaQuery("(min-width: 768px)") const Layout = isLargeScreen ? HStack : VStack return ( - + {story.title} - - {story.creator.nickname} + + {/* {story.creator.nickname} */} - + {/* - + */} - - {getSvgIcon("comments1", "0.9rem")} - {story.comments} + + {story.comments} + comments diff --git a/src/data/links.tsx b/src/data/links.tsx index 1a93545e..8b0ebd1a 100644 --- a/src/data/links.tsx +++ b/src/data/links.tsx @@ -1,6 +1,6 @@ import { getSvgIcon } from 'components/svg-icon' import React from 'react' -import { FaFileAlt, FaScroll, FaBookOpen, FaTags, FaUserCircle, FaRegFile, FaUser, FaRegUser, FaUserFriends } from 'react-icons/fa' +import { FaFileAlt, FaScroll, FaBookOpen, FaTags, FaUserCircle, FaRegFile, FaUser, FaRegUser, FaUserFriends, FaElementor } from 'react-icons/fa' import { Route } from 'src/types/route' import { SearchFilter } from 'src/types/search' import { ReserveUrls } from './reserve-urls' @@ -76,6 +76,12 @@ export const adminLinks: Route[] = [{ icon: getSvgIcon("user"), disabled: false }, +{ + title: '首页侧栏', + path: `${ReserveUrls.Admin}/sidebars`, + icon: , + disabled: false +}, ] diff --git a/src/types/misc.ts b/src/types/misc.ts new file mode 100644 index 00000000..dfab1b46 --- /dev/null +++ b/src/types/misc.ts @@ -0,0 +1,7 @@ +export interface HomeSidebar { + id?: number, + tagName: string + sort: string + displayCount: number + weight: number +} \ No newline at end of file