2
0
visuddhinanda 3 жил өмнө
parent
commit
63717727af

+ 13 - 0
dashboard/src/components/article/ArticleSkeleton.tsx

@@ -0,0 +1,13 @@
+import { Divider, Skeleton } from "antd";
+
+const Widget = () => {
+  return (
+    <div style={{ paddingTop: "1em" }}>
+      <Skeleton title={{ width: 200 }} paragraph={{ rows: 1 }} active />
+      <Divider />
+      <Skeleton title={{ width: 200 }} paragraph={{ rows: 10 }} active />
+    </div>
+  );
+};
+
+export default Widget;

+ 96 - 0
dashboard/src/components/auth/Avatar.tsx

@@ -0,0 +1,96 @@
+import { useIntl } from "react-intl";
+import { useEffect, useState } from "react";
+import { Link, useNavigate } from "react-router-dom";
+import { Tooltip } from "antd";
+import { Avatar } from "antd";
+import { Popover } from "antd";
+import { ProCard } from "@ant-design/pro-components";
+import {
+  UserOutlined,
+  HomeOutlined,
+  LogoutOutlined,
+  SettingOutlined,
+} from "@ant-design/icons";
+
+import { useAppSelector } from "../../hooks";
+import { currentUser as _currentUser } from "../../reducers/current-user";
+import { TooltipPlacement } from "antd/lib/tooltip";
+
+interface IWidget {
+  placement?: TooltipPlacement;
+}
+const Widget = ({ placement = "bottomRight" }: IWidget) => {
+  // TODO
+  const intl = useIntl();
+  const navigate = useNavigate();
+  const [userName, setUserName] = useState<string>();
+  const [nickName, setNickName] = useState<string>();
+  const user = useAppSelector(_currentUser);
+  useEffect(() => {
+    setUserName(user?.realName);
+    setNickName(user?.nickName);
+  }, [user]);
+
+  const userCard = (
+    <ProCard
+      style={{ maxWidth: 500, minWidth: 300 }}
+      actions={[
+        <Tooltip
+          title={intl.formatMessage({
+            id: "buttons.setting",
+          })}
+        >
+          <SettingOutlined key="setting" />
+        </Tooltip>,
+        <Tooltip
+          title={intl.formatMessage({
+            id: "columns.library.blog.label",
+          })}
+        >
+          <Link to={`/blog/${userName}/overview`}>
+            <HomeOutlined key="home" />
+          </Link>
+        </Tooltip>,
+        <Tooltip
+          title={intl.formatMessage({
+            id: "buttons.sign-out",
+          })}
+        >
+          <LogoutOutlined
+            key="logout"
+            onClick={() => {
+              sessionStorage.removeItem("token");
+              localStorage.removeItem("token");
+              navigate("/anonymous/users/sign-in");
+            }}
+          />
+        </Tooltip>,
+      ]}
+    >
+      <div>
+        <h2>{nickName}</h2>
+        <div style={{ textAlign: "right" }}>
+          {intl.formatMessage({
+            id: "buttons.welcome",
+          })}
+        </div>
+      </div>
+    </ProCard>
+  );
+  const login = <Link to="/anonymous/users/sign-in">登录</Link>;
+  return (
+    <>
+      <Popover content={user ? userCard : login} placement={placement}>
+        <Avatar
+          style={{ backgroundColor: user ? "#87d068" : "gray" }}
+          icon={<UserOutlined />}
+          size="small"
+        >
+          {user ? nickName?.slice(0, 1) : undefined}
+        </Avatar>
+      </Popover>
+    </>
+  );
+};
+
+export default Widget;

+ 100 - 0
dashboard/src/components/corpus/ChapterChannelSelect.tsx

