Procházet zdrojové kódy

Merge pull request #1917 from visuddhinanda/agile

:sparkles: GrammarTermLookup
visuddhinanda před 2 roky
rodič
revize
bec1e6174e

+ 4 - 2
dashboard/src/components/article/ArticleEditTools.tsx

@@ -7,6 +7,7 @@ import { ArticleTplModal } from "../template/Builder/ArticleTpl";
 import ShareModal from "../../components/share/ShareModal";
 import { EResType } from "../../components/share/Share";
 import AddToAnthology from "../../components/article/AddToAnthology";
+import Builder from "../template/Builder/Builder";
 
 interface IWidget {
   studioName?: string;
@@ -21,6 +22,7 @@ const ArticleEditToolsWidget = ({
   const intl = useIntl();
   return (
     <Space>
+      <Builder trigger={<Button type="link">{"<t>"}</Button>} />
       {articleId ? (
         <AddToAnthology
           trigger={<Button type="link">加入文集</Button>}
@@ -31,7 +33,7 @@ const ArticleEditToolsWidget = ({
       {articleId ? (
         <ShareModal
           trigger={
-            <Button icon={<TeamOutlined />}>
+            <Button type="link" icon={<TeamOutlined />}>
               {intl.formatMessage({
                 id: "buttons.share",
               })}
@@ -48,7 +50,7 @@ const ArticleEditToolsWidget = ({
         title={title}
         type="article"
         id={articleId}
-        trigger={<Button>获取模版</Button>}
+        trigger={<Button type="link">获取模版</Button>}
       />
     </Space>
   );

+ 5 - 0
dashboard/src/components/article/RightPanel.tsx

@@ -22,6 +22,7 @@ export type TPanelName =
   | "channel"
   | "discussion"
   | "suggestion"
+  | "grammar"
   | "close"
   | "open";
 
@@ -89,6 +90,10 @@ const RightPanelWidget = ({
         setOpen(true);
         setActiveTab(curr);
         break;
+      case "grammar":
+        setOpen(true);
+        setActiveTab(curr);
+        break;
       case "close":
         setOpen(false);
         break;

+ 22 - 0
dashboard/src/components/dict/GrammarLookup.tsx

@@ -0,0 +1,22 @@
+import { grammar } from "../../reducers/command";
+import { openPanel } from "../../reducers/right-panel";
+import store from "../../store";
+
+interface IWidget {
+  word?: string;
+  children?: React.ReactNode;
+}
+const GrammarLookup = ({ word, children }: IWidget) => {
+  return (
+    <span
+      onClick={() => {
+        store.dispatch(grammar(word));
+        store.dispatch(openPanel("grammar"));
+      }}
+    >
+      {children}
+    </span>
+  );
+};
+
+export default GrammarLookup;

+ 12 - 8
dashboard/src/components/template/Builder/Builder.tsx

@@ -2,6 +2,7 @@ import { useState } from "react";
 import { Col, Modal, Row, Tree } from "antd";
 import { Key } from "antd/lib/table/interface";
 import ArticleTpl from "./ArticleTpl";
+import VideoTpl from "./VideoTpl";
 
 interface DataNode {
   title: React.ReactNode;
@@ -15,6 +16,13 @@ interface tplListNode {
   component?: React.ReactNode;
 }
 
+const tplList: tplListNode[] = [
+  { name: "article", component: <ArticleTpl /> },
+  { name: "video", component: <VideoTpl /> },
+  { name: "note" },
+  { name: "term" },
+];
+
 interface IWidget {
   trigger?: React.ReactNode;
 }
@@ -23,11 +31,6 @@ const TplBuilderWidget = ({ trigger }: IWidget) => {
   const [template, setTemplate] =
     useState<React.ReactNode>("在左侧列表选择一个模版");
 
-  const tplList: tplListNode[] = [
-    { name: "article", component: <ArticleTpl /> },
-    { name: "note" },
-    { name: "term" },
-  ];
   const treeData: DataNode[] = tplList.map((item) => {
     return { title: item.name, key: item.name };
   });
@@ -44,14 +47,15 @@ const TplBuilderWidget = ({ trigger }: IWidget) => {
     <>
       <span onClick={showModal}>{trigger}</span>
       <Modal
-        width={700}
+        style={{ top: 20 }}
+        width={900}
         footer={false}
         title="template builder"
         open={isModalOpen}
         onCancel={handleCancel}
       >
         <Row>
-          <Col span={8}>
+          <Col span={6}>
             <Tree
               treeData={treeData}
               onSelect={(selectedKeys: Key[]) => {
@@ -64,7 +68,7 @@ const TplBuilderWidget = ({ trigger }: IWidget) => {
               }}
             />
           </Col>
-          <Col span={16}>{template}</Col>
+          <Col span={18}>{template}</Col>
         </Row>
       </Modal>
     </>

+ 161 - 0
dashboard/src/components/template/Builder/VideoTpl.tsx

@@ -0,0 +1,161 @@
+import { useEffect, useState } from "react";
+import { useIntl } from "react-intl";
+import { Divider, Input, Modal, Select, Space, Tabs, Typography } from "antd";
+
+import { TDisplayStyle } from "../Article";
+import { VideoCtl } from "../Video";
+
+const { TextArea } = Input;
+const { Paragraph } = Typography;
+
+interface IWidget {
+  url?: string;
+  title?: string;
+  style?: TDisplayStyle;
+  onSelect?: Function;
+  onCancel?: Function;
+}
+const VideoTplWidget = ({ url, title, style = "modal" }: IWidget) => {
+  const intl = useIntl(); //i18n
+  const [currTitle, setCurrTitle] = useState(title);
+
+  const [styleText, setStyleText] = useState(style);
+
+  const [urlText, setUrlText] = useState(url);
+  const [tplText, setTplText] = useState("");
+
+  useEffect(() => {
+    setCurrTitle(title);
+  }, [title]);
+  useEffect(() => {
+    let tplText = `{{v|\n`;
+    tplText += `url=${urlText}|\n`;
+    tplText += `title=${currTitle}|\n`;
+    tplText += `style=${styleText}`;
+    tplText += "}}";
+
+    setTplText(tplText);
+  }, [currTitle, styleText, urlText]);
+
+  return (
+    <>
+      <Space direction="vertical" style={{ width: 500 }}>
+        <Space style={{ width: 500 }}>
+          {"标题:"}
+          <Input
+            width={400}
+            value={currTitle}
+            placeholder="Title"
+            onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
+              setCurrTitle(event.target.value);
+            }}
+          />
+        </Space>
+        <Space style={{ width: 500 }}>
+          {"Url"}
+          <Space>
+            <Input
+              defaultValue={url}
+              width={400}
+              value={urlText}
+              placeholder="Url"
+              onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
+                setUrlText(event.target.value);
+              }}
+            />
+          </Space>
+        </Space>
+
+        <Space>
+          {"显示为:"}
+          <Select
+            defaultValue={style}
+            style={{ width: 120 }}
+            onChange={(value: string) => {
+              console.log(`selected ${value}`);
+              setStyleText(value as TDisplayStyle);
+            }}
+            options={["window", "modal", "card", "toggle", "link"].map(
+              (item) => {
+                return { value: item, label: item };
+              }
+            )}
+          />
+        </Space>
+        <Tabs
+          size="small"
+          defaultActiveKey="preview"
+          items={[
+            {
+              label: intl.formatMessage({
+                id: "buttons.preview",
+              }),
+              key: "preview",
+              children: (
+                <VideoCtl url={urlText} title={currTitle} style={styleText} />
+              ),
+            },
+            {
+              label: `Code`,
+              key: "code",
+              children: (
+                <TextArea
+                  value={tplText}
+                  rows={4}
+                  placeholder="maxLength is 6"
+                  maxLength={6}
+                />
+              ),
+            },
+          ]}
+        />
+        <Divider></Divider>
+        <Paragraph copyable={{ text: tplText }}>复制到剪贴板</Paragraph>
+      </Space>
+    </>
+  );
+};
+
+interface IModalWidget {
+  url?: string;
+  title?: string;
+  style?: TDisplayStyle;
+  trigger?: JSX.Element;
+}
+export const VideoTplModal = ({
+  url,
+  title,
+  style = "modal",
+  trigger,
+}: IModalWidget) => {
+  const [isModalOpen, setIsModalOpen] = useState(false);
+
+  const showModal = () => {
+    setIsModalOpen(true);
+  };
+
+  const handleOk = () => {
+    setIsModalOpen(false);
+  };
+
+  const handleCancel = () => {
+    setIsModalOpen(false);
+  };
+
+  return (
+    <>
+      <span onClick={showModal}>{trigger}</span>
+      <Modal
+        width={"80%"}
+        title="生成模版"
+        open={isModalOpen}
+        onOk={handleOk}
+        onCancel={handleCancel}
+      >
+        <VideoTplWidget url={url} title={title} style={style} />
+      </Modal>
+    </>
+  );
+};
+
+export default VideoTplWidget;

+ 29 - 0
dashboard/src/components/template/GrammarTermLookup.tsx

@@ -0,0 +1,29 @@
+import { IWidgetTermCtl, TermCtl } from "./Term";
+import GrammarLookup from "../dict/GrammarLookup";
+
+interface IGrammarTermLookupCtl {
+  word?: string;
+  term?: IWidgetTermCtl;
+}
+const GrammarTermLookupCtl = ({ word, term }: IGrammarTermLookupCtl) => {
+  return (
+    <GrammarLookup word={word}>
+      <TermCtl {...term} compact={true} />
+    </GrammarLookup>
+  );
+};
+
+interface IWidget {
+  props: string;
+}
+const Widget = ({ props }: IWidget) => {
+  const prop = JSON.parse(atob(props)) as IGrammarTermLookupCtl;
+  console.debug("QuoteLink", prop);
+  return (
+    <>
+      <GrammarTermLookupCtl {...prop} />
+    </>
+  );
+};
+
+export default Widget;

+ 3 - 0
dashboard/src/components/template/MdTpl.tsx

@@ -1,6 +1,7 @@
 import { GrammarPopShell } from "../dict/GrammarPop";
 import Article from "./Article";
 import Exercise from "./Exercise";
+import GrammarTermLookup from "./GrammarTermLookup";
 import Mermaid from "./Mermaid";
 import Nissaya from "./Nissaya";
 import Note from "./Note";
@@ -60,6 +61,8 @@ const Widget = ({ tpl, props, children }: IWidgetMdTpl) => {
       return <Qa props={props ? props : ""} />;
     case "video":
       return <Video props={props ? props : ""} />;
+    case "grammar":
+      return <GrammarTermLookup props={props ? props : ""} />;
     default:
       return <>未定义模版({tpl})</>;
   }

+ 1 - 1
dashboard/src/reducers/accept-pr.ts

@@ -1,5 +1,5 @@
 /**
- * 查字典,添加术语命令
+ *
  */
 import { createSlice, PayloadAction } from "@reduxjs/toolkit";
 import { ISentence } from "../components/template/SentEdit";

+ 1 - 1
dashboard/src/reducers/article-mode.ts

@@ -1,5 +1,5 @@
 /**
- * 查字典,添加术语命令
+ *
  */
 import { createSlice, PayloadAction } from "@reduxjs/toolkit";
 import { ArticleMode } from "../components/article/Article";

+ 1 - 1
dashboard/src/reducers/cart-mode.ts

@@ -1,5 +1,5 @@
 /**
- * 查字典,添加术语命令
+ *
  */
 import { createSlice, PayloadAction } from "@reduxjs/toolkit";
 

+ 1 - 1
dashboard/src/reducers/discussion.ts

@@ -1,5 +1,5 @@
 /**
- * 查字典,添加术语命令
+ *
  */
 import { createSlice, PayloadAction } from "@reduxjs/toolkit";
 

+ 1 - 1
dashboard/src/reducers/net-status.ts

@@ -1,5 +1,5 @@
 /**
- * 查字典,添加术语命令
+ *
  */
 import { createSlice, PayloadAction } from "@reduxjs/toolkit";
 import { ENetStatus } from "../components/general/NetStatus";

+ 1 - 1
dashboard/src/reducers/relation-add.ts

@@ -1,5 +1,5 @@
 /**
- * 查字典,添加术语命令
+ *
  */
 import { createSlice, PayloadAction } from "@reduxjs/toolkit";
 import { IRelation } from "../pages/admin/relation/list";

+ 1 - 1
dashboard/src/reducers/right-panel.ts

@@ -1,5 +1,5 @@
 /**
- * 查字典,添加术语命令
+ *
  */
 import { createSlice, PayloadAction } from "@reduxjs/toolkit";
 import { TPanelName } from "../components/article/RightPanel";

+ 1 - 1
dashboard/src/reducers/term-order.ts

@@ -1,5 +1,5 @@
 /**
- * 查字典,添加术语命令
+ * 
  */
 import { createSlice, PayloadAction } from "@reduxjs/toolkit";
 

+ 1 - 1
dashboard/src/reducers/wbw.ts

@@ -1,5 +1,5 @@
 /**
- * 查字典,添加术语命令
+ *
  */
 import { createSlice, PayloadAction } from "@reduxjs/toolkit";