diff --git a/go.mod b/go.mod index 2df8ce07..53468c09 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/ramya-rao-a/go-outline v0.0.0-20200117021646-2a048b4510eb // indirect github.com/rogpeppe/godef v1.1.2 // indirect github.com/spf13/cobra v1.1.1 + github.com/spf13/pflag v1.0.5 github.com/stamblerre/gocode v1.0.0 // indirect github.com/uudashr/gopkgs v1.3.2 // indirect golang.org/x/mod v0.4.1 // indirect diff --git a/layouts/editor-nav.tsx b/layouts/nav/editor-nav.tsx similarity index 99% rename from layouts/editor-nav.tsx rename to layouts/nav/editor-nav.tsx index 6732487c..b28f580a 100644 --- a/layouts/editor-nav.tsx +++ b/layouts/nav/editor-nav.tsx @@ -13,7 +13,6 @@ import { useDisclosure, DrawerOverlay, DrawerContent, - Text, Divider, Heading } from "@chakra-ui/react" @@ -138,7 +137,7 @@ function EditorNav(props) { pos="fixed" top="0" zIndex="3" - bg={bg} + // bg={bg} left="0" right="0" borderTop="4px solid" diff --git a/layouts/nav/nav-links.ts b/layouts/nav/nav-links.ts deleted file mode 100644 index 9af79cfa..00000000 --- a/layouts/nav/nav-links.ts +++ /dev/null @@ -1,15 +0,0 @@ -const navLinks = [{ - title: '主页', - url: '/', -}, -{ - title: '标签', - url: '/tags', -}, -{ - title: '学习资料', - url: '/courses', -}, -] - -export default navLinks \ No newline at end of file diff --git a/layouts/nav/nav.tsx b/layouts/nav/nav.tsx index 6c8606fa..b296a390 100644 --- a/layouts/nav/nav.tsx +++ b/layouts/nav/nav.tsx @@ -4,7 +4,6 @@ import { Button, HStack, IconButton, - Link, useColorMode, useColorModeValue, useDisclosure, @@ -15,44 +14,38 @@ import { MenuItem, MenuDivider, Image, + Box } from "@chakra-ui/react" import siteConfig from "configs/site-config" import { useViewportScroll } from "framer-motion" import NextLink from "next/link" import React from "react" -import { FaMoon, FaSun, FaUserAlt, FaRegSun, FaSignOutAlt, FaRegBookmark, FaChartBar, FaHome, FaArrowRight, FaGithub, FaFileAlt, FaBookmark, FaEdit } from "react-icons/fa" +import { FaMoon, FaSun, FaUserAlt, FaRegSun, FaSignOutAlt,FaStar, FaGithub, FaBookmark, FaEdit } from "react-icons/fa" import Logo, { LogoIcon } from "src/components/logo" import { MobileNavButton, MobileNavContent } from "../mobile-nav" import AlgoliaSearch from "src/components/search/algolia-search" import useSession from "hooks/use-session" -import { requestApi } from "utils/axios/request" -import { removeToken, saveToken } from "utils/axios/getToken" import { Session } from "src/types/session" -import navLinks from "./nav-links" import { useRouter } from "next/router" -import events from "utils/events" import storage from "utils/localStorage" import { logout } from "utils/session" - - -const DiscordIcon = (props) => ( - - - -) - -const GithubIcon = (props) => ( - - - -) +import { isAdmin, isEditor } from "utils/role" +import { ReserveUrls } from "src/data/reserve-urls" +import Link from "next/link" + +const navLinks = [{ + title: '主页', + url: '/', +}, +{ + title: '标签', + url: ReserveUrls.Tags, +}, +{ + title: '学习资料', + url: ReserveUrls.Courses, +}, +] function HeaderContent() { @@ -74,7 +67,7 @@ function HeaderContent() { const login = () => { console.log(router) storage.set("current-page", asPath) - router.push('/login') + router.push(ReserveUrls.Login) } return ( @@ -93,7 +86,7 @@ function HeaderContent() { - {navLinks.map(link => {link.title})} + {navLinks.map(link => {link.title})} @@ -106,7 +99,6 @@ function HeaderContent() { @@ -155,7 +147,8 @@ function HeaderContent() { Sunface - {} href="/editor">创作中心} + {isEditor(session.user.role) && } >创作中心} + {isAdmin(session.user.role) && } >管理员} }>书签收藏 }>偏好设置 @@ -186,7 +179,6 @@ function HeaderContent() { } function Header(props) { - const bg = useColorModeValue("white", "gray.800") const ref = React.useRef() const [y, setY] = React.useState(0) const { height = 0 } = ref.current?.getBoundingClientRect() ?? {} @@ -204,7 +196,6 @@ function Header(props) { pos="fixed" top="0" zIndex="3" - bg={bg} left="0" right="0" borderTop="4px solid" diff --git a/next-redirect.js b/next-redirect.js index c7ce7514..f8dd012a 100644 --- a/next-redirect.js +++ b/next-redirect.js @@ -1,16 +1,10 @@ async function redirect() { return [ - { - source: "/discord", - destination: "https://discord.gg/dQHfcWF", - permanent: true, - }, - // GENERAL - { - source: "/editor", - destination: "/editor/posts", - permanent: true, - } + // { + // source: "/discord", + // destination: "https://discord.gg/dQHfcWF", + // permanent: true, + // } ] } diff --git a/pages/admin/tag/[id].tsx b/pages/admin/tag/[id].tsx new file mode 100644 index 00000000..4b6dd92b --- /dev/null +++ b/pages/admin/tag/[id].tsx @@ -0,0 +1,207 @@ +import { Box, Button, Flex, useColorMode, useColorModeValue, useDisclosure, useRadioGroup, useToast, chakra, Input, HStack, IconButton, Heading, Divider } from '@chakra-ui/react'; +import React, { useEffect, useState } from 'react'; +import { MarkdownEditor } from 'components/markdown-editor/editor'; +import PageContainer from 'layouts/page-container'; +import { EditMode } from 'src/types/editor'; +import { MarkdownRender } from 'components/markdown-editor/render'; +import { requestApi } from 'utils/axios/request'; +import { useRouter } from 'next/router'; +import { config } from 'utils/config'; +import { cloneDeep } from 'lodash'; +import { FaMoon, FaSun } from 'react-icons/fa'; +import Link from 'next/link'; +import NextLink from "next/link" +import Logo, { LogoIcon } from 'components/logo'; +import RadioCard from 'components/radio-card'; +import { useViewportScroll } from 'framer-motion'; +import Card from 'components/card'; +import { Tag } from 'src/types/tag'; + + +function PostEditPage() { + const router = useRouter() + const { id } = router.query + const [editMode, setEditMode] = useState(EditMode.Edit) + const [tag, setTag]:[Tag,any] = useState({ + md: `标签介绍,支持markdown`, + title: '' + }) + + const toast = useToast() + useEffect(() => { + if (id && id !== 'new') { + requestApi.get(`/tag/${id}`).then(res => setTag(res.data)) + } + }, [id]) + + const onMdChange = newMd => { + setTag({ + ...tag, + md: newMd + }) + } + + const onChange = () => { + setTag(cloneDeep(tag)) + } + + + const publish = async () => { + const res = await requestApi.post(`/admin/tag`, tag) + toast({ + description: "发布成功", + status: "success", + duration: 2000, + isClosable: true, + }) + router.push(`/tags/${tag.name}`) + } + + return ( + setEditMode(v)} + publish={() => publish()} + />} + > + + + {editMode === EditMode.Edit ? + onMdChange(md)} + md={tag.md} + /> : + + + + } + + + + Title + + { tag.title = e.target.value; onChange()}} mt="4" variant="flushed" size="sm" placeholder="Tag title..." focusBorderColor="teal.400" /> + + + Name + + { tag.name = e.target.value; onChange()}} mt="4" variant="flushed" size="sm" placeholder="Tag name..." focusBorderColor="teal.400" /> + + + 封面 + + { tag.cover = e.target.value; onChange()}} mt="4" variant="flushed" size="sm" placeholder="图片链接,你可以用github当图片存储服务" focusBorderColor="teal.400" /> + + + 图标 + + { tag.icon = e.target.value; onChange()}} mt="4" variant="flushed" size="sm" placeholder="图片链接" focusBorderColor="teal.400" /> + + + + ); +} + +export default PostEditPage + +function HeaderContent(props: any) { + const { toggleColorMode: toggleMode } = useColorMode() + const text = useColorModeValue("dark", "light") + const SwitchIcon = useColorModeValue(FaMoon, FaSun) + + const editOptions = [EditMode.Edit, EditMode.Preview] + const { getRootProps, getRadioProps } = useRadioGroup({ + name: "framework", + defaultValue: EditMode.Edit, + onChange: (v) => { + props.changeEditMode(v) + }, + }) + const group = getRootProps() + + return ( + <> + + + + + + + + + + + + + + + + {editOptions.map((value) => { + const radio = getRadioProps({ value }) + return ( + + {value} + + ) + })} + + + } + /> + + + + + ) +} + +function Nav(props) { + const bg = useColorModeValue("white", "gray.800") + const ref = React.useRef() + const [y, setY] = React.useState(0) + const { height = 0 } = ref.current?.getBoundingClientRect() ?? {} + + const { scrollY } = useViewportScroll() + React.useEffect(() => { + return scrollY.onChange(() => setY(scrollY.get())) + }, [scrollY]) + + return ( + height ? "sm" : undefined} + transition="box-shadow 0.2s" + pos="fixed" + top="0" + zIndex="3" + bg={bg} + left="0" + right="0" + borderTop="4px solid" + borderTopColor="teal.400" + width="full" + > + + + + + ) +} diff --git a/pages/admin/tags.tsx b/pages/admin/tags.tsx new file mode 100644 index 00000000..bc8942f3 --- /dev/null +++ b/pages/admin/tags.tsx @@ -0,0 +1,86 @@ +import {Text, Box, Heading, Image, Center, Button, Flex, VStack, Divider, useToast } from "@chakra-ui/react" +import Card from "components/card" +import Nav from "layouts/nav/nav" +import PageContainer from "layouts/page-container" +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/posts/tag-card" +import { Post } from "src/types/posts" +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" + + +const PostsPage = () => { + const [tags, setTags] = useState([]) + const router = useRouter() + const toast = useToast() + const getTags = () => { + requestApi.get(`/tags`).then((res) => setTags(res.data)).catch(_ => setTags([])) + } + + useEffect(() => { + getTags() + }, []) + + const editTag = (tag: Tag) => { + router.push(`${ReserveUrls.Admin}/tag/${tag.name}`) + } + + const deleteTag= async (id) => { + await requestApi.delete(`/admin/tag/${id}`) + getTags() + toast({ + description: "删除成功", + status: "success", + duration: 2000, + isClosable: true, + }) + } + + return ( + <> +