@@ -0,0 +1,100 @@
+import { Col, List, Modal, Progress, Row, Space, Typography } from "antd";
+
+import ChannelListItem from "../channel/ChannelListItem";
+import { IChapterChannelData } from "./ChapterInChannel";
+import { LikeOutlined, EyeOutlined } from "@ant-design/icons";
+import { useState } from "react";
+import TimeShow from "../general/TimeShow";
+
+const { Text } = Typography;
+
+/**
+ * 章节中的版本选择对话框
+ * @returns
+ */
+interface IWidget {
+  trigger?: JSX.Element | string;
+  channels?: IChapterChannelData[];
+  currChannel?: string;
+  onSelect?: Function;
+}
+const Widget = ({ trigger, channels, currChannel, onSelect }: IWidget) => {
+  const [open, setOpen] = useState(false);
+
+  const handleCancel = () => {
+    setOpen(false);
+  };
+
+  return (
+    <div>
+      <div
+        onClick={() => {
+          setOpen(true);
+        }}
+      >
+        {trigger}
+      </div>
+      <Modal
+        title="版本选择"
+        open={open}
+        onCancel={handleCancel}
+        onOk={handleCancel}
+      >
+        <List
+          style={{ maxWidth: 500 }}
+          itemLayout="vertical"
+          size="small"
+          dataSource={channels}
+          pagination={
+            currChannel
+              ? undefined
+              : {
+                  showQuickJumper: false,
+                  showSizeChanger: false,
+                  pageSize: 5,
+                  total: channels?.length,
+                  position: "bottom",
+                  showTotal: (total) => {
+                    return `结果: ${total}`;
+                  },
+                }
+          }
+          renderItem={(item, id) => (
+            <List.Item key={id}>
+              <Row>
+                <Col span={12}>
+                  <div
+                    onClick={() => {
+                      if (typeof onSelect !== "undefined") {
+                        onSelect(item.channel.id);
+                      }
+                    }}
+                  >
+                    <ChannelListItem
+                      channel={item.channel}
+                      studio={item.studio}
+                    />
+                  </div>
+                </Col>
+                <Col span={12}>
+                  <Progress percent={item.progress} size="small" />
+                </Col>
+              </Row>
+
+              <Text type="secondary">
+                <Space style={{ paddingLeft: "2em" }}>
+                  <EyeOutlined />
+                  {item.hit} | <LikeOutlined />
+                  {item.like} |
+                  <TimeShow time={item.updatedAt} title={item.updatedAt} />
+                </Space>
+              </Text>
+            </List.Item>
+          )}
+        />
+      </Modal>
+    </div>
+  );
+};
+
+export default Widget;

+ 154 - 0
dashboard/src/pages/admin/_defaultProps.tsx

@@ -0,0 +1,154 @@
+import {
+  ChromeFilled,
+  CrownFilled,
+  SmileFilled,
+  TabletFilled,
+} from "@ant-design/icons";
+
+const defaultProps = {
+  route: {
+    path: "/",
+    routes: [
+      {
+        path: "/welcome",
+        name: "欢迎",
+        icon: <SmileFilled />,
+        component: "./Welcome",
+      },
+      {
+        path: "/admin",
+        name: "管理页",
+        icon: <CrownFilled />,
+        access: "canAdmin",
+        component: "./Admin",
+        routes: [
+          {
+            path: "/admin/sub-page1",
+            name: "一级页面",
+            icon: "https://gw.alipayobjects.com/zos/antfincdn/upvrAjAPQX/Logo_Tech%252520UI.svg",
+            component: "./Welcome",
+          },
+          {
+            path: "/admin/sub-page2",
+            name: "二级页面",
+            icon: <CrownFilled />,
+            component: "./Welcome",
+          },
+          {
+            path: "/admin/sub-page3",
+            name: "三级页面",
+            icon: <CrownFilled />,
+            component: "./Welcome",
+          },
+        ],
+      },
+      {
+        name: "列表页",
+        icon: <TabletFilled />,
+        path: "/list",
+        component: "./ListTableList",
+        routes: [
+          {
+            path: "/list/sub-page",
+            name: "列表页面",
+            icon: <CrownFilled />,
+            routes: [
+              {
+                path: "sub-sub-page1",
+                name: "一一级列表页面",
+                icon: <CrownFilled />,
+                component: "./Welcome",
+              },
+              {
+                path: "sub-sub-page2",
+                name: "一二级列表页面",
+                icon: <CrownFilled />,
+                component: "./Welcome",
+              },
+              {
+                path: "sub-sub-page3",
+                name: "一三级列表页面",
+                icon: <CrownFilled />,
+                component: "./Welcome",
+              },
+            ],
+          },
+          {
+            path: "/list/sub-page2",
+            name: "二级列表页面",
+            icon: <CrownFilled />,
+            component: "./Welcome",
+          },
+          {
+            path: "/list/sub-page3",
+            name: "三级列表页面",
+            icon: <CrownFilled />,
+            component: "./Welcome",
+          },
+        ],
+      },
+      {
+        path: "https://ant.design",
+        name: "Ant Design 官网外链",
+        icon: <ChromeFilled />,
+      },
+    ],
+  },
+  location: {
+    pathname: "/",
+  },
+  appList: [
+    {
+      icon: "https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg",
+      title: "Ant Design",
+      desc: "杭州市较知名的 UI 设计语言",
+      url: "https://ant.design",
+    },
+    {
+      icon: "https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png",
+      title: "AntV",
+      desc: "蚂蚁集团全新一代数据可视化解决方案",
+      url: "https://antv.vision/",
+      target: "_blank",
+    },
+    {
+      icon: "https://gw.alipayobjects.com/zos/antfincdn/upvrAjAPQX/Logo_Tech%252520UI.svg",
+      title: "Pro Components",
+      desc: "专业级 UI 组件库",
+      url: "https://procomponents.ant.design/",
+    },
+    {
+      icon: "https://img.alicdn.com/tfs/TB1zomHwxv1gK0jSZFFXXb0sXXa-200-200.png",
+      title: "umi",
+      desc: "插件化的企业级前端应用框架。",
+      url: "https://umijs.org/zh-CN/docs",
+    },
+
+    {
+      icon: "https://gw.alipayobjects.com/zos/bmw-prod/8a74c1d3-16f3-4719-be63-15e467a68a24/km0cv8vn_w500_h500.png",
+      title: "qiankun",
+      desc: "可能是你见过最完善的微前端解决方案🧐",
+      url: "https://qiankun.umijs.org/",
+    },
+    {
+      icon: "https://gw.alipayobjects.com/zos/rmsportal/XuVpGqBFxXplzvLjJBZB.svg",
+      title: "语雀",
+      desc: "知识创作与分享工具",
+      url: "https://www.yuque.com/",
+    },
+    {
+      icon: "https://gw.alipayobjects.com/zos/rmsportal/LFooOLwmxGLsltmUjTAP.svg",
+      title: "Kitchen ",
+      desc: "Sketch 工具集",
+      url: "https://kitchen.alipay.com/",
+    },
+    {
+      icon: "https://gw.alipayobjects.com/zos/bmw-prod/d3e3eb39-1cd7-4aa5-827c-877deced6b7e/lalxt4g3_w256_h256.png",
+      title: "dumi",
+      desc: "为组件开发场景而生的文档工具",
+      url: "https://d.umijs.org/zh-CN",
+    },
+  ],
+};
+
+export default defaultProps;

