From 9065e4f2129be4b7790952aa42667c6cf789de44 Mon Sep 17 00:00:00 2001 From: Clifton Hills Date: Wed, 11 Sep 2024 15:37:39 +0800 Subject: [PATCH] feat: add ai generate content & footer link (#35) ![image](https://github.com/user-attachments/assets/0c6d739b-a77c-4f87-a734-c0d6e4e8784a) --------- Co-authored-by: linxin --- scripts/wiki/template.mdx | 7 +- src/api.ts | 23 ++- src/components/ai-content/index.tsx | 64 +++++++ src/components/article-meta/index.tsx | 90 ++++++++-- src/components/footer/index.tsx | 229 ++++++++++++++++++++++++ src/css/custom.scss | 5 +- src/theme/DocRoot/Layout/Main/index.tsx | 4 +- 7 files changed, 396 insertions(+), 26 deletions(-) create mode 100644 src/components/ai-content/index.tsx create mode 100644 src/components/footer/index.tsx diff --git a/scripts/wiki/template.mdx b/scripts/wiki/template.mdx index 544c1c39f..62afd15a2 100644 --- a/scripts/wiki/template.mdx +++ b/scripts/wiki/template.mdx @@ -5,11 +5,14 @@ id: "<%= id %>" hide_table_of_contents: true --- -import {ArticleMeta} from '@site/src/components/article-meta' +import { ArticleMeta } from "@site/src/components/article-meta"; +import { AIContent } from "@site/src/components/ai-content"; # <%= title %> } updatedAt={'<%=contentUpdatedAt%>'} />
-<%= jsxBody || jsxDesc %> +<%= jsxDesc %> + +`} id={<%= id %>} /> diff --git a/src/api.ts b/src/api.ts index 1688b73be..05af1b2a0 100644 --- a/src/api.ts +++ b/src/api.ts @@ -3,13 +3,12 @@ import axios, { AxiosInstance, ResponseType } from "axios"; const defaultOptions = { timeout: 60 * 1000, responseType: "json" as ResponseType, - withCredentials: true + withCredentials: true, }; - const axiosInstance: AxiosInstance = axios.create(defaultOptions); axiosInstance.interceptors.response.use( - response => { + (response) => { const res = response.data; // code === 0 为正常返回 @@ -24,15 +23,21 @@ axiosInstance.interceptors.response.use( // 其它未知错误,原样返回 return Promise.reject(res); }, - error => { + (error) => { return Promise.reject(error); } ); export function post(url, params = {}) { return axiosInstance.request({ - url, - method: "post", - params - } - ); + url, + method: "post", + params, + }); +} +export function get(url, params = {}) { + return axiosInstance.request({ + url, + method: "GET", + params, + }); } diff --git a/src/components/ai-content/index.tsx b/src/components/ai-content/index.tsx new file mode 100644 index 000000000..f9c67fdd6 --- /dev/null +++ b/src/components/ai-content/index.tsx @@ -0,0 +1,64 @@ +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import { get } from "@site/src/api"; +import React, { useEffect, useState } from "react"; +import { useDefaultLocale } from "@site/src/utils"; + +interface AIContentProps { + content?: string; + id: number; +} +const LocalesMap = { + aiGen: { + "zh-CN": "以下内容是 AI 的进一步解读", + "zh-HK": "以下內容是 AI 的進一步解讀", + en: "The following content is a further interpretation by AI.", + }, + disclaimer: { + "zh-CN": "免责声明", + "zh-HK": "免責聲明", + en: "Disclaimer", + }, +}; +export const AIContent: React.FC = ({ content, id }) => { + const [AIContent, setAIContent] = useState(content); + const locale = useDefaultLocale() || "en"; + + const { + siteConfig: { + customFields: { apiProxyUrl }, + }, + } = useDocusaurusContext(); + + useEffect(() => { + if (!apiProxyUrl || !id || AIContent) return; + + get(`${apiProxyUrl}/v1/social/wiki/content`, { id }).then((resp) => { + const { data } = resp; + if (data?.content) { + setAIContent(data.content); + } + }); + }, [apiProxyUrl, id]); + + if (!AIContent) return <>; + return ( +
+
+ port-ai + {LocalesMap.aiGen[locale]} +
+
+ + {LocalesMap.disclaimer[locale]} + +
+ ); +}; diff --git a/src/components/article-meta/index.tsx b/src/components/article-meta/index.tsx index 7dfd0f1d9..725487ffd 100644 --- a/src/components/article-meta/index.tsx +++ b/src/components/article-meta/index.tsx @@ -1,23 +1,48 @@ import React, { FC, useCallback, useEffect, useState } from "react"; -import { post } from "@site/src/api"; +import { post, get } from "@site/src/api"; import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; import { translate } from "@docusaurus/Translate"; +import { useDefaultLocale } from "@site/src/utils"; interface IArticleMetaProps { updatedAt: string | null; id: number; } +const LocalesMap = { + peopleLearned: { + "zh-CN": "人学过", + "zh-HK": "人學過", + en: "People learned", + }, + aiGen: { + "zh-CN": "AI 解读", + "zh-HK": "AI 解读", + en: "AI analysis", + }, + aiGenUnderReview: { + "zh-CN": "审核中,通过后可查看新内容", + "zh-HK": "審核中,通過後可查看新內容", + en: "Under review, content will be available after approval.", + }, +}; + export const ArticleMeta: FC = (props) => { const { updatedAt, id } = props; const [viewCount, setViewCount] = useState(0); + const [AIContent, setAIContent] = useState(""); + const [aiGen, setAIGen] = useState(false); + const [aiGenLoading, setAIGenLoading] = useState(false); + const locale = useDefaultLocale() || "en"; // translate({ // id: "theme.docs.paginator.navAriaLabel", // message: "Docs pages", // description: "The ARIA label for the docs pagination" // }); const { - siteConfig: { customFields: { apiProxyUrl } } + siteConfig: { + customFields: { apiProxyUrl }, + }, } = useDocusaurusContext(); useEffect(() => { @@ -28,21 +53,34 @@ export const ArticleMeta: FC = (props) => { if (data?.pv) { setViewCount(data.pv); } + if (data?.body) { + setAIContent(data.body); + } }); }, [apiProxyUrl, id]); - const onClickCopySection = useCallback(() => { + const postAI = () => { + setAIGenLoading(true); + setAIGen(true); + get(`${apiProxyUrl}/v1/social/wiki/content`, { id }) + .then((resp) => { + const { data } = resp; + }) + .finally(() => { + setAIGenLoading(false); + }); + }; - }, []); + const onClickCopySection = useCallback(() => {}, []); useEffect(() => { // @ts-ignore const clipboard = new ClipboardJS("#copy_trigger"); - clipboard.on("success", function(e) { + clipboard.on("success", function (e) { e.clearSelection(); }); - clipboard.on("error", function(e) { + clipboard.on("error", function (e) { console.error("Action:", e.action); console.error("Trigger:", e.trigger); }); @@ -51,17 +89,45 @@ export const ArticleMeta: FC = (props) => { return (
- {!!viewCount && - {viewCount} 人学过 . - } - {translate({ - id: "article.meta.updated_at" - }, { datetime: updatedAt })} + {!!viewCount && ( + + {viewCount} {LocalesMap.peopleLearned[locale]} . + + )} + + {" "} + {translate( + { + id: "article.meta.updated_at", + }, + { datetime: updatedAt } + )} +
{/*
*/} {/*
*/} {/*
*/} + + {/* 没有 AI 内容并且没点击过生成 */} + {/* {!AIContent && aiGen && ( +
{LocalesMap.aiGenUnderReview[locale]}
+ )} + {!AIContent && !aiGen && ( +
+ + ··· + +
+ +
+
+ )} */}
); diff --git a/src/components/footer/index.tsx b/src/components/footer/index.tsx new file mode 100644 index 000000000..6a7119ac0 --- /dev/null +++ b/src/components/footer/index.tsx @@ -0,0 +1,229 @@ +import React from "react"; +import { useDefaultLocale } from "@site/src/utils"; + +const LinksMap = [ + { + kindName: { + "zh-CN": "社区资讯", + "zh-HK": "社區資訊", + en: "Community", + }, + links: [ + { + text: { + "zh-CN": "社群", + "zh-HK": "社群", + en: "Community", + }, + href: "https://longportapp.com", + }, + { + text: { + "zh-CN": "资讯", + "zh-HK": "資訊", + en: "News", + }, + href: "https://longportapp.com/news", + }, + { + text: { + "zh-CN": "海豚投研", + "zh-HK": "海豚投研", + en: "Dolphin", + }, + href: "https://longportapp.com/news/dolphin", + }, + { + text: { + "zh-CN": "每日必读", + "zh-HK": "每日必讀", + en: "Daily Report", + }, + href: "https://longportapp.com/news/node/daily", + }, + ], + }, + { + kindName: { + "zh-CN": "市场", + "zh-HK": "市场", + en: "Market", + }, + links: [ + { + text: { + "zh-CN": "市场", + "zh-HK": "市场", + en: "Market", + }, + href: "https://longportapp.com/markets", + }, + { + text: { + "zh-CN": "选股器", + "zh-HK": "选股器", + en: "Screener", + }, + href: "https://longportapp.com/screener", + }, + ], + }, + { + kindName: { + "zh-CN": "下载", + "zh-HK": "下载", + en: "Download", + }, + links: [ + { + text: { + "zh-CN": "下载", + "zh-HK": "下载", + en: "Download", + }, + href: "https://longportapp.com/download", + }, + { + text: { + "zh-CN": "HK 下载", + "zh-HK": "HK 下载", + en: "HK Download", + }, + href: "https://longbridge.com/hk/download", + }, + { + text: { + "zh-CN": "SG 下载", + "zh-HK": "SG 下载", + en: "SG Download", + }, + href: "https://longbridge.com/sg/download", + }, + ], + }, + { + kindName: { + "zh-CN": "PortAI", + "zh-HK": "PortAI", + en: "PortAI", + }, + links: [ + { + text: { + "zh-CN": "PortAI", + "zh-HK": "PortAI", + en: "PortAI", + }, + href: "https://longportapp.com/portai", + }, + { + text: { + "zh-CN": "LongPort AI", + "zh-HK": "LongPort AI", + en: "LongPort AI", + }, + href: "https://longport.ai", + }, + ], + }, + { + kindName: { + "zh-CN": "开发", + "zh-HK": "开发", + en: "Developer", + }, + links: [ + { + text: { + "zh-CN": "开发者平台", + "zh-HK": "开发者平台", + en: "Developer", + }, + href: "https://open.longportapp.com", + }, + ], + }, +]; + +const getBasePath = () => { + if (typeof window === "undefined") { + return ""; + } + const path = window.location.pathname.split("/learn/"); + return `/learn/${path[1]}`; +}; +const Footer: React.FC = () => { + const locale = useDefaultLocale() || "en"; + let basePath = getBasePath(); + + const renderLinksMap = [...LinksMap]; + if (basePath) { + renderLinksMap.push({ + kindName: { + "zh-CN": "Wiki", + "zh-HK": "Wiki", + en: "Wiki", + }, + links: [ + { + text: { + "zh-CN": "简体中文", + "zh-HK": "简体中文", + en: "简体中文", + }, + href: `/zh-CN${basePath}`, + }, + { + text: { + "zh-CN": "繁體中文", + "zh-HK": "繁體中文", + en: "繁體中文", + }, + href: `/zh-HK${basePath}`, + }, + { + text: { + "zh-CN": "English", + "zh-HK": "English", + en: "English", + }, + href: `/en${basePath}`, + }, + ], + }); + } + + return ( +
+
+
+ {renderLinksMap.map((kind, i) => { + return ( +
+

+ {kind.kindName[locale]} +

+ +
+ ); + })} +
+
+ ); +}; + +export default Footer; diff --git a/src/css/custom.scss b/src/css/custom.scss index b65a5ee47..c655eff86 100644 --- a/src/css/custom.scss +++ b/src/css/custom.scss @@ -20,7 +20,7 @@ @tailwind utilities; body { color: #222; - @apply bg-[#f8f9fa]; + @apply bg-[#fff]; h1 { text-transform: capitalize; } @@ -134,6 +134,7 @@ body { } .main-wrapper { + overflow-x: hidden; .container { max-width: 780px; } @@ -144,5 +145,5 @@ body { } .theme-doc-markdown.markdown { - @apply bg-[#f8f9fa]; + @apply bg-[#fff]; } diff --git a/src/theme/DocRoot/Layout/Main/index.tsx b/src/theme/DocRoot/Layout/Main/index.tsx index ec7db554e..b1e55bc63 100644 --- a/src/theme/DocRoot/Layout/Main/index.tsx +++ b/src/theme/DocRoot/Layout/Main/index.tsx @@ -9,6 +9,7 @@ import React from "react"; import clsx from "clsx"; import { useDocsSidebar } from "@docusaurus/theme-common/internal"; import type { Props } from "@theme/DocRoot/Layout/Main"; +import Footer from "@site/src/components/footer"; import styles from "./styles.module.css"; import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; @@ -33,13 +34,14 @@ export default function DocRootLayoutMain({ >
{children} +
);