+ 106 - 0
dashboard/src/pages/admin/index.tsx

@@ -0,0 +1,106 @@
+import {
+  GithubFilled,
+  InfoCircleFilled,
+  QuestionCircleFilled,
+} from "@ant-design/icons";
+import { ProConfigProvider, ProSettings } from "@ant-design/pro-components";
+import {
+  PageContainer,
+  ProLayout,
+  SettingDrawer,
+  ProCard,
+} from "@ant-design/pro-components";
+import { useState } from "react";
+import defaultProps from "./_defaultProps";
+
+const Widget = () => {
+  const [settings, setSetting] = useState<Partial<ProSettings> | undefined>({
+    layout: "side",
+  });
+
+  const [pathname, setPathname] = useState("/list/sub-page/sub-sub-page1");
+
+  return (
+    <div
+      id="test-pro-layout"
+      style={{
+        height: "100vh",
+      }}
+    >
+      <ProLayout
+        siderWidth={216}
+        bgLayoutImgList={[
+          {
+            src: "https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png",
+            left: 85,
+            bottom: 100,
+            height: "303px",
+          },
+          {
+            src: "https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png",
+            bottom: -68,
+            right: -45,
+            height: "303px",
+          },
+          {
+            src: "https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png",
+            bottom: 0,
+            left: 0,
+            width: "331px",
+          },
+        ]}
+        {...defaultProps}
+        location={{
+          pathname,
+        }}
+        avatarProps={{
+          src: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
+          title: "七妮妮",
+          size: "small",
+        }}
+        actionsRender={(props) => {
+          if (props.isMobile) return [];
+          return [
+            <InfoCircleFilled key="InfoCircleFilled" />,
+            <QuestionCircleFilled key="QuestionCircleFilled" />,
+            <GithubFilled key="GithubFilled" />,
+          ];
+        }}
+        menuItemRender={(item, dom) => (
+          <div
+            onClick={() => {
+              setPathname(item.path || "/welcome");
+            }}
+          >
+            {dom}
+          </div>
+        )}
+        {...settings}
+      >
+        <PageContainer>
+          <ProCard
+            style={{
+              height: "100vh",
+              minHeight: 800,
+            }}
+          >
+            <div />
+          </ProCard>
+        </PageContainer>
+      </ProLayout>
+
+      <SettingDrawer
+        pathname={pathname}
+        enableDarkTheme
+        getContainer={() => document.getElementById("test-pro-layout")}
+        settings={settings}
+        onSettingChange={(changeSetting) => {
+          setSetting(changeSetting);
+        }}
+        disableUrlParams={false}
+      />
+    </div>
+  );
+};
+
+export default Widget;